SHARE
TWEET

jms2mod v0.5

8bitbubsy Jul 30th, 2016 (edited) 377 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* jms2mod v0.5 - by 8bitbubsy - 2016-2019
  2. ** Converts JMS (Josha Munnik's format) v1.0/v1.1 files to ProTracker .MOD */
  3.  
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <stdint.h>
  7. #include <string.h>
  8. #include <stdbool.h>
  9.  
  10. #define SWAP16(value) \
  11. ( \
  12.     (((uint16_t)((value) & 0x00FF)) << 8) | \
  13.     (((uint16_t)((value) & 0xFF00)) >> 8)   \
  14. )
  15.  
  16. static bool handleSamples(FILE *in, FILE *out, uint8_t numSamples);
  17. static bool handlePatterns(FILE *in, FILE *out, uint8_t numPatterns);
  18. static bool handleSampleDatas(FILE *in, FILE *out, uint8_t numSamples);
  19.  
  20. static struct
  21. {
  22.     char name[22];
  23.     uint16_t length;
  24.     int8_t finetune;
  25.     uint8_t volume;
  26.     uint16_t repeat, replen;
  27. } samples[31];
  28.  
  29. static const uint16_t periodTable[36] =
  30. {
  31.     856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
  32.     428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
  33.     214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113
  34. };
  35.  
  36. int main(int argc, char *argv[])
  37. {
  38.     char *outFilename, songName[20 + 1], JMSID[26], chunkID[4];
  39.     uint8_t numSamples, numPatterns, numOrders, orderList[128];
  40.     bool isVer11;
  41.     FILE *in, *out;
  42.  
  43.     printf("jms2mod v0.5 - by 8bitbubsy\n");
  44.     printf("===========================\n");
  45.  
  46.     // check number of arguments passed
  47.     if (argc != 2)
  48.     {
  49.         printf("Usage: jms2mod <song.jms>\n");
  50.         return -1;
  51.     }
  52.  
  53.     // open input file
  54.     in = fopen(argv[1], "rb");
  55.     if (in == NULL)
  56.     {
  57.         printf("Error: Couldn't open \"%s\" for reading!\n", argv[1]);
  58.         return 1;
  59.     }
  60.  
  61.     // get output filename
  62.     outFilename = (char *)malloc(strlen(argv[1]) + 4 + 1);
  63.     if (outFilename == NULL)
  64.     {
  65.         fclose(in);
  66.         printf("Error: Out of memory!\n");
  67.         return 1;
  68.     }
  69.  
  70.     // open output file
  71.     sprintf(outFilename, "%s.mod", argv[1]);
  72.     out = fopen(outFilename, "wb");
  73.     if (out == NULL)
  74.     {
  75.         fclose(in);
  76.         printf("Error: Couldn't open \"%s\" for writing!\n", outFilename);
  77.         return 1;
  78.     }
  79.     free(outFilename);
  80.  
  81.     // read start header and find out what version this JMS is
  82.     fread(JMSID, 1, 26, in);
  83.     if (!strncmp(JMSID, "JMUSIC file v1.1b, song : ", 26))
  84.     {
  85.         isVer11 = true;
  86.     }
  87.     else if (!strncmp(JMSID, "JMUSIC file v1.0 , song : ", 26))
  88.     {
  89.         isVer11 = false;
  90.     }
  91.     else
  92.     {
  93.         printf("Error: Input file is not a valid JMS!\n");
  94.         goto error;
  95.     }
  96.  
  97.     // read/write song name
  98.     fread(songName,  1, 20, in);
  99.     fwrite(songName, 1, 20, out);
  100.     if (fgetc(in) != 0x1A) goto corruptJMS; // DOS end character
  101.     songName[20] = '\0';
  102.  
  103.     // sample chunk ID
  104.     fread(chunkID, 1, 4, in);
  105.     if (strncmp(chunkID, isVer11 ? "0001" : "SAMP", 4) != 0) goto corruptJMS;
  106.  
  107.     // read number of samples value
  108.     fread(&numSamples, 1, 1, in);
  109.     if (numSamples > 31) goto corruptJMS;
  110.  
  111.     // read/write sample struct
  112.     if (!handleSamples(in, out, numSamples)) goto error;
  113.  
  114.     // pattern chunk ID
  115.     fread(chunkID, 1, 4, in);
  116.     if (strncmp(chunkID, isVer11 ? "0010" : "PATT", 4) != 0) goto corruptJMS;
  117.  
  118.     // read number of patterns value
  119.     fread(&numPatterns, 1, 1, in);
  120.     if (numPatterns == 0 || numPatterns > 64) goto corruptJMS;
  121.  
  122.     // read number of orders value
  123.     fread(&numOrders, 1, 1, in);
  124.     if (numOrders == 0 || numOrders > 128) goto corruptJMS;
  125.  
  126.     // read order list
  127.     memset(orderList, 0, 128);
  128.     fread(orderList, 1, numOrders, in);
  129.  
  130.     fwrite(&numOrders, 1, 1, out); // write number of orders
  131.     fputc(0x7F, out); // restart position (0x7F in PT MODs)
  132.     fwrite(orderList, 1, 128, out); // write order list
  133.     fwrite("M.K.", 1, 4, out); // write MOD ID
  134.  
  135.     printf("File \"%s\" opened.\n\n", argv[1]);
  136.     printf("Version  : %s\n", isVer11 ? "1.1b" : "1.0");
  137.     printf("Name     : %s\n", songName);
  138.     printf("Samples  : %d\n", numSamples);
  139.     printf("Patterns : %d\n", numPatterns);
  140.     printf("Orders   : %d\n", numOrders);
  141.  
  142.     if (!handlePatterns(in, out, numPatterns)) goto error;
  143.     if (!handleSampleDatas(in, out, numSamples)) goto error;
  144.  
  145.     printf("\nConversion done.\n");
  146.  
  147.     fclose(in);
  148.     fclose(out);
  149.  
  150.     return 0;
  151.  
  152. corruptJMS:
  153.     printf("Error: Input JMS file is corrupt!\n");
  154. error:
  155.     fclose(in);
  156.     fclose(out);
  157.  
  158.     return 1;
  159. }
  160.  
  161. bool handleSamples(FILE *in, FILE *out, uint8_t numSamples)
  162. {
  163.     uint8_t i, volume, sampleNum;
  164.     uint16_t length, repeat, replen;
  165.  
  166.     memset(samples, 0, sizeof (samples));
  167.     for (i = 0; i < 31; i++)
  168.         samples[i].replen = 2;
  169.  
  170.     // read sample struct
  171.     for (i = 0; i < numSamples; i++)
  172.     {
  173.         fread(&sampleNum, 1, 1, in);
  174.         if (sampleNum == 0 || sampleNum > 32)
  175.         {
  176.             printf("Error: Input JMS file is corrupt!\n");
  177.             return false;
  178.         }
  179.         sampleNum--;
  180.  
  181.         fread(samples[sampleNum].name, 1, 20, in);
  182.  
  183.         fread(&length, 2, 1, in);
  184.         fread(&repeat, 2, 1, in);
  185.         fread(&replen, 2, 1, in);
  186.         fread(&volume, 1, 1, in);
  187.  
  188.         if (replen == 0)
  189.         {
  190.             replen = 2;
  191.             repeat = 0;
  192.         }
  193.  
  194.         samples[sampleNum].volume = (volume > 64) ? 64 : volume;
  195.         samples[sampleNum].length = SWAP16(length / 2);
  196.         samples[sampleNum].repeat = SWAP16(repeat / 2);
  197.         samples[sampleNum].replen = SWAP16(replen / 2);
  198.     }
  199.  
  200.     // write sample struct
  201.     for (i = 0; i < 31; i++)
  202.     {
  203.         fwrite(samples[i].name, 1, 22, out);
  204.         fwrite(&samples[i].length, 2, 1, out);
  205.         fwrite(&samples[i].finetune, 1, 1, out);
  206.         fwrite(&samples[i].volume, 1, 1, out);
  207.         fwrite(&samples[i].repeat, 2, 1, out);
  208.         fwrite(&samples[i].replen, 2, 1, out);
  209.     }
  210.  
  211.     return true;
  212. }
  213.  
  214. void unpackPatt(uint8_t *inPtr, uint8_t *outPtr, uint16_t len)
  215. {
  216.     uint8_t numZeroBytes, numDataBytes, *tempOutPtr;
  217.     uint8_t note, sample, effect, param;
  218.     uint16_t i, writtenBytes, readBytes, period;
  219.  
  220.     // unpack JMS pattern (this unpacker is not safe, but "works")
  221.  
  222.     readBytes = 0;
  223.     writtenBytes = 0;
  224.  
  225.     tempOutPtr = outPtr;
  226.     while (readBytes < len && writtenBytes < 1024)
  227.     {
  228.         numZeroBytes = *inPtr++;
  229.         numDataBytes = *inPtr++;
  230.  
  231.         for (i = 0; i < numDataBytes; i++) *tempOutPtr++ = *inPtr++;
  232.         for (i = 0; i < numZeroBytes; i++) *tempOutPtr++ = 0;
  233.  
  234.         writtenBytes += numDataBytes + numZeroBytes;
  235.         readBytes += numDataBytes + 2;
  236.     }
  237.  
  238.     // convert to MOD pattern format
  239.     for (i = 0; i < 1024/4; i++)
  240.     {
  241.         // note to period
  242.         note = outPtr[0];
  243.         if (note == 0 || note > 36)
  244.             period = 0;
  245.         else
  246.             period = periodTable[note-1];
  247.  
  248.         sample = outPtr[1];
  249.         effect = outPtr[2];
  250.         param = outPtr[3];
  251.  
  252.         // remove F00 (doesn't stop song in NoiseTracker)
  253.         if (effect == 0x0F && param == 0x00)
  254.             effect = 0;
  255.  
  256.         // write MOD pattern bytes
  257.        *outPtr++ = (sample & 0xF0) | ((period >> 8) & 0x0F);
  258.        *outPtr++ = (period & 0xFF);
  259.        *outPtr++ = ((sample << 4) & 0xF0) | (effect & 0x0F);
  260.        *outPtr++ = param;
  261.     }
  262. }
  263.  
  264. bool handlePatterns(FILE *in, FILE *out, uint8_t numPatterns)
  265. {
  266.     uint8_t *packedPatt, *unpackedPatt;
  267.     uint16_t packedPattLen;
  268.  
  269.     for (uint8_t i = 0; i < numPatterns; i++)
  270.     {
  271.         fseek(in, 4, SEEK_CUR); // skip chunk ID
  272.         fread(&packedPattLen, 2, 1, in);
  273.  
  274.         if (packedPattLen == 0 || packedPattLen >= 1044)
  275.         {
  276.             printf("Error: Input JMS file is corrupt!\n");
  277.             return false;
  278.         }
  279.  
  280.         packedPatt = (uint8_t *)malloc(packedPattLen);
  281.         if (packedPatt == NULL)
  282.         {
  283.             printf("Error: Out of memory!\n");
  284.             return false;
  285.         }
  286.  
  287.         unpackedPatt = (uint8_t *)malloc((4 * 64 * 4) + 20); // +20 is needed. The JMS pattern packer is buggy!
  288.         if (unpackedPatt == NULL)
  289.         {
  290.             free(packedPatt);
  291.             printf("Error: Out of memory!\n");
  292.             return false;
  293.         }
  294.  
  295.         fread(packedPatt, 1, packedPattLen, in);
  296.         unpackPatt(packedPatt, unpackedPatt, packedPattLen);
  297.         fwrite(unpackedPatt, 1, 4 * 64 * 4, out);
  298.  
  299.         free(packedPatt);
  300.         free(unpackedPatt);
  301.     }
  302.  
  303.     return true;
  304. }
  305.  
  306. bool handleSampleDatas(FILE *in, FILE *out, uint8_t numSamples)
  307. {
  308.     int8_t *sampleData;
  309.     uint16_t sampleLength;
  310.  
  311.     for (uint8_t i = 0; i < numSamples; i++)
  312.     {
  313.         fseek(in, 4, SEEK_CUR); // skip chunk ID
  314.         fread(&sampleLength, 2, 1, in);
  315.  
  316.         sampleData = (int8_t *)malloc(sampleLength);
  317.         if (sampleData == NULL)
  318.         {
  319.             printf("Error: Out of memory!\n");
  320.             return false;
  321.         }
  322.  
  323.         fread(sampleData, 1, sampleLength, in);
  324.         fwrite(sampleData, 1, sampleLength, out);
  325.         free(sampleData);
  326.     }
  327.  
  328.     return true;
  329. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top