Skip to content

Commit

Permalink
Merge pull request #75 from jpcima/vgz
Browse files Browse the repository at this point in the history
support loading VGZ files with zlib
  • Loading branch information
Wohlstand authored Mar 30, 2019
2 parents 46bcc2a + 9b40a64 commit 37b19ef
Show file tree
Hide file tree
Showing 11 changed files with 647 additions and 210 deletions.
9 changes: 6 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ find_package(Qt5Concurrent REQUIRED)
find_package(Qt5LinguistTools REQUIRED)
set(QWT_NAMES qwt-qt5 qwt) # find versions for Qt5 only
find_package(Qwt)
find_package(ZLIB REQUIRED)
find_package(Threads REQUIRED)

if(QWT_FOUND)
Expand Down Expand Up @@ -82,10 +83,12 @@ set(FILEFORMATS_SOURCES
"src/FileFormats/format_opm.cpp"
"src/FileFormats/format_mucom88_dat.cpp"
"src/FileFormats/format_vgi.cpp"
"src/FileFormats/format_wohlstand_opn2.cpp")
"src/FileFormats/format_wohlstand_opn2.cpp"
"src/FileFormats/ym2612_to_wopi.cpp"
"src/FileFormats/ym2151_to_wopi.cpp")
add_library(FileFormats STATIC ${FILEFORMATS_SOURCES})
target_include_directories(FileFormats PUBLIC "src")
target_link_libraries(FileFormats PUBLIC Common)
target_include_directories(FileFormats PUBLIC "src" PRIVATE ${ZLIB_INCLUDE_DIRS})
target_link_libraries(FileFormats PUBLIC Common PRIVATE ${ZLIB_LIBRARIES})

include(src/opl/chips/chipset.cmake)
add_library(Chips STATIC ${CHIPS_SOURCES})
Expand Down
8 changes: 6 additions & 2 deletions FMBankEdit.pro
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ win32 {
CONFIG += rtmidi
CONFIG += rtaudio
#CONFIG += plots
LIBS += -lz

!macx:{
QMAKE_CXXFLAGS += -fopenmp
Expand Down Expand Up @@ -119,6 +120,8 @@ SOURCES += \
src/FileFormats/format_mucom88_dat.cpp \
src/FileFormats/format_vgi.cpp \
src/FileFormats/format_wohlstand_opn2.cpp \
src/FileFormats/ym2612_to_wopi.cpp \
src/FileFormats/ym2151_to_wopi.cpp \
src/formats_sup.cpp \
src/importer.cpp \
src/latency.cpp \
Expand Down Expand Up @@ -155,6 +158,8 @@ HEADERS += \
src/FileFormats/format_mucom88_dat.h \
src/FileFormats/format_vgi.h \
src/FileFormats/format_wohlstand_opn2.h \
src/FileFormats/ym2612_to_wopi.h \
src/FileFormats/ym2151_to_wopi.h \
src/formats_sup.h \
src/importer.h \
src/latency.h \
Expand All @@ -168,8 +173,7 @@ HEADERS += \
src/opl/realtime/ring_buffer.h \
src/opl/realtime/ring_buffer.tcc \
src/piano.h \
src/version.h \
src/FileFormats/ym2612_to_wopi.hpp
src/version.h

FORMS += \
src/bank_editor.ui \
Expand Down
2 changes: 1 addition & 1 deletion src/FileFormats/format_gym_import.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

#include "format_gym_import.h"
#include "ym2612_to_wopi.hpp"
#include "ym2612_to_wopi.h"
#include "../common.h"

#include <QSet>
Expand Down
2 changes: 1 addition & 1 deletion src/FileFormats/format_s98_import.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

#include "format_s98_import.h"
#include "ym2612_to_wopi.hpp"
#include "ym2612_to_wopi.h"
#include "../common.h"
#include <memory>

Expand Down
208 changes: 200 additions & 8 deletions src/FileFormats/format_vgm_import.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,114 @@
*/

#include "format_vgm_import.h"
#include "ym2612_to_wopi.hpp"
#include "ym2612_to_wopi.h"
#include "ym2151_to_wopi.h"
#include "../common.h"

#include <QSet>
#include <QByteArray>
#include <QBuffer>
#include <zlib.h>
#include <algorithm>
#include <cstring>

static void make_size_table(uint8_t *table, unsigned version);
static gzFile gzopen_q(const QString &path, const char *mode);

const char magic_vgm[4] = {0x56, 0x67, 0x6D, 0x20};
const unsigned char magic_gzip[2] = {0x1F, 0x8B};

bool VGM_Importer::detect(const QString &, char *magic)
bool VGM_Importer::detect(const QString &filePath, char *magic)
{
return (memcmp(magic_vgm, magic, 4) == 0);
if(memcmp(magic_vgm, magic, 4) == 0)
return true;

//Try as compressed VGM file
if(memcmp(magic_gzip, magic, 2) == 0)
{
gzFile vgz = gzopen_q(filePath, "rb");
if(!vgz)
return false;

char compMagic[4];
int count = (int)gzread(vgz, compMagic, 4);
gzclose(vgz);
if(count == 4 && memcmp(compMagic, magic_vgm, 4) == 0)
return true;
}

return false;
}

FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank)
{
QFile file(filePath);
if(!file.open(QIODevice::ReadOnly))
return FfmtErrCode::ERR_NOFILE;

char magic[4];
if(file.read(magic, 4) != 4)
return FfmtErrCode::ERR_BADFORMAT;

if(memcmp(magic_vgm, magic, 4) == 0)
{
file.seek(0);
return load(file, bank);
}

file.close();

//Try as compressed VGM file
if(memcmp(magic_gzip, magic, 2) == 0)
{
gzFile vgz = gzopen_q(filePath, "rb");
if(!vgz)
return FfmtErrCode::ERR_NOFILE;

QBuffer buffer;
buffer.open(QBuffer::ReadWrite);

int n;
char buf[1024];
while((n = (int)gzread(vgz, buf, sizeof(buf))) > 0)
buffer.write(buf, n);
if(n == -1)
return FfmtErrCode::ERR_BADFORMAT;
gzclose(vgz);

buffer.open(QIODevice::ReadOnly);
return load(buffer, bank);
}

return FfmtErrCode::ERR_BADFORMAT;
}

FfmtErrCode VGM_Importer::load(QIODevice &file, FmBank &bank)
{
RawYm2612ToWopi pseudoChip;
RawYm2612ToWopi pseudoChip2608;
RawYm2151ToWopi pseudoChip2151;
pseudoChip2608.shareInstruments(pseudoChip);
pseudoChip2151.shareInstruments(pseudoChip);

char magic[4];
uint8_t numb[4];

QFile file(filePath);
if(!file.open(QIODevice::ReadOnly))
return FfmtErrCode::ERR_NOFILE;

bank.reset();
if(file.read(magic, 4) != 4)
return FfmtErrCode::ERR_BADFORMAT;

if(memcmp(magic, magic_vgm, 4) != 0)
return FfmtErrCode::ERR_BADFORMAT;

file.seek(0x8);
if(file.read(char_p(numb), 4) != 4)
return FfmtErrCode::ERR_BADFORMAT;

uint32_t vgm_version = toUint32LE(numb);
uint8_t vgm_sizetable[0x100];
make_size_table(vgm_sizetable, vgm_version);

file.seek(0x34);
file.read(char_p(numb), 4);

Expand All @@ -71,6 +145,18 @@ FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank)
file.read(char_p(&cmd), 1);
switch(cmd)
{
default: {
uint8_t toSkip = vgm_sizetable[cmd];
if(toSkip != 0xFF)
file.seek(file.pos() + toSkip);
else
{
//Unrecognized command
end = true;
}
break;
}

case 0x52://Write YM2612 port 0
file.read(char_p(&reg), 1);
file.read(char_p(&val), 1);
Expand All @@ -82,6 +168,12 @@ FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank)
pseudoChip.passReg(1, reg, val);
break;

case 0x54://Write YM2151 port
file.read(char_p(&reg), 1);
file.read(char_p(&val), 1);
pseudoChip2151.passReg(reg, val);
break;

case 0x56://Write YM2608 port 0
file.read(char_p(&reg), 1);
file.read(char_p(&val), 1);
Expand Down Expand Up @@ -116,6 +208,7 @@ FfmtErrCode VGM_Importer::loadFile(QString filePath, FmBank &bank)
file.seek(file.pos() + 2);
pseudoChip.doAnalyzeState();
pseudoChip2608.doAnalyzeState();
pseudoChip2151.doAnalyzeState();
break;

case 0x66://End of sound data
Expand Down Expand Up @@ -179,10 +272,109 @@ QString VGM_Importer::formatModuleName() const

QString VGM_Importer::formatExtensionMask() const
{
return "*.vgm";
return "*.vgm *.vgz";
}

BankFormats VGM_Importer::formatId() const
{
return BankFormats::FORMAT_VGM_IMPORTER;
}

static void make_size_table(uint8_t *table, unsigned version)
{
std::memset(table, 0xFF, 0x100);
table[0x4F] = 1; // Game Gear PSG stereo, write dd to port 0x06
table[0x50] = 1; // PSG (SN76489/SN76496) write value dd
table[0x51] = 2; // YM2413, write value dd to register aa
table[0x52] = 2; // YM2612 port 0, write value dd to register aa
table[0x53] = 2; // YM2612 port 1, write value dd to register aa
table[0x54] = 2; // YM2151, write value dd to register aa
table[0x55] = 2; // YM2203, write value dd to register aa
table[0x56] = 2; // YM2608 port 0, write value dd to register aa
table[0x57] = 2; // YM2608 port 1, write value dd to register aa
table[0x58] = 2; // YM2610 port 0, write value dd to register aa
table[0x59] = 2; // YM2610 port 1, write value dd to register aa
table[0x5A] = 2; // YM3812, write value dd to register aa
table[0x5B] = 2; // YM3526, write value dd to register aa
table[0x5C] = 2; // Y8950, write value dd to register aa
table[0x5D] = 2; // YMZ280B, write value dd to register aa
table[0x5E] = 2; // YMF262 port 0, write value dd to register aa
table[0x5F] = 2; // YMF262 port 1, write value dd to register aa
table[0x61] = 2; // Wait n samples, n can range from 0 to 65535 (approx 1.49 seconds). Longer pauses than this are represented by multiple wait commands.
table[0x62] = 0; // wait 735 samples (60th of a second), a shortcut for 0x61 0xdf 0x02
table[0x63] = 0; // wait 882 samples (50th of a second), a shortcut for 0x61 0x72 0x03
table[0x66] = 0; // end of sound data
table[0x67] = /* variable size */0xFF; // data block: see below
table[0x68] = 11; // PCM RAM write: see below
for(unsigned a = 0x70; a <= 0x7F; ++a)
table[a] = 0; // wait n+1 samples, n can range from 0 to 15.
for(unsigned a = 0x80; a <= 0x8F; ++a)
table[a] = 0; // YM2612 port 0 address 2A write from the data bank, then wait n samples; n can range from 0 to 15. Note that the wait is n, NOT n+1. See also command 0xE0.
table[0x90] = 4; // Setup Stream Control
table[0x91] = 4; // Set Stream Data
table[0x92] = 5; // Set Stream Frequency
table[0x93] = 10; // Start Stream
table[0x94] = 1; // Stop Stream
table[0x95] = 4; // Start Stream (fast call)
table[0xA0] = 2; // AY8910, write value dd to register aa
table[0xB0] = 2; // RF5C68, write value dd to register aa
table[0xB1] = 2; // RF5C164, write value dd to register aa
table[0xB2] = 2; // PWM, write value ddd to register a (d is MSB, dd is LSB)
table[0xB3] = 2; // GameBoy DMG, write value dd to register aa
table[0xB4] = 2; // NES APU, write value dd to register aa
table[0xB5] = 2; // MultiPCM, write value dd to register aa
table[0xB6] = 2; // uPD7759, write value dd to register aa
table[0xB7] = 2; // OKIM6258, write value dd to register aa
table[0xB8] = 2; // OKIM6295, write value dd to register aa
table[0xB9] = 2; // HuC6280, write value dd to register aa
table[0xBA] = 2; // K053260, write value dd to register aa
table[0xBB] = 2; // Pokey, write value dd to register aa
table[0xBC] = 2; // WonderSwan, write value dd to register aa
table[0xBD] = 2; // SAA1099, write value dd to register aa
table[0xBE] = 2; // ES5506, write value dd to register aa
table[0xBF] = 2; // GA20, write value dd to register aa
table[0xC0] = 3; // Sega PCM, write value dd to memory offset aabb
table[0xC1] = 3; // RF5C68, write value dd to memory offset aabb
table[0xC2] = 3; // RF5C164, write value dd to memory offset aabb
table[0xC3] = 3; // MultiPCM, write set bank offset aabb to channel cc
table[0xC4] = 3; // QSound, write value mmll to register rr (mm - data MSB, ll - data LSB)
table[0xC5] = 3; // SCSP, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB)
table[0xC6] = 3; // WonderSwan, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB)
table[0xC7] = 3; // VSU, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB)
table[0xC8] = 3; // X1-010, write value dd to memory offset mmll (mm - offset MSB, ll - offset LSB)
table[0xD0] = 3; // YMF278B, port pp, write value dd to register aa
table[0xD1] = 3; // YMF271, port pp, write value dd to register aa
table[0xD2] = 3; // SCC1, port pp, write value dd to register aa
table[0xD3] = 3; // K054539, write value dd to register ppaa
table[0xD4] = 3; // C140, write value dd to register ppaa
table[0xD5] = 3; // ES5503, write value dd to register ppaa
table[0xD6] = 3; // ES5506, write value aadd to register pp
table[0xE0] = 4; // Seek to offset dddddddd (Intel byte order) in PCM data bank of data block type 0 (YM2612).
table[0xE1] = 4; // C352, write value aadd to register mmll
for(unsigned a = 0x30; a <= 0x3F; ++a)
table[a] = 1; // one operand, reserved for future use
for(unsigned a = 0x40; a <= 0x4E; ++a)
{
if(version >= 0x160)
table[a] = 2; // two operands, reserved for future use
else
table[a] = 1; // was one operand only til v1.60
}
for(unsigned a = 0xA1; a <= 0xAF; ++a)
table[a] = 2; // two operands, reserved for future use
for(unsigned a = 0xC9; a <= 0xCF; ++a)
table[a] = 3; // three operands, reserved for future use
for(unsigned a = 0xD7; a <= 0xDF; ++a)
table[a] = 3; // three operands, reserved for future use
for(unsigned a = 0xE2; a <= 0xFF; ++a)
table[a] = 4; // three operands, reserved for future use
}

static gzFile gzopen_q(const QString &path, const char *mode)
{
#ifndef _WIN32
return gzopen(path.toStdString().c_str(), mode);
#else
return gzopen_w(path.toStdWString().c_str(), mode);
#endif
}
5 changes: 5 additions & 0 deletions src/FileFormats/format_vgm_import.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#include "ffmt_base.h"

class QIODevice;

/**
* @brief Import from VGM files
*/
Expand All @@ -34,6 +36,9 @@ class VGM_Importer final : public FmBankFormatBase
QString formatModuleName() const override;
QString formatExtensionMask() const override;
BankFormats formatId() const override;

private:
FfmtErrCode load(QIODevice &file, FmBank &bank);
};

#endif // VGM_IMPORT_H
Loading

0 comments on commit 37b19ef

Please sign in to comment.