From 48dcc093999e17dc41144cb55f79ab6192a65a7b Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 14 Jun 2018 12:29:59 +1000 Subject: [PATCH] Initial commit. --- .gitignore | 6 + README.md | 32 ++++ adpcm.Makefile | 70 +++++++ adpcm.c | 158 ++++++++++++++++ adpcm.txt | 16 ++ adpcmb.Makefile | 15 ++ adpcmb.c | 445 ++++++++++++++++++++++++++++++++++++++++++++ autoextract.bat | 19 ++ neoadpcmextract.cpp | 109 +++++++++++ 9 files changed, 870 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 adpcm.Makefile create mode 100644 adpcm.c create mode 100644 adpcm.txt create mode 100644 adpcmb.Makefile create mode 100644 adpcmb.c create mode 100644 autoextract.bat create mode 100644 neoadpcmextract.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66f2fcc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.exe +*.vgm +*.vgz +*.log +*.pcm +*.wav diff --git a/README.md b/README.md new file mode 100644 index 0000000..5c17ccf --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +Neo-Geo VGM tools +----------------- + +A hodge-podge of tools for working on NG VGM files, +these files are provided in the hope that they may be useful to someone. + +Makefiles have currently not been tested but shouldn't be hard to get working. +Windows binaries are available under the Releases tab. + +Included tools (sources included). + - **adpcm.exe**: + ADPCM Type-A to WAV converter. + - **adpcmb.exe**: + ADPCM Type-B encoding tool that also does decoding to WAV. + - **neoadpcmextract.exe**: + Scans a .vgm and dumps all ADPCM type A&B data to raw .pcm files. + - **autoextract.bat**: + Convenience batch script that uses the above tools to dump all samples to WAVs. + +How to use +---------- +Copy your .vgz into this directory, drag it onto autoextract.bat, +the script will create a directory of the same name and dump all the converted samples there. + +That's all there is to it. + +TODO +---- + + - Replace the batch script with something less shite (and actually cross-platform). + - Write a makefile for neoadpcmextract. + - Make this stuff build on Linux. diff --git a/adpcm.Makefile b/adpcm.Makefile new file mode 100644 index 0000000..abab6f9 --- /dev/null +++ b/adpcm.Makefile @@ -0,0 +1,70 @@ +# Standard makefile to use as a base for DJGPP projects +# By MARTINEZ Fabrice aka SNK of SUPREMACY + +# Programs to use during make +AR = ar +CC = gcc +LD = gcc +ASM = nasm +PACKER = upx + +# Flags for debugging +#DEBUG=1 +#SYMBOLS=1 + +# Flags for compilation +ASMFLAGS = -f coff +ifdef SYMBOLS +CFLAGS = -o -mpentium -Wall -Werror -g +LDFLAGS = -s +else +CFLAGS = -fomit-frame-pointer -O3 -mpentium -Werror -Wall \ + -W -Wno-sign-compare -Wno-unused \ + -Wpointer-arith -Wbad-function-cast -Wcast-align -Waggregate-return \ + -pedantic \ + -Wshadow \ + -Wstrict-prototypes +LDFLAGS = +endif +CDEFS = +ASMDEFS = + +# Object files +MAINOBJS = + +# Library files +MAINLIBS = obj/adpcm.a + +# Make rules +all: adpcm.exe + +adpcm.exe: $(MAINOBJS) $(MAINLIBS) + $(LD) $(LDFLAGS) $(MAINOBJS) $(MAINLIBS) -o $@ + +src/%.asm: + +obj/%.o: src/%.c + $(CC) $(CDEFS) $(CFLAGS) -c $< -o $@ + +obj/%.oa: src/%.asm + $(ASM) -o $@ $(ASMFLAGS) $(ASMDEFS) $< + +obj/%.a: + $(AR) cr $@ $^ + +# Rules to manage files +pack: adpcm.exe + $(PACKER) adpcm.exe + +mkdir: + md obj + +clean: + del obj\*.o + del obj\*.a + del obj\*.oa + del *.exe + +# Rules to make libraries +obj/adpcm.a: obj/adpcm.o +# obj/decode.oa \ \ No newline at end of file diff --git a/adpcm.c b/adpcm.c new file mode 100644 index 0000000..1a27e51 --- /dev/null +++ b/adpcm.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 1024*256 +#define ADPCMA_VOLUME_RATE 1 +#define ADPCMA_DECODE_RANGE 1024 +#define ADPCMA_DECODE_MIN (-(ADPCMA_DECODE_RANGE*ADPCMA_VOLUME_RATE)) +#define ADPCMA_DECODE_MAX ((ADPCMA_DECODE_RANGE*ADPCMA_VOLUME_RATE)-1) +#define ADPCMA_VOLUME_DIV 1 + +unsigned char RiffWave[44] = { + 0x52, 0x49, 0x46, 0x46, 0x24, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, + 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x44, 0x48, 0x00, 0x00, 0x88, 0x90, + 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 + } ; + +static int decode_tableA1[16] = { + -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16, + -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16 +}; +static int jedi_table[49*16]; +static int signal; +static int delta; + +void adpcm_init(void); +void adpcm_decode(void *, void *, int); +int Limit(int, int, int); +FILE* errorlog; + +int main(int argc, char *argv[]) +{ + FILE *Fp1, *Fp2; + void *InputBuffer, *OutputBuffer; + int Readed; + unsigned int Filelen; + + puts("**** ADPCM to PCM converter v 1.01\n"); + if (argc!=3) { + puts("USAGE: adpcm "); + exit(-1); + } + + Fp1 = fopen(argv[1], "rb"); + if (Fp1==NULL) { + printf("Could not open inputfile %s\n", argv[1]); + exit(-2); + } + + Fp2 = fopen(argv[2], "wb"); + if (Fp2==NULL) { + printf("Could not open outputfile %s\n", argv[2]); + exit(-3); + } + + errorlog = fopen("error.log", "wb"); + + InputBuffer = malloc(BUFFER_SIZE); + if (InputBuffer == NULL) { + printf("Could not allocate input buffer. (%d bytes)\n", BUFFER_SIZE); + exit(-4); + } + + OutputBuffer = malloc(BUFFER_SIZE*10); + if (OutputBuffer == NULL) { + printf("Could not allocate output buffer. (%d bytes)\n", BUFFER_SIZE*4); + exit(-5); + } + + adpcm_init(); + + Filelen = filelength(fileno(Fp1)); + *((unsigned int*)(&RiffWave[4])) = Filelen*4 + 0x2C; + *((unsigned int*)(&RiffWave[0x28])) = Filelen*4; + + fwrite(RiffWave, 0x2c, 1, Fp2); + + do { + Readed = fread(InputBuffer, 1, BUFFER_SIZE, Fp1); + if (Readed>0) { + adpcm_decode(InputBuffer, OutputBuffer, Readed); + fwrite(OutputBuffer, Readed*4, 1, Fp2); + } + } while (Readed==BUFFER_SIZE); + + free(InputBuffer); + free(OutputBuffer); + fclose(Fp1); + fclose(Fp2); + + puts("Done..."); + + return 0; +} + +void adpcm_init(void) +{ + int step, nib; + + for (step = 0; step <= 48; step++) + { + int stepval = floor (16.0 * pow (11.0 / 10.0, (double)step) * ADPCMA_VOLUME_RATE); + /* loop over all nibbles and compute the difference */ + for (nib = 0; nib < 16; nib++) + { + int value = stepval*((nib&0x07)*2+1)/8; + jedi_table[step*16+nib] = (nib&0x08) ? -value : value; + } + } + + delta = 0; + signal = 0; +} + +void adpcm_decode(void *InputBuffer, void *OutputBuffer, int Length) +{ + char *in; + short *out; + int i, data, oldsignal; + + in = (char *)InputBuffer; + out = (short *)OutputBuffer; + + for(i=0;i>4)&0x0F; + oldsignal = signal; + signal = Limit(signal + (jedi_table[data+delta]), ADPCMA_DECODE_MAX, ADPCMA_DECODE_MIN); + delta = Limit(delta + decode_tableA1[data], 48*16, 0*16); + if (abs(oldsignal-signal)>2500) { + fprintf(errorlog, "WARNING: Suspicious signal evolution %06x,%06x pos:%06x delta:%06x\n", oldsignal, signal, i, delta); + fprintf(errorlog, "data:%02x dx:%08x\n", data, jedi_table[data+delta]); + } + *(out++) = (signal&0xffff)*32; + + data = (*in++)&0x0F; + oldsignal = signal; + signal = Limit(signal + (jedi_table[data+delta]), ADPCMA_DECODE_MAX, ADPCMA_DECODE_MIN); + delta = Limit(delta + decode_tableA1[data], 48*16, 0*16); + if (abs(oldsignal-signal)>2500) { + fprintf(errorlog, "WARNING: Suspicious signal evolution %06x,%06x pos:%06x delta:%06x\n", oldsignal, signal, i, delta); + fprintf(errorlog, "data:%02x dx:%08x\n", data, jedi_table[data+delta]); + } + *(out++) = (signal&0xffff)*32; + } +} + +int Limit( int val, int max, int min ) { + + if ( val > max ) + val = max; + else if ( val < min ) + val = min; + + return val; +} + diff --git a/adpcm.txt b/adpcm.txt new file mode 100644 index 0000000..9124501 --- /dev/null +++ b/adpcm.txt @@ -0,0 +1,16 @@ +**** ADPCM to PCM converter v 1.01 + +USAGE: adpcm + +By MARTINEZ Fabrice aka SNK of SUPREMACY + + + +--(ADPCM.diz.txt)-- + + ADPCM To WAV Converter +This simple utility converts ADPCM files from NeoGeo cartridge systems +(*_v?.rom) or NeoGeo CD systems (*.PCM). This tool converts ADPCM A type +samples (99% of the samples), ADPCM B type samples will sound distorted... +Get it here + diff --git a/adpcmb.Makefile b/adpcmb.Makefile new file mode 100644 index 0000000..0dbfcca --- /dev/null +++ b/adpcmb.Makefile @@ -0,0 +1,15 @@ +CC = gcc +CFLAGS = -O3 + +SRC = . +OBJ = . + +OUT_OBJ = $(OBJ)/adpcmb.exe + +all: $(OUT_OBJ) + +$(OBJ)/%.exe: $(SRC)/%.c + $(CC) $(CCFLAGS) $(MAINFLAGS) $< -o $@ + +clean: + rm $(OUT_OBJ) diff --git a/adpcmb.c b/adpcmb.c new file mode 100644 index 0000000..24f4ac4 --- /dev/null +++ b/adpcmb.c @@ -0,0 +1,445 @@ +/*; YM2610 ADPCM-B Codec +; +;**** PCM to ADPCM-B & ADPCM-B to PCM converters for NEO-GEO System **** +;ADPCM-B - 1 channel 1.8-55.5 KHz, 16 MB Sample ROM size, 256 B min size of sample, 16 MB max, compatable with YM2608 +; +;http://www.raregame.ru/file/15/YM2610.pdf YM2610 DATASHEET +; +; Fred/FRONT +; +;Usage 1: ADPCM_Encode.exe -d [-r:reg,clock] Input.bin Output.wav +;Usage 2: ADPCM_Encode.exe -e Input.wav Output.bin +; +; Valley Bell +;----------------------------------------------------------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#define INLINE static __inline +#elif defined(__GNUC__) +#define INLINE static __inline__ +#else +#define INLINE static inline +#endif + +#ifdef USE_STDINT + +#include +typedef uint8_t UINT8; +typedef int8_t INT8; +typedef uint16_t UINT16; +typedef int16_t INT16; +typedef uint32_t UINT32; +typedef int32_t INT32; + +#else + +typedef unsigned char UINT8; +typedef signed char INT8; +typedef unsigned short UINT16; +typedef signed short INT16; +typedef unsigned int UINT32; +typedef signed int INT32; + +#endif + + +typedef UINT8 BYTE; +typedef UINT16 WORD; +typedef UINT32 DWORD; + +// -- from mmsystem.h -- +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \ + ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 )) + +// -- from mmreg.h, slightly modified -- + +/* general waveform format structure (information common to all formats) */ +typedef struct waveformat_tag { + WORD wFormatTag; /* format type */ + WORD nChannels; /* number of channels (i.e. mono, stereo...) */ + DWORD nSamplesPerSec; /* sample rate */ + DWORD nAvgBytesPerSec; /* for buffer estimation */ + WORD nBlockAlign; /* block size of data */ + WORD wBitsPerSample; +} WAVEFORMAT; + +/* flags for wFormatTag field of WAVEFORMAT */ +#define WAVE_FORMAT_PCM 1 + +// -- from mm*.h end -- + +#define FOURCC_RIFF MAKEFOURCC('R', 'I', 'F', 'F') +#define FOURCC_WAVE MAKEFOURCC('W', 'A', 'V', 'E') +#define FOURCC_fmt_ MAKEFOURCC('f', 'm', 't', ' ') +#define FOURCC_data MAKEFOURCC('d', 'a', 't', 'a') + +typedef struct +{ + UINT32 RIFFfcc; // 'RIFF' + UINT32 RIFFLen; + UINT32 WAVEfcc; // 'WAVE' + UINT32 fmt_fcc; // 'fmt ' + UINT32 fmt_Len; + WAVEFORMAT fmt_Data; + UINT32 datafcc; // 'data' + UINT32 dataLen; +} WAVE_FILE; + + +static const long stepsizeTable[16] = +{ + 57, 57, 57, 57, 77, 102, 128, 153, + 57, 57, 57, 57, 77, 102, 128, 153 +}; + +static int YM2610_ADPCM_Encode(INT16 *src, UINT8 *dest, int len) +{ + int lpc, flag; + long i, dn, xn, stepSize; + UINT8 adpcm; + UINT8 adpcmPack; + + xn = 0; + stepSize = 127; + flag = 0; + + for (lpc = 0; lpc < len; lpc ++) + { + dn = *src - xn; + src ++; + + i = (abs(dn) << 16) / (stepSize << 14); + if (i > 7) + i = 7; + adpcm = (UINT8)i; + + i = (adpcm * 2 + 1) * stepSize / 8; + + if (dn < 0) + { + adpcm |= 0x8; + xn -= i; + } + else + { + xn += i; + } + + stepSize = (stepsizeTable[adpcm] * stepSize) / 64; + + if (stepSize < 127) + stepSize = 127; + else if (stepSize > 24576) + stepSize = 24576; + + if (flag == 0) + { + adpcmPack = (adpcm << 4); + flag = 1; + } + else + { + adpcmPack |= adpcm; + *dest = adpcmPack; + dest ++; + flag = 0; + } + } + + return 0; +} + +static int YM2610_ADPCM_Decode(UINT8 *src, INT16 *dest, int len) +{ + int lpc, flag, shift, step; + long i, xn, stepSize; + UINT8 adpcm; + + xn = 0; + stepSize = 127; + flag = 0; + shift = 4; + step = 0; + + for (lpc = 0; lpc < len; lpc ++) + { + adpcm = (*src >> shift) & 0xf; + + i = ((adpcm & 7) * 2 + 1) * stepSize / 8; + if (adpcm & 8) + xn -= i; + else + xn += i; + + if (xn > 32767) + xn = 32767; + else if (xn < -32768) + xn = -32768; + + stepSize = stepSize * stepsizeTable[adpcm] / 64; + + if (stepSize < 127) + stepSize = 127; + else if (stepSize > 24576) + stepSize = 24576; + + *dest = (INT16)xn; + dest ++; + + src += step; + step = step ^ 1; + shift = shift ^ 4; + } + + return 0; +} + +INLINE UINT32 DeltaTReg2SampleRate(UINT16 DeltaN, UINT32 Clock) +{ + return (UINT32)(DeltaN * (Clock / 72.0) / 65536.0 + 0.5); +} + +int main(int argc, char* argv[]) +{ + int ErrVal; + int ArgBase; + DWORD OutSmplRate; + FILE* hFile; + unsigned int AdpcmSize; + UINT8* AdpcmData; + unsigned int WaveSize; + UINT16* WaveData; + WAVE_FILE WaveFile; + WAVEFORMAT* TempFmt; + unsigned int TempLng; + UINT16 DTRegs; + char* TempPnt; + + printf("NeoGeo ADPCM-B En-/Decoder\n--------------------------\n"); + if (argc < 4) + { + printf("Usage: ADPCM_Encode.exe -method [-option] InputFile OutputFile\n"); + printf("-method - En-/Decoding Method:\n"); + printf(" -d for decode (bin -> wav)\n"); + printf(" -e for encode (wav -> bin)\n"); + printf("-option - Options for Sample Rate of decoded Wave:\n"); + printf(" -s:rate - Sample Rate in Hz (default: 22050)\n"); + printf(" -r:regs[,clock] - DeltaT Register value (use 0x for hex) and chip clock\n"); + printf(" (default chip clock: 4 MHz)\n"); + printf("\n"); + printf("Wave In-/Output is 16-bit, Mono\n"); + return 1; + } + + if (strcmp(argv[1], "-d") && strcmp(argv[1], "-e")) + { + printf("Wrong option! Use -d or -e!\n"); + return 1; + } + + ErrVal = 0; + AdpcmData = NULL; + WaveData = NULL; + OutSmplRate = 0; + + ArgBase = 2; + if (argv[2][0] == '-' && argv[2][2] == ':') + { + switch(argv[2][1]) + { + case 's': + OutSmplRate = strtol(argv[2] + 3, NULL, 0); + break; + case 'r': + DTRegs = (UINT16)strtoul(argv[2] + 3, &TempPnt, 0); + TempLng = 0; + if (*TempPnt == ',') + { + TempLng = strtoul(TempPnt + 1, NULL, 0); + } + if (! TempLng) + TempLng = 4000000; + OutSmplRate = DeltaTReg2SampleRate(DTRegs, TempLng); + break; + } + ArgBase ++; + if (argc < ArgBase + 2) + { + printf("Not enought arguments!\n"); + return 1; + } + } + + switch(argv[1][1]) + { + case 'd': + hFile = fopen(argv[ArgBase + 0], "rb"); + if (hFile == NULL) + { + printf("Error opening input file!\n"); + ErrVal = 2; + goto Finish; + } + + fseek(hFile, 0x00, SEEK_END); + AdpcmSize = ftell(hFile); + + fseek(hFile, 0x00, SEEK_SET); + AdpcmData = (UINT8*)malloc(AdpcmSize); + fread(AdpcmData, 0x01, AdpcmSize, hFile); + fclose(hFile); + + WaveSize = AdpcmSize * 2; // 4-bit ADPCM -> 2 values per byte + WaveData = (UINT16*)malloc(WaveSize * 2); + printf("Decoding ..."); + YM2610_ADPCM_Decode(AdpcmData, WaveData, WaveSize); + printf(" OK\n"); + + WaveFile.RIFFfcc = FOURCC_RIFF; + WaveFile.WAVEfcc = FOURCC_WAVE; + WaveFile.fmt_fcc = FOURCC_fmt_; + WaveFile.fmt_Len = sizeof(WAVEFORMAT); + + TempFmt = &WaveFile.fmt_Data; + TempFmt->wFormatTag = WAVE_FORMAT_PCM; + TempFmt->nChannels = 1; + TempFmt->wBitsPerSample = 16; + TempFmt->nSamplesPerSec = OutSmplRate ? OutSmplRate : 22050; + TempFmt->nBlockAlign = TempFmt->nChannels * TempFmt->wBitsPerSample / 8; + TempFmt->nAvgBytesPerSec = TempFmt->nBlockAlign * TempFmt->nSamplesPerSec; + + WaveFile.datafcc = FOURCC_data; + WaveFile.dataLen = WaveSize * 2; + WaveFile.RIFFLen = 0x04 + 0x08 + WaveFile.fmt_Len + 0x08 + WaveFile.dataLen; + + hFile = fopen(argv[ArgBase + 1], "wb"); + if (hFile == NULL) + { + printf("Error opening output file!\n"); + ErrVal = 3; + goto Finish; + } + + fwrite(&WaveFile, sizeof(WAVE_FILE), 0x01, hFile); + fwrite(WaveData, 0x02, WaveSize, hFile); + fclose(hFile); + printf("File written.\n"); + + break; + case 'e': + hFile = fopen(argv[ArgBase + 0], "rb"); + if (hFile == NULL) + { + printf("Error opening input file!\n"); + ErrVal = 2; + goto Finish; + } + + fread(&WaveFile.RIFFfcc, 0x0C, 0x01, hFile); + if (WaveFile.RIFFfcc != FOURCC_RIFF || WaveFile.WAVEfcc != FOURCC_WAVE) + { + fclose(hFile); + printf("This is no wave file!\n"); + ErrVal = 4; + goto Finish; + } + + TempLng = fread(&WaveFile.fmt_fcc, 0x04, 0x01, hFile); + fread(&WaveFile.fmt_Len, 0x04, 0x01, hFile); + while(WaveFile.fmt_fcc != FOURCC_fmt_) + { + if (! TempLng) // TempLng == 0 -> EOF reached + { + fclose(hFile); + printf("Error in wave file: Can't find format-tag!\n"); + ErrVal = 4; + goto Finish; + } + fseek(hFile, WaveFile.fmt_Len, SEEK_CUR); + + TempLng = fread(&WaveFile.fmt_fcc, 0x04, 0x01, hFile); + fread(&WaveFile.fmt_Len, 0x04, 0x01, hFile); + }; + TempLng = ftell(hFile) + WaveFile.fmt_Len; + fread(&WaveFile.fmt_Data, sizeof(WAVEFORMAT), 0x01, hFile); + fseek(hFile, TempLng, SEEK_SET); + + TempFmt = &WaveFile.fmt_Data; + if (TempFmt->wFormatTag != WAVE_FORMAT_PCM) + { + fclose(hFile); + printf("Error in wave file: Compressed wave file are not supported!\n"); + ErrVal = 4; + goto Finish; + } + if (TempFmt->nChannels != 1) + { + fclose(hFile); + printf("Error in wave file: Unsupported number of channels (%hu)!\n", TempFmt->nChannels); + ErrVal = 4; + goto Finish; + } + if (TempFmt->wBitsPerSample != 16) + { + fclose(hFile); + printf("Error in wave file: Only 16-bit waves are supported! (File uses %hu bit)\n", TempFmt->wBitsPerSample); + ErrVal = 4; + goto Finish; + } + + TempLng = fread(&WaveFile.datafcc, 0x04, 0x01, hFile); + fread(&WaveFile.dataLen, 0x04, 0x01, hFile); + while(WaveFile.datafcc != FOURCC_data) + { + if (! TempLng) // TempLng == 0 -> EOF reached + { + fclose(hFile); + printf("Error in wave file: Can't find data-tag!\n"); + ErrVal = 4; + goto Finish; + } + fseek(hFile, WaveFile.dataLen, SEEK_CUR); + + TempLng = fread(&WaveFile.datafcc, 0x04, 0x01, hFile); + fread(&WaveFile.dataLen, 0x04, 0x01, hFile); + }; + WaveSize = WaveFile.dataLen / 2; + WaveData = (UINT16*)malloc(WaveSize * 2); + fread(WaveData, 0x02, WaveSize, hFile); + + fclose(hFile); + + AdpcmSize = WaveSize / 2; + AdpcmData = (UINT8*)malloc(AdpcmSize); + printf("Encoding ..."); + YM2610_ADPCM_Encode(WaveData, AdpcmData, WaveSize); + printf(" OK\n"); + + hFile = fopen(argv[ArgBase + 1], "wb"); + if (hFile == NULL) + { + printf("Error opening output file!\n"); + ErrVal = 3; + goto Finish; + } + + fwrite(AdpcmData, 0x01, AdpcmSize, hFile); + fclose(hFile); + printf("File written.\n"); + + break; + } + +Finish: + free(AdpcmData); + free(WaveData); + + return ErrVal; +} diff --git a/autoextract.bat b/autoextract.bat new file mode 100644 index 0000000..614edf8 --- /dev/null +++ b/autoextract.bat @@ -0,0 +1,19 @@ +if ["%~x1"]==[".vgz"] goto vgztopcm else goto vgmtopcm + +:vgmtopcm +neoadpcmextract.exe %1 +goto pcmtowav + +:vgztopcm +copy /y %1 temp.vgm.gz +gzip.exe -d temp.vgm.gz +neoadpcmextract.exe temp.vgm +del temp.vgm +goto pcmtowav + +:pcmtowav +for /r %%v in (smpa_*.pcm) do adpcm.exe "%%v" "%%v.wav" +for /r %%v in (smpb_*.pcm) do adpcmb.exe -d "%%v" "%%v.wav" +del "*.pcm" +mkdir "%~n1" +move "*.wav" "%~n1" \ No newline at end of file diff --git a/neoadpcmextract.cpp b/neoadpcmextract.cpp new file mode 100644 index 0000000..268a5c1 --- /dev/null +++ b/neoadpcmextract.cpp @@ -0,0 +1,109 @@ +/* neoadpcmextract.cpp + Copyright (C) 2017 Nicholas Curtis + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#include +#include +#include + +void DecodeSample ( std::ifstream& a_file, std::vector& a_out ) +{ + // Set up output vector. + uint32_t sampLen = 0; + a_file.read ( (char*)&sampLen, sizeof(uint32_t) ); + if ( sampLen < sizeof(uint64_t) ) + { + return; + } + + sampLen -= sizeof(uint64_t); + a_out.clear (); + a_out.resize ( sampLen ); + + // Ignore 8 bytes. + uint64_t dummy; + a_file.read ( (char*)&dummy, sizeof(uint64_t) ); + + // Read adpcm data. + a_file.read ( (char*)a_out.data (), sampLen ); +} + +void DumpBytes ( std::string a_path, const std::vector& a_bytes ) +{ + std::ofstream fileOut ( a_path, std::ios::binary ); + fileOut.write ( (const char*)a_bytes.data (), a_bytes.size () ); + fileOut.close (); +} + +int main ( int argc, char** argv ) +{ + if ( argc != 2 ) + { + return -1; + } + + // Open file. + std::ifstream file ( argv[1], std::ios::binary ); + if ( !file.is_open () ) + { + return -1; + } + + // Search for pcm headers. + std::vector smpBytes; + int smpA = 0, smpB = 0; + while ( !file.eof () && !file.fail () ) + { + uint8_t byte; + + file >> byte; + if ( byte == 0x67 ) + { + file >> byte; + if ( byte == 0x66 ) + { + file >> byte; + if ( byte == 0x82 ) + { + std::cout << "ADPCM-A data found at 0x" << std::hex << file.tellg () << std::endl; + DecodeSample ( file, smpBytes ); + std::stringstream path; + path << std::hex << "smpa_" << (smpA++) << ".pcm"; + DumpBytes ( path.str (), smpBytes ); + } + else + if ( byte == 0x83 ) + { + std::cout << "ADPCM-B data found at 0x" << std::hex << file.tellg () << std::endl; + DecodeSample ( file, smpBytes ); + std::stringstream path; + path << std::hex << "smpb_" << (smpB++) << ".pcm"; + DumpBytes ( path.str (), smpBytes ); + } + } + } + } + + file.close (); + + return 0; +}