SHARE
TWEET

jms2mod v0.4

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