G2A Many GEOs
SHARE
TWEET

jms2mod v0.5

8bitbubsy Jul 30th, 2016 (edited) 448 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
Ledger Nano X - The secure hardware wallet
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