XGM* XGM_createFromData(unsigned char* data, int dataSize) { int s; XGM* result = XGM_create(); if (!silent) printf("Parsing XGM file...\n"); if (strncasecmp(&data[0x00], "XGM ", 4)) { printf("Error: XGM file not recognized !\n"); return NULL; } // sample id table LList* samples = NULL; for (s = 1; s < 0x40; s++) { int offset = getInt16(data, (s * 4) + 0); int len = getInt16(data, (s * 4) + 2); // ignore empty sample if ((offset != 0xFFFF) && (len != 0x0100)) { offset <<= 8; len <<= 8; // add sample samples = insertAfterLList(samples, XGMSample_create(s, data + (offset + 0x104), len, offset)); } } result->samples = getHeadLList(samples); // calculate music data offset (sample block size + 0x104) int offset = (getInt16(data, 0x100) << 8) + 0x104; // int version = data[0x102]; result->pal = data[0x103] & 1; // get music data length int len = getInt(data, offset); if (verbose) { printf("XGM sample number: %d\n", getSizeLList(result->samples)); printf("XGM start music data: %6X len: %d\n", offset + 4, len); } // build command list XGM_parseMusic(result, data + offset + 4, len); if (!silent) printf("XGM duration: %d frames (%d seconds)\n", XGM_computeLenInFrame(result), XGM_computeLenInSecond(result)); // GD3 tags ? if (data[0x103] & 2) result->gd3 = GD3_createFromData(data + offset + 4 + len); return result; }
static void XGM_parseMusic(XGM* xgm, unsigned char* data, int length) { // build command list int off; // parse all XGM commands off = 0; LList* commands = xgm->commands; while (off < length) { // check for loop start XGMCommand* command = XGMCommand_createFromData(data + off); commands = insertAfterLList(commands, command); off += command->size; // stop here if (XGMCommand_isEnd(command)) break; } xgm->commands = getHeadLList(commands); if (!silent) printf("Number of command: %d\n", getSizeLList(xgm->commands)); }
XGM* XGC_create(XGM* xgm) { XGM* result = XGM_create(); LList* s; LList* d; if (!silent) printf("Converting to XGC...\n"); // copy pal/ntsc information result->pal = xgm->pal; // simple copy for sample s = xgm->samples; d = result->samples; while(s != NULL) { XGMSample* sample = s->element; d = insertAfterLList(d, sample); s = s->next; } result->samples = getHeadLList(d); // and extract music data XGC_extractMusic(result, xgm); // shift all samples to 2 frames for PAL and 3 frames ahead for NTSC (because of PCM buffer length) if (result->pal) XGC_shiftSamples(result, 2); else XGC_shiftSamples(result, 3); // display play PCM command // if (verbose) // { // LList* curCom = result->commands; // while(curCom != NULL) // { // XGMCommand* command = curCom->element; // // if (XGCCommand_isPCM(command)) // printf("play sample %2X at frame %d\n", XGCCommand_getPCMId(command), XGC_getTimeInFrame(result, command)); // // curCom = curCom->next; // } // } if (verbose) { printf("Sample size: %d\n", XGM_getSampleDataSize(result)); printf("Music data size: %d\n", XGM_getMusicDataSize(result)); printf("Number of sample: %d\n", getSizeLList(result->samples)); } if (!silent) printf("XGC duration: %d frames (%d seconds)\n", XGC_computeLenInFrame(result), XGC_computeLenInSecond(result)); return result; }
LList* XGMCommand_createPSGCommands(LList* commands) { LList* result; LList* src; result = NULL; src = commands; while (src != NULL) result = insertAfterLList(result, XGMCommand_createPSGCommand(&src)); return getHeadLList(result); }
static void XGM_extractSamples(XGM* xgm, VGM* vgm) { int index; LList* sampleXgm; // index should be equal to current size + 1 index = getSizeLList(xgm->samples) + 1; sampleXgm = getTailLList(xgm->samples); // extract samples LList* b = vgm->sampleBanks; while(b != NULL) { SampleBank* sampleBank = b->element; LList* s = sampleBank->samples; // can't have more than 64 samples in XGM music while((s != NULL) && (index < 64)) { XGMSample* sample = XGMSample_createFromVGMSample(sampleBank, s->element); // valid sample if (sample != NULL) { sample->index = index++; sampleXgm = insertAfterLList(sampleXgm, sample); } s = s->next; } // can't extract all samples (XGM music doesn't not support more than 63 samples) if ((s != NULL) && (index >= 64)) { if (!silent) { printf("Error: XGM does not support music with more than 63 samples !\n"); printf("Input VGM file probably has improper PCM data extraction, try to use another VGM source.\n"); } // interrupt sample extraction break; } b = b->next; } xgm->samples = getHeadLList(sampleXgm); }
LList* XGMCommand_createPCMCommands(XGM* xgm, VGM* vgm, LList* commands) { LList* result; LList* src; result = NULL; src = commands; while(src != NULL) { VGMCommand* command = src->element; if (VGMCommand_isStreamStartLong(command) || VGMCommand_isStreamStart(command) || VGMCommand_isStreamStop(command)) result = insertAfterLList(result, XGMCommand_createPCMCommand(xgm, vgm, command, -1)); src = src->next; } return getHeadLList(result); }
static void XGM_parseMusicFromXGC(XGM* xgm, unsigned char* data, int length) { // build command list int off; // parse all XGM commands off = 0; LList* commands = xgm->commands; while (off < length) { // get frame size int size = data[off++] - 1; while(size > 0) { XGMCommand* command = XGCCommand_createFromData(data + off); // add command if not state or frame skip command if (!XGCCommand_isState(command) && !XGCCommand_isFrameSkip(command)) commands = insertAfterLList(commands, command); off += command->size; size -= command->size; } // add frame end command commands = insertAfterLList(commands, XGMCommand_createFrameCommand()); // stop here // if (XGMCommand_isEnd(command)) // break; } xgm->commands = getHeadLList(commands); if (!silent) printf("Number of command: %d\n", getSizeLList(xgm->commands)); }
static void XGC_extractMusic(XGM* xgc, XGM* xgm) { LList* vgmCommands; LList* frameCommands = NULL; LList* ymCommands = NULL; LList* ymKeyCommands = NULL; LList* psgCommands = NULL; LList* otherCommands = NULL; LList* newCommands = NULL; LList* stateChange = NULL; LList* xgcCommands; LList* com; LList* tmpCom; XGMCommand* sizeCommand; YM2612* ymOldState; YM2612* ymState; int j, size; ymOldState = YM2612_create(); ymState = YM2612_create(); // reset frame xgcCommands = createElement(XGCCommand_createFrameSizeCommand(0)); // TL / D1L / RR set to max vgmCommands = NULL; vgmCommands = insertAllAfterLList(vgmCommands, VGMCommand_createYMCommands(0, 0x40, 0x7F)); vgmCommands = insertAllAfterLList(vgmCommands, VGMCommand_createYMCommands(0, 0x80, 0xFF)); vgmCommands = getHeadLList(vgmCommands); xgcCommands = insertAllAfterLList(xgcCommands, XGCCommand_convert(XGMCommand_createYMPort0Commands(vgmCommands))); // set ym state com = vgmCommands; while(com != NULL) { VGMCommand* command = com->element; YM2612_set(ymState, VGMCommand_getYM2612Port(command), VGMCommand_getYM2612Register(command), VGMCommand_getYM2612Value(command)); com = com->next; } deleteLList(vgmCommands); vgmCommands = NULL; vgmCommands = insertAllAfterLList(vgmCommands, VGMCommand_createYMCommands(1, 0x40, 0x7F)); vgmCommands = insertAllAfterLList(vgmCommands, VGMCommand_createYMCommands(1, 0x80, 0xFF)); vgmCommands = getHeadLList(vgmCommands); xgcCommands = insertAllAfterLList(xgcCommands, XGCCommand_convert(XGMCommand_createYMPort1Commands(vgmCommands))); // set ym state com = vgmCommands; while(com != NULL) { VGMCommand* command = com->element; YM2612_set(ymState, VGMCommand_getYM2612Port(command), VGMCommand_getYM2612Register(command), VGMCommand_getYM2612Value(command)); com = com->next; } // key off for all channels deleteLList(vgmCommands); vgmCommands = NULL; vgmCommands = insertAfterLList(vgmCommands, VGMCommand_createYMCommand(0, 0x28, 0x00)); vgmCommands = insertAfterLList(vgmCommands, VGMCommand_createYMCommand(0, 0x28, 0x01)); vgmCommands = insertAfterLList(vgmCommands, VGMCommand_createYMCommand(0, 0x28, 0x02)); vgmCommands = insertAfterLList(vgmCommands, VGMCommand_createYMCommand(0, 0x28, 0x04)); vgmCommands = insertAfterLList(vgmCommands, VGMCommand_createYMCommand(0, 0x28, 0x05)); vgmCommands = insertAfterLList(vgmCommands, VGMCommand_createYMCommand(0, 0x28, 0x06)); vgmCommands = getHeadLList(vgmCommands); xgcCommands = insertAllAfterLList(xgcCommands, XGCCommand_convert(XGMCommand_createYMKeyCommands(vgmCommands))); // set ym state com = vgmCommands; while(com != NULL) { VGMCommand* command = com->element; YM2612_set(ymState, VGMCommand_getYM2612Port(command), VGMCommand_getYM2612Register(command), VGMCommand_getYM2612Value(command)); com = com->next; } stateChange = XGC_getStateChange(ymState, ymOldState); // add the state commands if no empty if (stateChange != NULL) xgcCommands = insertAllAfterLList(xgcCommands, XGCCommand_createStateCommands(stateChange)); // add 3 dummy frames (reserve frame space for PCM shift) xgcCommands = insertAfterLList(xgcCommands, XGCCommand_createFrameSizeCommand(0)); xgcCommands = insertAfterLList(xgcCommands, XGCCommand_createFrameSizeCommand(0)); xgcCommands = insertAfterLList(xgcCommands, XGCCommand_createFrameSizeCommand(0)); XGMCommand* loopCommand = XGM_getLoopPointedCommand(xgm); int loopOffset = -1; com = xgm->commands; while(com != NULL) { // build frame commands deleteLList(frameCommands); frameCommands = NULL; while(com != NULL) { // get command and pass to next one XGMCommand* command = com->element; com = com->next; // this is the command where we loop if (command == loopCommand) { if (loopOffset == -1) loopOffset = XGM_getMusicDataSizeOf(getHeadLList(xgcCommands)); } // loop information --> ignore if (XGMCommand_isLoop(command) || XGMCommand_isEnd(command)) continue; // stop here if (XGMCommand_isFrame(command)) break; // add command frameCommands = insertAfterLList(frameCommands, command); } // get back to head frameCommands = getHeadLList(frameCommands); // update state ymOldState = ymState; ymState = YM2612_copy(ymOldState); // prepare new commands for this frame deleteLList(newCommands); // add size command sizeCommand = XGCCommand_createFrameSizeCommand(0); newCommands = createElement(sizeCommand); // group commands deleteLList(ymCommands); deleteLList(ymKeyCommands); deleteLList(psgCommands); deleteLList(otherCommands); ymCommands = NULL; ymKeyCommands = NULL; psgCommands = NULL; otherCommands = NULL; tmpCom = frameCommands; while(tmpCom != NULL) { XGMCommand* command = tmpCom->element; if (XGMCommand_isPSGWrite(command)) psgCommands = insertAfterLList(psgCommands, command); else if (XGMCommand_isYM2612RegKeyWrite(command)) ymKeyCommands = insertAfterLList(ymKeyCommands, command); else if (XGMCommand_isYM2612Write(command)) { // update YM state for (j = 0; j < XGMCommand_getYM2612WriteCount(command); j++) { if (XGMCommand_isYM2612Port0Write(command)) YM2612_set(ymState, 0, command->data[(j * 2) + 1] & 0xFF, command->data[(j * 2) + 2] & 0xFF); else YM2612_set(ymState, 1, command->data[(j * 2) + 1] & 0xFF, command->data[(j * 2) + 2] & 0xFF); } // remove all $2B register writes (DAC enable is done automatically) if (XGMCommand_removeYM2612RegWrite(command, 0, 0x2B)) ymCommands = insertAfterLList(ymCommands, command); } else otherCommands = insertAfterLList(otherCommands, command); tmpCom = tmpCom->next; } XGMCommand* pcmComCH[4]; pcmComCH[0] = NULL; pcmComCH[1] = NULL; pcmComCH[2] = NULL; pcmComCH[3] = NULL; // discard multi PCM command in a single frame tmpCom = otherCommands; while(tmpCom != NULL) { XGMCommand* command = tmpCom->element; if (XGMCommand_isPCM(command)) { // get channel int ch = XGMCommand_getPCMChannel(command); // already have a PCM command for this channel ? if (pcmComCH[ch] != NULL) { // remove current PCM command removeFromLList(tmpCom); // we removed the last command --> update pointer if (tmpCom == otherCommands) otherCommands = tmpCom->prev; if (!silent) { int frameInd = XGC_computeLenInFrameOf(getHeadLList(xgcCommands)); int id = XGMCommand_getPCMId(command); // we are ignoring a real play command --> display it if (id != 0) printf("Warning: multiple PCM command on %d --> play %2X removed\n", frameInd, id); } } else pcmComCH[ch] = command; } tmpCom = tmpCom->prev; } psgCommands = getHeadLList(psgCommands); ymCommands = getHeadLList(ymCommands); ymKeyCommands = getHeadLList(ymKeyCommands); otherCommands = getHeadLList(otherCommands); // PSG commands first as PSG require main BUS access (DMA contention) if (psgCommands != NULL) newCommands = insertAllAfterLList(newCommands, XGCCommand_convert(psgCommands)); // then general YM commands if (ymCommands != NULL) newCommands = insertAllAfterLList(newCommands, XGCCommand_convert(ymCommands)); // then key commands if (ymKeyCommands != NULL) newCommands = insertAllAfterLList(newCommands, XGCCommand_convert(ymKeyCommands)); // and finally others commands if (otherCommands != NULL) newCommands = insertAllAfterLList(newCommands, XGCCommand_convert(otherCommands)); // state change stateChange = XGC_getStateChange(ymState, ymOldState); // add the state command if no empty if (stateChange != NULL) newCommands = insertAllAfterLList(newCommands, XGCCommand_createStateCommands(stateChange)); // is it the last frame ? if (com == NULL) { // loop point ? if (loopOffset != -1) newCommands = insertAfterLList(newCommands, XGMCommand_createLoopCommand(loopOffset)); else // add end command newCommands = insertAfterLList(newCommands, XGMCommand_createEndCommand()); } // get back to head newCommands = getHeadLList(newCommands); // limit frame commands to 255 bytes max size = 0; tmpCom = newCommands; while(tmpCom != NULL) { XGMCommand* command = tmpCom->element; // limit reached ? if ((size + command->size) >= 256) { // if ((frameInd > 10) && (!silent)) if (!silent) { int frameInd = XGC_computeLenInFrameOf(getHeadLList(xgcCommands)) + (XGC_computeLenInFrameOf(newCommands) - 1); printf("Warning: frame > 256 at frame %4X (need to split frame)\n", frameInd); } // end previous frame XGCCommand_setFrameSizeSize(sizeCommand, size); // insert new frame size info sizeCommand = XGCCommand_createFrameSizeCommand(0); insertBeforeLList(tmpCom, sizeCommand); // reset size and pass to next element size = 1; } size += command->size; tmpCom = tmpCom->next; } // set frame size XGCCommand_setFrameSizeSize(sizeCommand, size); // finally add the new commands xgcCommands = insertAllAfterLList(xgcCommands, newCommands); } xgc->commands = getHeadLList(xgcCommands); // compute offset & frame size XGC_computeAllOffset(xgc); XGC_computeAllFrameSize(xgc); if (!silent) printf("Number of command: %d\n", getSizeLList(xgc->commands)); }
static void XGM_extractMusic(XGM* xgm, VGM* vgm) { LList* frameCommands = NULL; LList* ymKeyCommands = NULL; LList* ymPort0Commands = NULL; LList* ymPort1Commands = NULL; LList* psgCommands = NULL; LList* sampleCommands = NULL; LList* xgmCommands = NULL; int loopOffset = -1; int frame = 0; bool loopEnd; bool hasKeyCom; LList* vgmCom = vgm->commands; while (vgmCom != NULL) { // get frame commands deleteLList(frameCommands); frameCommands = NULL; loopEnd = false; while (vgmCom != NULL) { VGMCommand* command = vgmCom->element; vgmCom = vgmCom->next; // ignore data block if (VGMCommand_isDataBlock(command)) continue; // save loop start if (VGMCommand_isLoopStart(command)) { if (loopOffset == -1) loopOffset = XGM_getMusicDataSizeOf(getHeadLList(xgm->commands)); continue; } // save loop end if (VGMCommand_isLoopEnd(command)) { loopEnd = true; continue; } // stop here if (VGMCommand_isWait(command)) { // set PAL flag if not already set if (xgm->pal == -1) { if (VGMCommand_isWaitPAL(command)) xgm->pal = 1; else if (VGMCommand_isWaitNTSC(command)) xgm->pal = 0; } break; } if (VGMCommand_isEnd(command)) break; // add command frameCommands = insertAfterLList(frameCommands, command); } // prepare new commands for this frame deleteLList(xgmCommands); xgmCommands = NULL; // group commands deleteLList(ymKeyCommands); deleteLList(ymPort0Commands); deleteLList(ymPort1Commands); deleteLList(psgCommands); deleteLList(sampleCommands); ymKeyCommands = NULL; ymPort0Commands = NULL; ymPort1Commands = NULL; psgCommands = NULL; sampleCommands = NULL; hasKeyCom = false; LList* com = getHeadLList(frameCommands); while(com != NULL) { VGMCommand* command = com->element; if (VGMCommand_isStream(command)) sampleCommands = insertAfterLList(sampleCommands, command); else if (VGMCommand_isPSGWrite(command)) psgCommands = insertAfterLList(psgCommands, command); else if (VGMCommand_isYM2612KeyWrite(command)) { // keep all key commands ymKeyCommands = insertAfterLList(ymKeyCommands, command); hasKeyCom = true; } else if (VGMCommand_isYM2612Write(command)) { // need accurate order of key event / register write so we transfer commands now if (hasKeyCom) { ymPort0Commands = getHeadLList(ymPort0Commands); ymPort1Commands = getHeadLList(ymPort1Commands); ymKeyCommands = getHeadLList(ymKeyCommands); // general YM commands first as key event were just done if (ymPort0Commands != NULL) xgmCommands = insertAllAfterLList(xgmCommands, XGMCommand_createYMPort0Commands(ymPort0Commands)); if (ymPort1Commands != NULL) xgmCommands = insertAllAfterLList(xgmCommands, XGMCommand_createYMPort1Commands(ymPort1Commands)); // then key commands if (ymKeyCommands != NULL) xgmCommands = insertAllAfterLList(xgmCommands, XGMCommand_createYMKeyCommands(ymKeyCommands)); deleteLList(ymPort0Commands); deleteLList(ymPort1Commands); deleteLList(ymKeyCommands); ymPort0Commands = NULL; ymPort1Commands = NULL; ymKeyCommands = NULL; hasKeyCom = false; } if (VGMCommand_isYM2612Port0Write(command)) ymPort0Commands = insertAfterLList(ymPort0Commands, command); else ymPort1Commands = insertAfterLList(ymPort1Commands, command); } else { if (verbose) printf("Command %d ignored at frame %d\n", command->command, frame); } com = com->next; } ymPort0Commands = getHeadLList(ymPort0Commands); ymPort1Commands = getHeadLList(ymPort1Commands); ymKeyCommands = getHeadLList(ymKeyCommands); psgCommands = getHeadLList(psgCommands); sampleCommands = getHeadLList(sampleCommands); // general YM commands first as key event were just done if (ymPort0Commands != NULL) xgmCommands = insertAllAfterLList(xgmCommands, XGMCommand_createYMPort0Commands(ymPort0Commands)); if (ymPort1Commands != NULL) xgmCommands = insertAllAfterLList(xgmCommands, XGMCommand_createYMPort1Commands(ymPort1Commands)); // then key commands if (ymKeyCommands != NULL) xgmCommands = insertAllAfterLList(xgmCommands, XGMCommand_createYMKeyCommands(ymKeyCommands)); // then PSG commands if (psgCommands != NULL) xgmCommands = insertAllAfterLList(xgmCommands, XGMCommand_createPSGCommands(psgCommands)); // and finally PCM commands if (sampleCommands != NULL) xgmCommands = insertAllAfterLList(xgmCommands, XGMCommand_createPCMCommands(xgm, vgm, sampleCommands)); // loop point ? if (loopEnd) { if (loopOffset != -1) { xgmCommands = insertAfterLList(xgmCommands, XGMCommand_createLoopCommand(loopOffset)); loopOffset = -1; } } // last frame ? if (vgmCom == NULL) { // loop point not yet defined (should not arrive) ? if (loopOffset != -1) { loopEnd = true; xgmCommands = insertAfterLList(xgmCommands, XGMCommand_createLoopCommand(loopOffset)); loopOffset = -1; } // add end command xgmCommands = insertAfterLList(xgmCommands, XGMCommand_createEndCommand()); } // end frame else xgmCommands = insertAfterLList(xgmCommands, XGMCommand_createFrameCommand()); // get back to head of frame commands xgmCommands = getHeadLList(xgmCommands); int numCom = getSizeLList(xgmCommands); // heavy frame warning, probably something wrong here... if (numCom > 200) { // show warning if (!silent) printf("Warning: Heavy frame at position %d (%d commands), playback may be altered !\n", frame, numCom); // verbose enable --> log frame command into a file // if (verbose) // { // char filename[32]; // // sprintf(filename, "frame_%.4X.log", frame); // XGMCommand_logCommands(filename, xgmCommands); // } } // finally add the new commands xgm->commands = insertAllAfterLList(xgm->commands, xgmCommands); // next frame frame++; } // get back to head xgm->commands = getHeadLList(xgm->commands); // recompute all offset XGM_computeAllOffset(xgm); if (!silent) printf("Number of command: %d\n", getSizeLList(xgm->commands)); }