daily pastebin goal
32%
SHARE
TWEET

jms2mod v0.5

8bitbubsy Jul 30th, 2016 (edited) 358 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 amigaPeriodTable[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) samples[i].replen = 2;
  168.  
  169.     // read sample struct
  170.     for (i = 0; i < numSamples; ++i)
  171.     {
  172.         fread(&sampleNum, 1, 1, in);
  173.         if ((sampleNum == 0) || (sampleNum > 32))
  174.         {
  175.             printf("Error: Input JMS file is corrupt!\n");
  176.             return (false);
  177.         }
  178.         sampleNum--;
  179.  
  180.         fread(samples[sampleNum].name, 1, 20, in);
  181.  
  182.         fread(&length, 2,  1, in);
  183.         fread(&repeat, 2,  1, in);
  184.         fread(&replen, 2,  1, in);
  185.         fread(&volume, 1,  1, in);
  186.  
  187.         if (replen == 0)
  188.         {
  189.             replen = 2;
  190.             repeat = 0;
  191.         }
  192.  
  193.         samples[sampleNum].volume = (volume > 64) ? 64 : volume;
  194.         samples[sampleNum].length = SWAP16(length / 2);
  195.         samples[sampleNum].repeat = SWAP16(repeat / 2);
  196.         samples[sampleNum].replen = SWAP16(replen / 2);
  197.     }
  198.  
  199.     // write sample struct
  200.     for (i = 0; i < 31; ++i)
  201.     {
  202.         fwrite(samples[i].name,      1, 22, out);
  203.         fwrite(&samples[i].length,   2,  1, out);
  204.         fwrite(&samples[i].finetune, 1,  1, out);
  205.         fwrite(&samples[i].volume,   1,  1, out);
  206.         fwrite(&samples[i].repeat,   2,  1, out);
  207.         fwrite(&samples[i].replen,   2,  1, out);
  208.     }
  209.  
  210.     return (true);
  211. }
  212.  
  213. void unpackPatt(uint8_t *inPtr, uint8_t *outPtr, uint16_t len)
  214. {
  215.     uint8_t numZeroBytes, numDataBytes, *tempOutPtr;
  216.     uint8_t note, sample, effect, param;
  217.     uint16_t i, writtenBytes, readBytes, period;
  218.  
  219.     // unpack JMS pattern (this unpacker is not safe, but "works")
  220.  
  221.     readBytes    = 0;
  222.     writtenBytes = 0;
  223.  
  224.     tempOutPtr = outPtr;
  225.     while ((readBytes < len) && (writtenBytes < 1024))
  226.     {
  227.         numZeroBytes = *inPtr++;
  228.         numDataBytes = *inPtr++;
  229.  
  230.         for (i = 0; i < numDataBytes; ++i) *tempOutPtr++ = *inPtr++;
  231.         for (i = 0; i < numZeroBytes; ++i) *tempOutPtr++ = 0;
  232.  
  233.         writtenBytes += (numDataBytes + numZeroBytes);
  234.         readBytes    += (numDataBytes + 2);
  235.     }
  236.  
  237.     // convert to MOD pattern format
  238.     for (i = 0; i < (1024 / 4); ++i)
  239.     {
  240.         // note to period
  241.         note = outPtr[0];
  242.         if ((note == 0) || (note > 36))
  243.             period = 0;
  244.         else
  245.             period = amigaPeriodTable[note - 1];
  246.  
  247.         sample = outPtr[1];
  248.         effect = outPtr[2];
  249.         param  = outPtr[3];
  250.  
  251.         // remove F00 (doesn't stop song in NoiseTracker)
  252.         if ((effect == 0x0F) && (param == 0x00))
  253.             effect = 0;
  254.  
  255.         // write MOD pattern bytes
  256.        *outPtr++ = ((sample & 0xF0) | ((period >> 8) & 0x0F));
  257.        *outPtr++ = (period & 0x00FF);
  258.        *outPtr++ = (((sample << 4) & 0xF0) | (effect & 0x0F));
  259.        *outPtr++ = param;
  260.     }
  261. }
  262.  
  263. bool handlePatterns(FILE *in, FILE *out, uint8_t numPatterns)
  264. {
  265.     uint8_t i, *packedPatt, *unpackedPatt;
  266.     uint16_t packedPattLen;
  267.  
  268.     for (i = 0; i < numPatterns; ++i)
  269.     {
  270.         fseek(in, 4, SEEK_CUR); // skip chunk ID
  271.         fread(&packedPattLen, 2, 1, in);
  272.  
  273.         if ((packedPattLen == 0) || (packedPattLen >= 1044))
  274.         {
  275.             printf("Error: Input JMS file is corrupt!\n");
  276.             return (false);
  277.         }
  278.  
  279.         packedPatt = (uint8_t *)(malloc(packedPattLen));
  280.         if (packedPatt == NULL)
  281.         {
  282.             printf("Error: Out of memory!\n");
  283.             return (false);
  284.         }
  285.  
  286.         unpackedPatt = (uint8_t *)(malloc((4 * 64 * 4) + 20)); // +20 is needed. The JMS pattern packer is buggy!
  287.         if (unpackedPatt == NULL)
  288.         {
  289.             free(packedPatt);
  290.             printf("Error: Out of memory!\n");
  291.             return (false);
  292.         }
  293.  
  294.         fread(packedPatt, 1, packedPattLen, in);
  295.         unpackPatt(packedPatt, unpackedPatt, packedPattLen);
  296.         fwrite(unpackedPatt, 1, 4 * 64 * 4, out);
  297.  
  298.         free(packedPatt);
  299.         free(unpackedPatt);
  300.     }
  301.  
  302.     return (true);
  303. }
  304.  
  305. bool handleSampleDatas(FILE *in, FILE *out, uint8_t numSamples)
  306. {
  307.     int8_t *sampleData;
  308.     uint8_t i;
  309.     uint16_t sampleLength;
  310.  
  311.     for (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