static void RoundVGMData(void) { UINT32 DstPos; UINT8 ChipID; UINT8 Command; UINT32 CmdDelay; UINT8 TempByt; UINT16 TempSht; UINT32 TempLng; #ifdef WIN32 UINT32 CmdTimer; char TempStr[0x80]; char MinSecStr[0x80]; #endif UINT32 CmdLen; bool StopVGM; bool IsDelay; bool WasDelay; bool WriteEvent; UINT32 LoopPos; UINT32 DelayQueue; DstData = (UINT8*)malloc(VGMDataLen); VGMPos = VGMHead.lngDataOffset; DstPos = VGMHead.lngDataOffset; DidSomething = false; #ifdef WIN32 CmdTimer = 0; #endif VGMSmplPos = 0x00; DelayQueue = 0x00; LoopPos = 0x00; StopVGM = false; IsDelay = false; while(VGMPos < VGMHead.lngEOFOffset) { CmdLen = 0x00; Command = VGMData[VGMPos + 0x00]; WasDelay = IsDelay; IsDelay = false; CmdDelay = 0x00; if (Command >= 0x70 && Command <= 0x8F) { switch(Command & 0xF0) { case 0x70: TempSht = (Command & 0x0F) + 0x01; VGMSmplPos += TempSht; CmdDelay = TempSht; IsDelay = true; break; case 0x80: TempSht = Command & 0x0F; VGMSmplPos += TempSht; CmdDelay = TempSht; break; } CmdLen = 0x01; } else { // Cheat Mode (to use 2 instances of 1 chip) ChipID = 0x00; switch(Command) { case 0x30: if (VGMHead.lngHzPSG & 0x40000000) { Command += 0x20; ChipID = 0x01; } break; case 0xA1: if (VGMHead.lngHzYM2413 & 0x40000000) { Command -= 0x50; ChipID = 0x01; } break; case 0xA2: case 0xA3: if (VGMHead.lngHzYM2612 & 0x40000000) { Command -= 0x50; ChipID = 0x01; } break; case 0xA4: if (VGMHead.lngHzYM2151 & 0x40000000) { Command -= 0x50; ChipID = 0x01; } break; case 0xA5: if (VGMHead.lngHzYM2203 & 0x40000000) { Command -= 0x50; ChipID = 0x01; } break; case 0xA6: case 0xA7: if (VGMHead.lngHzYM2608 & 0x40000000) { Command -= 0x50; ChipID = 0x01; } break; case 0xA8: case 0xA9: if (VGMHead.lngHzYM2610 & 0x40000000) { Command -= 0x50; ChipID = 0x01; } break; case 0xAA: if (VGMHead.lngHzYM3812 & 0x40000000) { Command -= 0x50; ChipID = 0x01; } break; case 0xAB: if (VGMHead.lngHzYM3526 & 0x40000000) { Command -= 0x50; ChipID = 0x01; } break; case 0xAC: if (VGMHead.lngHzY8950 & 0x40000000) { Command -= 0x50; ChipID = 0x01; } break; } switch(Command) { case 0x66: // End Of File CmdLen = 0x01; StopVGM = true; break; case 0x62: // 1/60s delay TempSht = 735; VGMSmplPos += TempSht; CmdDelay = TempSht; CmdLen = 0x01; IsDelay = true; break; case 0x63: // 1/50s delay TempSht = 882; VGMSmplPos += TempSht; CmdDelay = TempSht; CmdLen = 0x01; IsDelay = true; break; case 0x61: // xx Sample Delay memcpy(&TempSht, &VGMData[VGMPos + 0x01], 0x02); VGMSmplPos += TempSht; CmdDelay = TempSht; CmdLen = 0x03; IsDelay = true; break; case 0x50: // SN76496 write CmdLen = 0x02; break; case 0x51: // YM2413 write CmdLen = 0x03; break; case 0x52: // YM2612 write port 0 case 0x53: // YM2612 write port 1 CmdLen = 0x03; break; case 0x67: // PCM Data Stream TempByt = VGMData[VGMPos + 0x02]; memcpy(&TempLng, &VGMData[VGMPos + 0x03], 0x04); TempLng &= 0x7FFFFFFF; CmdLen = 0x07 + TempLng; break; case 0xE0: // Seek to PCM Data Bank Pos CmdLen = 0x05; break; case 0x4F: // GG Stereo CmdLen = 0x02; break; case 0x54: // YM2151 write CmdLen = 0x03; break; case 0xC0: // Sega PCM memory write CmdLen = 0x04; break; case 0xB0: // RF5C68 register write CmdLen = 0x03; break; case 0xC1: // RF5C68 memory write CmdLen = 0x04; break; case 0x55: // YM2203 CmdLen = 0x03; break; case 0x56: // YM2608 write port 0 case 0x57: // YM2608 write port 1 CmdLen = 0x03; break; case 0x58: // YM2610 write port 0 case 0x59: // YM2610 write port 1 CmdLen = 0x03; break; case 0x5A: // YM3812 write CmdLen = 0x03; break; case 0x5B: // YM3526 write CmdLen = 0x03; break; case 0x5C: // Y8950 write CmdLen = 0x03; break; case 0x5E: // YMF262 write port 0 case 0x5F: // YMF262 write port 1 CmdLen = 0x03; break; case 0x5D: // YMZ280B write CmdLen = 0x03; break; case 0x68: // PCM RAM write CmdLen = 0x0C; break; case 0x90: // DAC Ctrl: Setup Chip CmdLen = 0x05; break; case 0x91: // DAC Ctrl: Set Data CmdLen = 0x05; break; case 0x92: // DAC Ctrl: Set Freq CmdLen = 0x06; break; case 0x93: // DAC Ctrl: Play from Start Pos CmdLen = 0x0B; break; case 0x94: // DAC Ctrl: Stop immediately CmdLen = 0x02; break; case 0x95: // DAC Ctrl: Play Block (small) CmdLen = 0x05; break; default: switch(Command & 0xF0) { case 0x30: case 0x40: CmdLen = 0x02; break; case 0x50: case 0xA0: case 0xB0: CmdLen = 0x03; break; case 0xC0: case 0xD0: CmdLen = 0x04; break; case 0xE0: case 0xF0: CmdLen = 0x05; break; default: printf("Unknown Command: %X\n", Command); CmdLen = 0x01; //StopVGM = true; break; } break; } } WriteEvent = ! IsDelay; if (VGMHead.lngLoopOffset && VGMPos == VGMHead.lngLoopOffset) { LoopPos = DstPos; WasDelay = true; } if (IsDelay) { if (! DelayQueue && CmdDelay > MAX_SMALL_DELAY) { WriteEvent = true; } else if (CmdDelay <= MAX_SMALL_DELAY && ! WasDelay) { DelayQueue += CmdDelay; DidSomething = true; } else { if (WasDelay) { CmdDelay = 0x00; WriteEvent = true; } CmdDelay += DelayQueue; while(CmdDelay) { if (CmdDelay <= 0xFFFF) TempSht = (UINT16)CmdDelay; else TempSht = 0xFFFF; if (TempSht <= 0x10) { DstData[DstPos] = 0x70 | (TempSht - 0x01); DstPos ++; } else { DstData[DstPos + 0x00] = 0x61; memcpy(&DstData[DstPos + 0x01], &TempSht, 0x02); DstPos += 0x03; } CmdDelay -= TempSht; } DelayQueue = 0x00; } } if (WriteEvent) { memcpy(&DstData[DstPos], &VGMData[VGMPos], CmdLen); DstPos += CmdLen; } VGMPos += CmdLen; if (StopVGM) break; #ifdef WIN32 if (CmdTimer < GetTickCount()) { PrintMinSec(VGMSmplPos, MinSecStr); PrintMinSec(VGMHead.lngTotalSamples, TempStr); TempLng = VGMPos - VGMHead.lngDataOffset; CmdLen = VGMHead.lngEOFOffset - VGMHead.lngDataOffset; printf("%04.3f %% - %s / %s (%08X / %08X) ...\r", (float)TempLng / CmdLen * 100, MinSecStr, TempStr, VGMPos, VGMHead.lngEOFOffset); CmdTimer = GetTickCount() + 200; } #endif } VGMHead.lngTotalSamples = VGMSmplPos - DelayQueue; if (VGMHead.lngLoopOffset && ! LoopPos) printf("Warning! Failed to relocate Loop Point!\n"); WriteVGMHeader(DstData, VGMData, DstPos, LoopPos); return; }
//---------------------------------------------------------------------------------------------- // Pause optimiser //---------------------------------------------------------------------------------------------- BOOL OptimiseVGMPauses(char *filename) { gzFile *in,*out; struct TVGMHeader VGMHeader; char *outfilename,b0,b1,b2; long int OldLoopOffset; int i,PauseLength=0; if (!FileExists(filename)) return FALSE; // Open input file in=gzopen(filename,"rb"); // Read its VGM header if(!ReadVGMHeader(in,&VGMHeader,FALSE)) { gzclose(in); return FALSE; } OldLoopOffset=VGMHeader.LoopOffset+LOOPDELTA; // Make the output filename... outfilename=MakeTempFilename(filename); // ...open it... out=gzopen(outfilename,"wb0"); // ...skip to the data section... gzseek(in,VGM_DATA_OFFSET,SEEK_SET); gzseek(out,VGM_DATA_OFFSET,SEEK_SET); // ...and parse the input file // Go through the file; if it's a pause, add it to the total; if it's data, write the current total and zero it do { // Update loop point // Write any remaining pauyse being buffered first, though if (gztell(in)==OldLoopOffset) { WritePause(out,PauseLength); PauseLength=0; VGMHeader.LoopOffset=gztell(out)-LOOPDELTA; } b0=gzgetc(in); switch (b0) { case VGM_GGST: // GG stereo case VGM_PSG: // PSG write WritePause(out,PauseLength); PauseLength=0; b1=gzgetc(in); gzputc(out,b0); gzputc(out,b1); break; case VGM_YM2413: // YM2413 case VGM_YM2612_0: // YM2612 port 0 case VGM_YM2612_1: // YM2612 port 1 case VGM_YM2151: // YM2151 case 0x55: // Reserved up to 0x5f case 0x56: // All have 2 bytes of data case 0x57: case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f: WritePause(out,PauseLength); PauseLength=0; b1=gzgetc(in); b2=gzgetc(in); gzputc(out,b0); gzputc(out,b1); gzputc(out,b2); break; case VGM_PAUSE_WORD: // Wait n samples b1=gzgetc(in); b2=gzgetc(in); PauseLength+=MAKEWORD(b1,b2); break; case VGM_PAUSE_60TH: // Wait 1/60 s PauseLength+=LEN60TH; break; case VGM_PAUSE_50TH: // Wait 1/50 s PauseLength+=LEN50TH; break; // case VGM_PAUSE_BYTE: // Wait n samples // b1=gzgetc(in); // PauseLength+=b1; // 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: // Wait 1-16 samples PauseLength+=(b0 & 0xf)+1; break; case VGM_END: // End of sound data WritePause(out,PauseLength); PauseLength=0; b0=EOF; // break out of loop break; default: break; } // end switch } while (b0!=EOF); // At end: // 1. Write EOF mrker gzputc(out,VGM_END); // 2. Copy GD3 tag if (VGMHeader.GD3Offset) { struct TGD3Header GD3Header; int NewGD3Offset=gztell(out)-GD3DELTA; gzseek(in,VGMHeader.GD3Offset+GD3DELTA,SEEK_SET); gzread(in,&GD3Header,sizeof(GD3Header)); gzwrite(out,&GD3Header,sizeof(GD3Header)); for (i=0; i<GD3Header.Length; ++i) gzputc(out,gzgetc(in)); VGMHeader.GD3Offset=NewGD3Offset; } // 3. Fill in VGM header VGMHeader.EoFOffset=gztell(out)-EOFDELTA; // LoopOffset updated while optimising gzclose(out); WriteVGMHeader(outfilename,VGMHeader); // Clean up gzclose(in); ReplaceFile(filename,outfilename); free(outfilename); return TRUE; }