commit 48dcc093999e17dc41144cb55f79ab6192a65a7b Author: Nick Date: Thu Jun 14 12:29:59 2018 +1000 Initial commit. 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; +}