diff minijss/jloadjss.c @ 2278:40ccc09f09be

Implement empty channel removal in xm2jss and make JSSMOD format support channel remapping for this.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 18 Jun 2019 12:12:51 +0300
parents ca9fe688ab6b
children fc58f62f100c
line wrap: on
line diff
--- a/minijss/jloadjss.c	Tue Jun 18 12:11:16 2019 +0300
+++ b/minijss/jloadjss.c	Tue Jun 18 12:12:51 2019 +0300
@@ -16,13 +16,19 @@
 #endif
 
 
+static inline JSSNote * jssGetNotePtr(JSSPattern *pattern, const int channel, const int row)
+{
+    return pattern->data + (pattern->nchannels * row) + pattern->map[channel];
+}
+
+
 // Short helper macros for reading data
 #define JSGETBYTE(XV) \
     if (!dmf_read_byte(inFile, XV)) \
         return DMERR_OUT_OF_DATA
 
 
-static int jssDoGetConvertedNote(DMResource *inFile, JSSNote *pnote, Uint8 note)
+static int jssDoGetConvertedNote(DMResource *inFile, JSSNote *pnote, const Uint8 note)
 {
     Uint8 tmp;
 
@@ -50,18 +56,29 @@
 }
 
 
-static inline int jssGetConvertedNote(DMResource *inFile, JSSNote *pnote)
+static inline int jssGetConvertedNote(DMResource *inFile,
+    JSSPattern *pattern, const int channel, const int row)
 {
     Uint8 tmp;
+    int res;
+
     JSGETBYTE(&tmp);
-    return jssDoGetConvertedNote(inFile, pnote, tmp);
+    if ((res = jssDoGetConvertedNote(inFile,
+        jssGetNotePtr(pattern, channel, row), tmp)) != DMERR_OK)
+        JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n",
+        row, channel);
+
+    return res;
 }
 
 
 #if defined(JM_SUP_PATMODE_2) || defined(JM_SUP_PATMODE_4)
-static int jssGetCompressedNote(DMResource *inFile, JSSNote *pnote)
+static int jssGetCompressedNoteDo(DMResource *inFile,
+    JSSPattern *pattern, const int channel, const int row)
 {
+    JSSNote *pnote = jssGetNotePtr(pattern, channel, row);
     Uint8 packb, tmp;
+    int res = DMERR_OK;
 
     JSGETBYTE(&packb);
     if (packb & 0x80)
@@ -102,12 +119,23 @@
     }
     else
     {
-        int ret;
-        if ((ret = jssDoGetConvertedNote(inFile, pnote, packb)) != DMERR_OK)
-            return ret;
+        res = jssDoGetConvertedNote(inFile, pnote, packb);
     }
 
-    return DMERR_OK;
+    return res;
+}
+
+
+static int jssGetCompressedNote(DMResource *inFile,
+    JSSPattern *pattern, const int channel, const int row)
+{
+    int res = jssGetCompressedNoteDo(inFile, pattern, channel, row);
+
+    if (res != DMERR_OK)
+        JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n",
+        row, channel);
+
+    return res;
 }
 #endif
 
@@ -115,19 +143,12 @@
 #ifdef JM_SUP_PATMODE_2
 static int jssGetPatternCompHoriz(DMResource *inFile, JSSPattern *pattern)
 {
-    int row, channel;
-
-    assert(buf != NULL);
-    assert(pattern != NULL);
-
-    for (row = 0; row < pattern->nrows; row++)
-    for (channel = 0; channel < pattern->nchannels; channel++)
+    for (int row = 0; row < pattern->nrows; row++)
+    for (int channel = 0; channel < pattern->nmap; channel++)
     {
-        JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel];
-        int res = jssGetCompressedNote(inFile, pnote);
+        int res = jssGetCompressedNote(inFile, pattern, channel, row);
         if (res != DMERR_OK)
-            JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n",
-            row, channel);
+            return res;
     }
 
     return DMERR_OK;
@@ -138,19 +159,12 @@
 #ifdef JM_SUP_PATMODE_4
 static int jssGetPatternCompVert(DMResource *inFile, JSSPattern *pattern)
 {
-    int row, channel;
-
-    assert(buf != NULL);
-    assert(pattern != NULL);
-
-    for (channel = 0; channel < pattern->nchannels; channel++)
-    for (row = 0; row < pattern->nrows; row++)
+    for (int channel = 0; channel < pattern->nmap; channel++)
+    for (int row = 0; row < pattern->nrows; row++)
     {
-        JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel];
-        int res = jssGetCompressedNote(inFile, pnote);
+        int res = jssGetCompressedNote(inFile, pattern, channel, row);
         if (res != DMERR_OK)
-            JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n",
-            row, channel);
+            return res;
     }
 
     return DMERR_OK;
@@ -161,19 +175,12 @@
 #ifdef JM_SUP_PATMODE_1
 static int jssGetPatternRawHoriz(DMResource *inFile, JSSPattern *pattern)
 {
-    int row, channel;
-
-    assert(buf != NULL);
-    assert(pattern != NULL);
-
-    for (row = 0; row < pattern->nrows; row++)
-    for (channel = 0; channel < pattern->nchannels; channel++)
+    for (int row = 0; row < pattern->nrows; row++)
+    for (int channel = 0; channel < pattern->nmap; channel++)
     {
-        JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel];
-        int res = jssGetConvertedNote(inFile, pnote);
+        int res = jssGetConvertedNote(inFile, pattern, channel, row);
         if (res != DMERR_OK)
-            JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n",
-            row, channel);
+            return res;
     }
 
     return DMERR_OK;
@@ -184,19 +191,12 @@
 #ifdef JM_SUP_PATMODE_3
 static int jssGetPatternRawVert(DMResource *inFile, JSSPattern *pattern)
 {
-    int row, channel;
-
-    assert(buf != NULL);
-    assert(pattern != NULL);
-
-    for (channel = 0; channel < pattern->nchannels; channel++)
-    for (row = 0; row < pattern->nrows; row++)
+    for (int channel = 0; channel < pattern->nmap; channel++)
+    for (int row = 0; row < pattern->nrows; row++)
     {
-        JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel];
-        int res = jssGetConvertedNote(inFile, pnote);
+        int res = jssGetConvertedNote(inFile, pattern, channel, row);
         if (res != DMERR_OK)
-            JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n",
-            row, channel);
+            return res;
     }
 
     return DMERR_OK;
@@ -209,21 +209,18 @@
 #undef JSGETBYTE
 #define JSGETBYTE(XV) if (!dmf_read_byte(inFile, XV)) return DMERR_OUT_OF_DATA
 
-#define JSFOREACHNOTE1                                                              \
-  for (channel = 0; channel < pattern->nchannels; channel++)                        \
-  for (row = 0; row < pattern->nrows; row++) {                                      \
-      JSSNote *pnote = pattern->data + (pattern->nchannels * row) + channel;
+#define JSFOREACHNOTE1 \
+    for (int channel = 0; channel < pattern->nmap; channel++) \
+    for (int row = 0; row < pattern->nrows; row++) { \
+        JSSNote *pnote = pattern->data + (pattern->nchannels * row) + pattern->map[channel];
 
 #define JSFOREACHNOTE2 }
 
+
 static int jssGetPatternRawVertElem(DMResource *inFile, JSSPattern *pattern)
 {
-    int row, channel;
     Uint8 tmp;
 
-    assert(buf != NULL);
-    assert(pattern != NULL);
-
     JSFOREACHNOTE1
     JSGETBYTE(&tmp);
     if (tmp == 0)
@@ -433,7 +430,8 @@
 
         // Read pattern header
         if (!dmf_read_le32(inFile, &jssP.size) ||
-            !dmf_read_le16(inFile, &jssP.nrows))
+            !dmf_read_le16(inFile, &jssP.nrows) ||
+            !dmf_read_le16(inFile, &jssP.nchannels))
             JSSERROR(DMERR_FREAD, DMERR_FREAD,
             "Failed to read JSSMOD pattern header #%d.\n",
             index);
@@ -444,6 +442,11 @@
             "Invalid number of rows in pattern #%d: %d.\n",
             index, jssP.nrows);
 
+        if (jssP.nchannels == 0 || jssP.nchannels > module->nchannels)
+            JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+            "Invalid number of channels in pattern #%d: %d.\n",
+            index, jssP.nchannels);
+
         // Allocate pattern
         pattern = module->patterns[index] = jssAllocatePattern(jssP.nrows, module->nchannels);
         if (pattern == NULL)
@@ -453,6 +456,28 @@
             index);
         }
 
+        // Read channel mappings, if any
+        pattern->nmap = jssP.nchannels;
+        if (jssP.nchannels != module->nchannels)
+        {
+            if (!dmf_read_str(inFile, pattern->map,
+                sizeof(pattern->map[0]) * jssP.nchannels))
+                JSSERROR(DMERR_FREAD, DMERR_FREAD,
+                "Failed to read JSSMOD channel mapping data for pattern #%d.\n",
+                index);
+
+            // Check mapping
+            for (int nch = 0; nch < jssP.nchannels; nch++)
+            {
+                if (pattern->map[nch] >= module->nchannels)
+                {
+                    JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
+                    "Invalid channel mapping in pattern #%d: chn %d -> %d.\n",
+                    index, nch, pattern->map[nch]);
+                }
+            }
+        }
+
         // Get pattern data
         switch (jssH.patMode)
         {