#define _CRT_SECURE_NO_WARNINGS
#include <cmath>
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <list>
#include <string>
#include "em_inflate.h"
#include "ymfm_misc.h"
#include "ymfm_opl.h"
#include "ymfm_opm.h"
#include "ymfm_opn.h"
#define LOG_WRITES (0)
#define EXTRA_CLOCKS (0)
#define RUN_NUKED_OPN2 (0)
#if (RUN_NUKED_OPN2)
namespace nuked {
bool s_log_envelopes = false;
const int s_log_envelopes_channel = 5;
#include "test/ym3438.h"
}
#endif
#define CAPTURE_NATIVE (0 || RUN_NUKED_OPN2)
using emulated_time = int64_t;
enum chip_type
{
CHIP_YM2149,
CHIP_YM2151,
CHIP_YM2203,
CHIP_YM2413,
CHIP_YM2608,
CHIP_YM2610,
CHIP_YM2612,
CHIP_YM3526,
CHIP_Y8950,
CHIP_YM3812,
CHIP_YMF262,
CHIP_YMF278B,
CHIP_TYPES
};
class vgm_chip_base
{
public:
vgm_chip_base(uint32_t clock, chip_type type, char const *name) :
m_type(type),
m_name(name)
{
}
virtual ~vgm_chip_base()
{
}
chip_type type() const { return m_type; }
virtual uint32_t sample_rate() const = 0;
virtual void write(uint32_t reg, uint8_t data) = 0;
virtual void generate(emulated_time output_start, emulated_time output_step, int32_t *buffer) = 0;
void write_data(ymfm::access_class type, uint32_t base, uint32_t length, uint8_t const *src)
{
uint32_t end = base + length;
if (end > m_data[type].size())
m_data[type].resize(end);
memcpy(&m_data[type][base], src, length);
}
void seek_pcm(uint32_t pos) { m_pcm_offset = pos; }
uint8_t read_pcm() { auto &pcm = m_data[ymfm::ACCESS_PCM]; return (m_pcm_offset < pcm.size()) ? pcm[m_pcm_offset++] : 0; }
protected:
chip_type m_type;
std::string m_name;
std::vector<uint8_t> m_data[ymfm::ACCESS_CLASSES];
uint32_t m_pcm_offset;
#if (CAPTURE_NATIVE)
public:
std::vector<int32_t> m_native_data;
#endif
#if (RUN_NUKED_OPN2)
public:
nuked::ym3438_t *m_external = nullptr;
std::vector<int32_t> m_nuked_data;
#endif
};
template<typename ChipType>
class vgm_chip : public vgm_chip_base, public ymfm::ymfm_interface
{
public:
vgm_chip(uint32_t clock, chip_type type, char const *name) :
vgm_chip_base(clock, type, name),
m_chip(*this),
m_clock(clock),
m_clocks(0),
m_step(0x100000000ull / m_chip.sample_rate(clock)),
m_pos(0)
{
m_chip.reset();
for (int clock = 0; clock < EXTRA_CLOCKS; clock++)
m_chip.generate(&m_output);
#if (RUN_NUKED_OPN2)
if (type == CHIP_YM2612)
{
m_external = new nuked::ym3438_t;
nuked::OPN2_SetChipType(nuked::ym3438_mode_ym2612);
nuked::OPN2_Reset(m_external);
nuked::Bit16s buffer[2];
for (int clocks = 0; clocks < 24 * EXTRA_CLOCKS; clocks++)
nuked::OPN2_Clock(m_external, buffer);
}
#endif
}
virtual uint32_t sample_rate() const override
{
return m_chip.sample_rate(m_clock);
}
virtual void write(uint32_t reg, uint8_t data) override
{
m_queue.push_back(std::make_pair(reg, data));
}
virtual void generate(emulated_time output_start, emulated_time output_step, int32_t *buffer) override
{
uint32_t addr1 = 0xffff, addr2 = 0xffff;
uint8_t data1 = 0, data2 = 0;
if (!m_queue.empty())
{
auto front = m_queue.front();
addr1 = 0 + 2 * ((front.first >> 8) & 3);
data1 = front.first & 0xff;
addr2 = addr1 + ((m_type == CHIP_YM2149) ? 2 : 1);
data2 = front.second;
m_queue.erase(m_queue.begin());
}
if (addr1 != 0xffff)
{
if (LOG_WRITES)
printf("%10.5f: %s %03X=%02X\n", double(output_start) / double(1LL << 32), m_name.c_str(), data1 + 0x100 * (addr1/2), data2);
m_chip.write(addr1, data1);
m_chip.write(addr2, data2);
}
for ( ; m_pos <= output_start; m_pos += m_step)
{
m_chip.generate(&m_output);
#if (CAPTURE_NATIVE)
m_native_data.push_back(m_output.data[0]);
m_native_data.push_back(m_output.data[ChipType::OUTPUTS > 1 ? 1 : 0]);
#endif
#if (RUN_NUKED_OPN2)
if (m_external != nullptr)
{
int32_t sum[2] = { 0 };
if (addr1 != 0xffff)
nuked::OPN2_Write(m_external, addr1, data1);
nuked::Bit16s buffer[2];
for (int clocks = 0; clocks < 12; clocks++)
{
nuked::OPN2_Clock(m_external, buffer);
sum[0] += buffer[0];
sum[1] += buffer[1];
}
if (addr2 != 0xffff)
nuked::OPN2_Write(m_external, addr2, data2);
for (int clocks = 0; clocks < 12; clocks++)
{
nuked::OPN2_Clock(m_external, buffer);
sum[0] += buffer[0];
sum[1] += buffer[1];
}
addr1 = addr2 = 0xffff;
m_nuked_data.push_back(sum[0] / 24);
m_nuked_data.push_back(sum[1] / 24);
}
#endif
}
if (m_type == CHIP_YM2203)
{
int32_t out0 = m_output.data[0];
int32_t out1 = m_output.data[1 % ChipType::OUTPUTS];
int32_t out2 = m_output.data[2 % ChipType::OUTPUTS];
int32_t out3 = m_output.data[3 % ChipType::OUTPUTS];
*buffer++ += out0 + out1 + out2 + out3;
*buffer++ += out0 + out1 + out2 + out3;
}
else if (m_type == CHIP_YM2608 || m_type == CHIP_YM2610)
{
int32_t out0 = m_output.data[0];
int32_t out1 = m_output.data[1 % ChipType::OUTPUTS];
int32_t out2 = m_output.data[2 % ChipType::OUTPUTS];
*buffer++ += out0 + out2;
*buffer++ += out1 + out2;
}
else if (m_type == CHIP_YMF278B)
{
*buffer++ += m_output.data[4 % ChipType::OUTPUTS];
*buffer++ += m_output.data[5 % ChipType::OUTPUTS];
}
else if (ChipType::OUTPUTS == 1)
{
*buffer++ += m_output.data[0];
*buffer++ += m_output.data[0];
}
else
{
*buffer++ += m_output.data[0];
*buffer++ += m_output.data[1 % ChipType::OUTPUTS];
}
m_clocks++;
}
protected:
virtual uint8_t ymfm_external_read(ymfm::access_class type, uint32_t offset) override
{
auto &data = m_data[type];
return (offset < data.size()) ? data[offset] : 0;
}
ChipType m_chip;
uint32_t m_clock;
uint64_t m_clocks;
typename ChipType::output_data m_output;
emulated_time m_step;
emulated_time m_pos;
std::vector<std::pair<uint32_t, uint8_t>> m_queue;
};
std::vector<std::unique_ptr<vgm_chip_base>> active_chips;
uint32_t parse_uint32(std::vector<uint8_t> &buffer, uint32_t &offset)
{
uint32_t result = buffer[offset++];
result |= buffer[offset++] << 8;
result |= buffer[offset++] << 16;
result |= buffer[offset++] << 24;
return result;
}
template<typename ChipType>
void add_chips(uint32_t clock, chip_type type, char const *chipname)
{
uint32_t clockval = clock & 0x3fffffff;
int numchips = (clock & 0x40000000) ? 2 : 1;
printf("Adding %s%s @ %dHz\n", (numchips == 2) ? "2 x " : "", chipname, clockval);
for (int index = 0; index < numchips; index++)
{
char name[100];
sprintf(name, "%s #%d", chipname, index);
active_chips.push_back(std::make_unique<vgm_chip<ChipType>>(clockval, type, (numchips == 2) ? name : chipname));
}
if (type == CHIP_YM2608)
{
FILE *rom = fopen("ym2608_adpcm_rom.bin", "rb");
if (rom == nullptr)
fprintf(stderr, "Warning: YM2608 enabled but ym2608_adpcm_rom.bin not found\n");
else
{
fseek(rom, 0, SEEK_END);
uint32_t size = ftell(rom);
fseek(rom, 0, SEEK_SET);
std::vector<uint8_t> temp(size);
fread(&temp[0], 1, size, rom);
fclose(rom);
for (auto &chip : active_chips)
if (chip->type() == type)
chip->write_data(ymfm::ACCESS_ADPCM_A, 0, size, &temp[0]);
}
}
}
uint32_t parse_header(std::vector<uint8_t> &buffer)
{
uint32_t offset = 4;
uint32_t size = parse_uint32(buffer, offset);
if (offset - 4 + size > buffer.size())
{
fprintf(stderr, "Total size for file is too small; file may be truncated\n");
size = buffer.size() - 4;
}
buffer.resize(size + 4);
uint32_t version = parse_uint32(buffer, offset);
if (version > 0x171)
fprintf(stderr, "Warning: version > 1.71 detected, some things may not work\n");
uint32_t clock = parse_uint32(buffer, offset);
if (clock != 0)
fprintf(stderr, "Warning: clock for SN76489 specified (%d), but not supported\n", clock);
clock = parse_uint32(buffer, offset);
if (clock != 0)
add_chips<ymfm::ym2413>(clock, CHIP_YM2413, "YM2413");
uint32_t dummy = parse_uint32(buffer, offset);
dummy = parse_uint32(buffer, offset);
dummy = parse_uint32(buffer, offset);
dummy = parse_uint32(buffer, offset);
dummy = parse_uint32(buffer, offset);
dummy = parse_uint32(buffer, offset);
clock = parse_uint32(buffer, offset);
if (version >= 0x110 && clock != 0)
add_chips<ymfm::ym2612>(clock, CHIP_YM2612, "YM2612");
clock = parse_uint32(buffer, offset);
if (version >= 0x110 && clock != 0)
add_chips<ymfm::ym2151>(clock, CHIP_YM2151, "YM2151");
uint32_t data_start = parse_uint32(buffer, offset);
data_start += offset - 4;
if (version < 0x150)
data_start = 0x40;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
fprintf(stderr, "Warning: clock for Sega PCM specified, but not supported\n");
dummy = parse_uint32(buffer, offset);
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
fprintf(stderr, "Warning: clock for RF5C68 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
add_chips<ymfm::ym2203>(clock, CHIP_YM2203, "YM2203");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
add_chips<ymfm::ym2608>(clock, CHIP_YM2608, "YM2608");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
{
if (clock & 0x80000000)
add_chips<ymfm::ym2610b>(clock, CHIP_YM2610, "YM2610B");
else
add_chips<ymfm::ym2610>(clock, CHIP_YM2610, "YM2610");
}
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
add_chips<ymfm::ym3812>(clock, CHIP_YM3812, "YM3812");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
add_chips<ymfm::ym3526>(clock, CHIP_YM3526, "YM3526");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
add_chips<ymfm::y8950>(clock, CHIP_Y8950, "Y8950");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
add_chips<ymfm::ymf262>(clock, CHIP_YMF262, "YMF262");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
add_chips<ymfm::ymf278b>(clock, CHIP_YMF278B, "YMF278B");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
fprintf(stderr, "Warning: clock for YMF271 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
fprintf(stderr, "Warning: clock for YMF280B specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
fprintf(stderr, "Warning: clock for RF5C164 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
fprintf(stderr, "Warning: clock for PWM specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x151 && clock != 0)
{
fprintf(stderr, "Warning: clock for AY8910 specified, substituting YM2149\n");
add_chips<ymfm::ym2149>(clock, CHIP_YM2149, "YM2149");
}
if (offset + 4 > data_start)
return data_start;
dummy = parse_uint32(buffer, offset);
if (offset + 4 > data_start)
return data_start;
dummy = parse_uint32(buffer, offset);
if ((dummy & 0xff) != 0)
printf("Volume modifier: %02X (=%d)\n", dummy & 0xff, int(pow(2, double(dummy & 0xff) / 0x20)));
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for GameBoy DMG specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for NES APU specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for MultiPCM specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for uPD7759 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for OKIM6258 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
dummy = parse_uint32(buffer, offset);
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for OKIM6295 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for K051649 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for K054539 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for HuC6280 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for C140 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for K053260 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for Pokey specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x161 && clock != 0)
fprintf(stderr, "Warning: clock for QSound specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x171 && clock != 0)
fprintf(stderr, "Warning: clock for SCSP specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
uint32_t extra_header = parse_uint32(buffer, offset);
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x171 && clock != 0)
fprintf(stderr, "Warning: clock for WonderSwan specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x171 && clock != 0)
fprintf(stderr, "Warning: clock for VSU specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x171 && clock != 0)
fprintf(stderr, "Warning: clock for SAA1099 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x171 && clock != 0)
fprintf(stderr, "Warning: clock for ES5503 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x171 && clock != 0)
fprintf(stderr, "Warning: clock for ES5505/ES5506 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
dummy = parse_uint32(buffer, offset);
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x171 && clock != 0)
fprintf(stderr, "Warning: clock for X1-010 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x171 && clock != 0)
fprintf(stderr, "Warning: clock for C352 specified, but not supported\n");
if (offset + 4 > data_start)
return data_start;
clock = parse_uint32(buffer, offset);
if (version >= 0x171 && clock != 0)
fprintf(stderr, "Warning: clock for GA20 specified, but not supported\n");
return data_start;
}
vgm_chip_base *find_chip(chip_type type, uint8_t index)
{
for (auto &chip : active_chips)
if (chip->type() == type && index-- == 0)
return chip.get();
return nullptr;
}
void write_chip(chip_type type, uint8_t index, uint32_t reg, uint8_t data)
{
vgm_chip_base *chip = find_chip(type, index);
if (chip != nullptr)
chip->write(reg, data);
}
void add_rom_data(chip_type type, ymfm::access_class access, std::vector<uint8_t> &buffer, uint32_t &localoffset, uint32_t size)
{
uint32_t length = parse_uint32(buffer, localoffset);
uint32_t start = parse_uint32(buffer, localoffset);
for (int index = 0; index < 2; index++)
{
vgm_chip_base *chip = find_chip(type, index);
if (chip != nullptr)
chip->write_data(access, start, size, &buffer[localoffset]);
}
}
void generate_all(std::vector<uint8_t> &buffer, uint32_t data_start, uint32_t output_rate, std::vector<int32_t> &wav_buffer)
{
uint32_t offset = data_start;
bool done = false;
emulated_time output_step = 0x100000000ull / output_rate;
emulated_time output_pos = 0;
while (!done && offset < buffer.size())
{
int delay = 0;
uint8_t cmd = buffer[offset++];
switch (cmd)
{
case 0x51:
case 0xa1:
write_chip(CHIP_YM2413, cmd >> 7, buffer[offset], buffer[offset + 1]);
offset += 2;
break;
case 0x52:
case 0xa2:
write_chip(CHIP_YM2612, cmd >> 7, buffer[offset], buffer[offset + 1]);
offset += 2;
break;
case 0x53:
case 0xa3:
write_chip(CHIP_YM2612, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]);
offset += 2;
break;
case 0x54:
case 0xa4:
write_chip(CHIP_YM2151, cmd >> 7, buffer[offset], buffer[offset + 1]);
offset += 2;
break;
case 0x55:
case 0xa5:
write_chip(CHIP_YM2203, cmd >> 7, buffer[offset], buffer[offset + 1]);
offset += 2;
break;
case 0x56:
case 0xa6:
write_chip(CHIP_YM2608, cmd >> 7, buffer[offset], buffer[offset + 1]);
offset += 2;
break;
case 0x57:
case 0xa7:
write_chip(CHIP_YM2608, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]);
offset += 2;
break;
case 0x58:
case 0xa8:
write_chip(CHIP_YM2610, cmd >> 7, buffer[offset], buffer[offset + 1]);
offset += 2;
break;
case 0x59:
case 0xa9:
write_chip(CHIP_YM2610, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]);
offset += 2;
break;
case 0x5a:
case 0xaa:
write_chip(CHIP_YM3812, cmd >> 7, buffer[offset], buffer[offset + 1]);
offset += 2;
break;
case 0x5b:
case 0xab:
write_chip(CHIP_YM3526, cmd >> 7, buffer[offset], buffer[offset + 1]);
offset += 2;
break;
case 0x5c:
case 0xac:
write_chip(CHIP_Y8950, cmd >> 7, buffer[offset], buffer[offset + 1]);
offset += 2;
break;
case 0x5e:
case 0xae:
write_chip(CHIP_YMF262, cmd >> 7, buffer[offset], buffer[offset + 1]);
offset += 2;
break;
case 0x5f:
case 0xaf:
write_chip(CHIP_YMF262, cmd >> 7, buffer[offset] | 0x100, buffer[offset + 1]);
offset += 2;
break;
case 0x61:
delay = buffer[offset] | (buffer[offset + 1] << 8);
offset += 2;
break;
case 0x62:
delay = 735;
break;
case 0x63:
delay = 882;
break;
case 0x66:
done = true;
break;
case 0x67:
{
uint8_t dummy = buffer[offset++];
if (dummy != 0x66)
break;
uint8_t type = buffer[offset++];
uint32_t size = parse_uint32(buffer, offset);
uint32_t localoffset = offset;
switch (type)
{
case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: break;
case 0x00: {
vgm_chip_base *chip = find_chip(CHIP_YM2612, 0);
if (chip != nullptr)
chip->write_data(ymfm::ACCESS_PCM, 0, size - 8, &buffer[localoffset]);
break;
}
case 0x82: add_rom_data(CHIP_YM2610, ymfm::ACCESS_ADPCM_A, buffer, localoffset, size - 8);
break;
case 0x81: add_rom_data(CHIP_YM2608, ymfm::ACCESS_ADPCM_B, buffer, localoffset, size - 8);
break;
case 0x83: add_rom_data(CHIP_YM2610, ymfm::ACCESS_ADPCM_B, buffer, localoffset, size - 8);
break;
case 0x84: case 0x87: add_rom_data(CHIP_YMF278B, ymfm::ACCESS_PCM, buffer, localoffset, size - 8);
break;
case 0x88: add_rom_data(CHIP_Y8950, ymfm::ACCESS_ADPCM_B, buffer, localoffset, size - 8);
break;
case 0x80: case 0x85: case 0x86: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F: case 0x90: case 0x91: case 0x92: case 0x93: break;
case 0xC0: case 0xC1: case 0xC2: case 0xE0: case 0xE1: break;
default:
if (type >= 0x40 && type < 0x7f)
printf("Compressed data block not supported\n");
else
printf("Unknown data block type 0x%02X\n", type);
break;
}
offset += size;
break;
}
case 0x68:
printf("68: PCM RAM write\n");
break;
case 0xa0:
write_chip(CHIP_YM2149, buffer[offset] >> 7, buffer[offset] & 0x7f, buffer[offset + 1]);
offset += 2;
break;
case 0xd0:
write_chip(CHIP_YMF278B, buffer[offset] >> 7, ((buffer[offset] & 0x7f) << 8) | buffer[offset + 1], buffer[offset + 2]);
offset += 3;
break;
case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
delay = (cmd & 15) + 1;
break;
case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
{
vgm_chip_base *chip = find_chip(CHIP_YM2612, 0);
if (chip != nullptr)
chip->write(0x2a, chip->read_pcm());
delay = cmd & 15;
break;
}
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
case 0x4f: case 0x50: offset++;
break;
case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e:
case 0x5d: case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7: case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf: offset += 2;
break;
case 0xc9: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf:
case 0xd7: case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf:
case 0xc0: case 0xc1: case 0xc2: case 0xc3: case 0xc4: case 0xc5: case 0xc6: case 0xc7: case 0xc8: case 0xd1: case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: offset += 3;
break;
case 0xe0: {
vgm_chip_base *chip = find_chip(CHIP_YM2612, 0);
uint32_t pos = parse_uint32(buffer, offset);
if (chip != nullptr)
chip->seek_pcm(pos);
offset += 4;
break;
}
case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7:
case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
offset += 4;
break;
}
while (delay-- != 0)
{
bool more_remaining = false;
int32_t outputs[2] = { 0 };
for (auto &chip : active_chips)
chip->generate(output_pos, output_step, outputs);
output_pos += output_step;
wav_buffer.push_back(outputs[0]);
wav_buffer.push_back(outputs[1]);
}
}
}
int write_wav(char const *filename, uint32_t output_rate, std::vector<int32_t> &wav_buffer_src)
{
int32_t max_scale = 0;
for (size_t index = 0; index < wav_buffer_src.size(); index++)
{
int32_t absval = std::abs(wav_buffer_src[index]);
max_scale = std::max(max_scale, absval);
}
if (max_scale == 0)
{
fprintf(stderr, "The WAV file data will only contain silence.\n");
max_scale = 1;
}
std::vector<int16_t> wav_buffer(wav_buffer_src.size());
for (size_t index = 0; index < wav_buffer_src.size(); index++)
wav_buffer[index] = wav_buffer_src[index] * 26000 / max_scale;
FILE *out = fopen(filename, "wb");
if (out == nullptr)
{
fprintf(stderr, "Error creating output file '%s'\n", filename);
return 6;
}
if (fwrite("RIFF", 1, 4, out) != 4)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
uint32_t total_size = 48 + wav_buffer.size() * 2 - 8;
uint8_t wavdata[4];
wavdata[0] = total_size >> 0;
wavdata[1] = total_size >> 8;
wavdata[2] = total_size >> 16;
wavdata[3] = total_size >> 24;
if (fwrite(wavdata, 1, 4, out) != 4)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
if (fwrite("WAVE", 1, 4, out) != 4)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
if (fwrite("fmt ", 1, 4, out) != 4)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
wavdata[0] = 16;
wavdata[1] = 0;
wavdata[2] = 0;
wavdata[3] = 0;
if (fwrite(wavdata, 1, 4, out) != 4)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
wavdata[0] = 1;
wavdata[1] = 0;
if (fwrite(wavdata, 1, 2, out) != 2)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
wavdata[0] = 2;
wavdata[1] = 0;
if (fwrite(wavdata, 1, 2, out) != 2)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
wavdata[0] = output_rate >> 0;
wavdata[1] = output_rate >> 8;
wavdata[2] = output_rate >> 16;
wavdata[3] = output_rate >> 24;
if (fwrite(wavdata, 1, 4, out) != 4)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
uint32_t bps = output_rate * 2 * 2;
wavdata[0] = bps >> 0;
wavdata[1] = bps >> 8;
wavdata[2] = bps >> 16;
wavdata[3] = bps >> 24;
if (fwrite(wavdata, 1, 4, out) != 4)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
wavdata[0] = 4;
wavdata[1] = 0;
if (fwrite(wavdata, 1, 2, out) != 2)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
wavdata[0] = 16;
wavdata[1] = 0;
if (fwrite(wavdata, 1, 2, out) != 2)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
if (fwrite("data", 1, 4, out) != 4)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
uint32_t datalen = wav_buffer.size() * 2;
wavdata[0] = datalen >> 0;
wavdata[1] = datalen >> 8;
wavdata[2] = datalen >> 16;
wavdata[3] = datalen >> 24;
if (fwrite(wavdata, 1, 4, out) != 4)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
if (fwrite(&wav_buffer[0], 1, datalen, out) != datalen)
{
fprintf(stderr, "Error writing to output file\n");
return 7;
}
fclose(out);
return 0;
}
int main(int argc, char *argv[])
{
char const *filename = nullptr;
char const *outfilename = nullptr;
int output_rate = 44100;
bool argerr = false;
for (int arg = 1; arg < argc; arg++)
{
char const *curarg = argv[arg];
if (*curarg == '-')
{
if (strcmp(curarg, "-o") == 0 || strcmp(curarg, "--output") == 0)
outfilename = argv[++arg];
else if (strcmp(curarg, "-r") == 0 || strcmp(curarg, "--samplerate") == 0)
output_rate = atoi(argv[++arg]);
else
{
fprintf(stderr, "Unknown argument: %s\n", curarg);
argerr = true;
}
}
else
filename = curarg;
}
if (argerr || filename == nullptr || outfilename == nullptr)
{
fprintf(stderr, "Usage: vgmrender <inputfile> -o <outputfile> [-r <rate>]\n");
return 1;
}
FILE *file = fopen(filename, "rb");
if (file == nullptr)
{
fprintf(stderr, "Error opening file '%s'\n", filename);
return 2;
}
fseek(file, 0, SEEK_END);
uint32_t size = ftell(file);
fseek(file, 0, SEEK_SET);
std::vector<uint8_t> buffer(size);
auto bytes_read = fread(&buffer[0], 1, size, file);
if (bytes_read != size)
{
fprintf(stderr, "Error reading file contents\n");
return 3;
}
fclose(file);
if (buffer.size() >= 10 && buffer[0] == 0x1f && buffer[1] == 0x8b && buffer[2] == 0x08)
{
std::vector<uint8_t> compressed = buffer;
uint8_t *end = &compressed[compressed.size()];
uint32_t uncompressed = end[-4] | (end[-3] << 8) | (end[-2] << 16) | (end[-1] << 24);
if (size < compressed.size() || size > 32*1024*1024)
{
fprintf(stderr, "File '%s' appears to be a compressed file but has unexpected size of %d\n", filename, size);
return 4;
}
buffer.resize(uncompressed);
auto result = em_inflate(&compressed[0], compressed.size(), &buffer[0], buffer.size());
if (result == -1)
{
fprintf(stderr, "Error decompressing data from file\n");
return 4;
}
}
uint32_t offset = 0;
if (buffer.size() < 64 || buffer[0] != 'V' || buffer[1] != 'g' || buffer[2] != 'm' || buffer[3] != ' ')
{
fprintf(stderr, "File '%s' does not appear to be a valid VGM file\n", filename);
return 4;
}
uint32_t data_start = parse_header(buffer);
if (active_chips.size() == 0)
{
fprintf(stderr, "No compatible chips found, exiting.\n");
return 5;
}
std::vector<int32_t> wav_buffer;
generate_all(buffer, data_start, output_rate, wav_buffer);
int err = write_wav(outfilename, output_rate, wav_buffer);
#if (CAPTURE_NATIVE)
{
int chipnum = 0;
for (auto &chip : active_chips)
if (err == 0 && chip->m_native_data.size() > 0)
{
char filename[20];
sprintf(filename, "native-%d.wav", chipnum++);
err = write_wav(filename, chip->sample_rate(), chip->m_native_data);
}
}
#endif
#if (RUN_NUKED_OPN2)
{
int chipnum = 0;
for (auto &chip : active_chips)
if (err == 0 && chip->m_nuked_data.size() > 0)
{
char filename[20];
sprintf(filename, "nuked-%d.wav", chipnum++);
err = write_wav(filename, chip->sample_rate(), chip->m_nuked_data);
}
}
#endif
active_chips.clear();
return err;
}
#if (RUN_NUKED_OPN2)
namespace nuked {
#include "test/ym3438.c"
}
#endif