Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* jms2mod v0.5 - by 8bitbubsy - 2016-2019
- ** Converts JMS (Josha Munnik's format) v1.0/v1.1 files to ProTracker .MOD */
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <string.h>
- #include <stdbool.h>
- #define SWAP16(value) \
- ( \
- (((uint16_t)((value) & 0x00FF)) << 8) | \
- (((uint16_t)((value) & 0xFF00)) >> 8) \
- )
- static bool handleSamples(FILE *in, FILE *out, uint8_t numSamples);
- static bool handlePatterns(FILE *in, FILE *out, uint8_t numPatterns);
- static bool handleSampleDatas(FILE *in, FILE *out, uint8_t numSamples);
- static struct
- {
- char name[22];
- uint16_t length;
- int8_t finetune;
- uint8_t volume;
- uint16_t repeat, replen;
- } samples[31];
- static const uint16_t periodTable[36] =
- {
- 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
- 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
- 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113
- };
- int main(int argc, char *argv[])
- {
- char *outFilename, songName[20 + 1], JMSID[26], chunkID[4];
- uint8_t numSamples, numPatterns, numOrders, orderList[128];
- bool isVer11;
- FILE *in, *out;
- printf("jms2mod v0.5 - by 8bitbubsy\n");
- printf("===========================\n");
- // check number of arguments passed
- if (argc != 2)
- {
- printf("Usage: jms2mod <song.jms>\n");
- return -1;
- }
- // open input file
- in = fopen(argv[1], "rb");
- if (in == NULL)
- {
- printf("Error: Couldn't open \"%s\" for reading!\n", argv[1]);
- return 1;
- }
- // get output filename
- outFilename = (char *)malloc(strlen(argv[1]) + 4 + 1);
- if (outFilename == NULL)
- {
- fclose(in);
- printf("Error: Out of memory!\n");
- return 1;
- }
- // open output file
- sprintf(outFilename, "%s.mod", argv[1]);
- out = fopen(outFilename, "wb");
- if (out == NULL)
- {
- fclose(in);
- printf("Error: Couldn't open \"%s\" for writing!\n", outFilename);
- return 1;
- }
- free(outFilename);
- // read start header and find out what version this JMS is
- fread(JMSID, 1, 26, in);
- if (!strncmp(JMSID, "JMUSIC file v1.1b, song : ", 26))
- {
- isVer11 = true;
- }
- else if (!strncmp(JMSID, "JMUSIC file v1.0 , song : ", 26))
- {
- isVer11 = false;
- }
- else
- {
- printf("Error: Input file is not a valid JMS!\n");
- goto error;
- }
- // read/write song name
- fread(songName, 1, 20, in);
- fwrite(songName, 1, 20, out);
- if (fgetc(in) != 0x1A) goto corruptJMS; // DOS end character
- songName[20] = '\0';
- // sample chunk ID
- fread(chunkID, 1, 4, in);
- if (strncmp(chunkID, isVer11 ? "0001" : "SAMP", 4) != 0) goto corruptJMS;
- // read number of samples value
- fread(&numSamples, 1, 1, in);
- if (numSamples > 31) goto corruptJMS;
- // read/write sample struct
- if (!handleSamples(in, out, numSamples)) goto error;
- // pattern chunk ID
- fread(chunkID, 1, 4, in);
- if (strncmp(chunkID, isVer11 ? "0010" : "PATT", 4) != 0) goto corruptJMS;
- // read number of patterns value
- fread(&numPatterns, 1, 1, in);
- if (numPatterns == 0 || numPatterns > 64) goto corruptJMS;
- // read number of orders value
- fread(&numOrders, 1, 1, in);
- if (numOrders == 0 || numOrders > 128) goto corruptJMS;
- // read order list
- memset(orderList, 0, 128);
- fread(orderList, 1, numOrders, in);
- fwrite(&numOrders, 1, 1, out); // write number of orders
- fputc(0x7F, out); // restart position (0x7F in PT MODs)
- fwrite(orderList, 1, 128, out); // write order list
- fwrite("M.K.", 1, 4, out); // write MOD ID
- printf("File \"%s\" opened.\n\n", argv[1]);
- printf("Version : %s\n", isVer11 ? "1.1b" : "1.0");
- printf("Name : %s\n", songName);
- printf("Samples : %d\n", numSamples);
- printf("Patterns : %d\n", numPatterns);
- printf("Orders : %d\n", numOrders);
- if (!handlePatterns(in, out, numPatterns)) goto error;
- if (!handleSampleDatas(in, out, numSamples)) goto error;
- printf("\nConversion done.\n");
- fclose(in);
- fclose(out);
- return 0;
- corruptJMS:
- printf("Error: Input JMS file is corrupt!\n");
- error:
- fclose(in);
- fclose(out);
- return 1;
- }
- bool handleSamples(FILE *in, FILE *out, uint8_t numSamples)
- {
- uint8_t i, volume, sampleNum;
- uint16_t length, repeat, replen;
- memset(samples, 0, sizeof (samples));
- for (i = 0; i < 31; i++)
- samples[i].replen = 2;
- // read sample struct
- for (i = 0; i < numSamples; i++)
- {
- fread(&sampleNum, 1, 1, in);
- if (sampleNum == 0 || sampleNum > 32)
- {
- printf("Error: Input JMS file is corrupt!\n");
- return false;
- }
- sampleNum--;
- fread(samples[sampleNum].name, 1, 20, in);
- fread(&length, 2, 1, in);
- fread(&repeat, 2, 1, in);
- fread(&replen, 2, 1, in);
- fread(&volume, 1, 1, in);
- if (replen == 0)
- {
- replen = 2;
- repeat = 0;
- }
- samples[sampleNum].volume = (volume > 64) ? 64 : volume;
- samples[sampleNum].length = SWAP16(length / 2);
- samples[sampleNum].repeat = SWAP16(repeat / 2);
- samples[sampleNum].replen = SWAP16(replen / 2);
- }
- // write sample struct
- for (i = 0; i < 31; i++)
- {
- fwrite(samples[i].name, 1, 22, out);
- fwrite(&samples[i].length, 2, 1, out);
- fwrite(&samples[i].finetune, 1, 1, out);
- fwrite(&samples[i].volume, 1, 1, out);
- fwrite(&samples[i].repeat, 2, 1, out);
- fwrite(&samples[i].replen, 2, 1, out);
- }
- return true;
- }
- void unpackPatt(uint8_t *inPtr, uint8_t *outPtr, uint16_t len)
- {
- uint8_t numZeroBytes, numDataBytes, *tempOutPtr;
- uint8_t note, sample, effect, param;
- uint16_t i, writtenBytes, readBytes, period;
- // unpack JMS pattern (this unpacker is not safe, but "works")
- readBytes = 0;
- writtenBytes = 0;
- tempOutPtr = outPtr;
- while (readBytes < len && writtenBytes < 1024)
- {
- numZeroBytes = *inPtr++;
- numDataBytes = *inPtr++;
- for (i = 0; i < numDataBytes; i++) *tempOutPtr++ = *inPtr++;
- for (i = 0; i < numZeroBytes; i++) *tempOutPtr++ = 0;
- writtenBytes += numDataBytes + numZeroBytes;
- readBytes += numDataBytes + 2;
- }
- // convert to MOD pattern format
- for (i = 0; i < 1024/4; i++)
- {
- // note to period
- note = outPtr[0];
- if (note == 0 || note > 36)
- period = 0;
- else
- period = periodTable[note-1];
- sample = outPtr[1];
- effect = outPtr[2];
- param = outPtr[3];
- // remove F00 (doesn't stop song in NoiseTracker)
- if (effect == 0x0F && param == 0x00)
- effect = 0;
- // write MOD pattern bytes
- *outPtr++ = (sample & 0xF0) | ((period >> 8) & 0x0F);
- *outPtr++ = (period & 0xFF);
- *outPtr++ = ((sample << 4) & 0xF0) | (effect & 0x0F);
- *outPtr++ = param;
- }
- }
- bool handlePatterns(FILE *in, FILE *out, uint8_t numPatterns)
- {
- uint8_t *packedPatt, *unpackedPatt;
- uint16_t packedPattLen;
- for (uint8_t i = 0; i < numPatterns; i++)
- {
- fseek(in, 4, SEEK_CUR); // skip chunk ID
- fread(&packedPattLen, 2, 1, in);
- if (packedPattLen == 0 || packedPattLen >= 1044)
- {
- printf("Error: Input JMS file is corrupt!\n");
- return false;
- }
- packedPatt = (uint8_t *)malloc(packedPattLen);
- if (packedPatt == NULL)
- {
- printf("Error: Out of memory!\n");
- return false;
- }
- unpackedPatt = (uint8_t *)malloc((4 * 64 * 4) + 20); // +20 is needed. The JMS pattern packer is buggy!
- if (unpackedPatt == NULL)
- {
- free(packedPatt);
- printf("Error: Out of memory!\n");
- return false;
- }
- fread(packedPatt, 1, packedPattLen, in);
- unpackPatt(packedPatt, unpackedPatt, packedPattLen);
- fwrite(unpackedPatt, 1, 4 * 64 * 4, out);
- free(packedPatt);
- free(unpackedPatt);
- }
- return true;
- }
- bool handleSampleDatas(FILE *in, FILE *out, uint8_t numSamples)
- {
- int8_t *sampleData;
- uint16_t sampleLength;
- for (uint8_t i = 0; i < numSamples; i++)
- {
- fseek(in, 4, SEEK_CUR); // skip chunk ID
- fread(&sampleLength, 2, 1, in);
- sampleData = (int8_t *)malloc(sampleLength);
- if (sampleData == NULL)
- {
- printf("Error: Out of memory!\n");
- return false;
- }
- fread(sampleData, 1, sampleLength, in);
- fwrite(sampleData, 1, sampleLength, out);
- free(sampleData);
- }
- return true;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement