# HG changeset patch # User Matti Hamalainen # Date 1560856982 -10800 # Node ID 25398f2eba648d5b63f7dafb30264c42b75edee8 # Parent 519c8726b235c329efdb607378d0742a3d77e721# Parent 2c90e455f006a01e5f3de744fb33cde770a972f4 Merge. diff -r 519c8726b235 -r 25398f2eba64 Makefile.gen --- a/Makefile.gen Tue Jun 18 07:38:05 2019 +0300 +++ b/Makefile.gen Tue Jun 18 14:23:02 2019 +0300 @@ -367,15 +367,16 @@ $(COMPILE_OBJ) -$(OBJPATH)jssmod.o: $(MINIJSS)jssmod.c $(MINIJSS)jssmod.h $(MINIJSS)jss.h +$(OBJPATH)jssmod.o: $(MINIJSS)jssmod.c $(MINIJSS)jssmod.h $(OBJPATH)jss.o $(COMPILE_OBJ) -$(OBJPATH)jssplr.o: $(MINIJSS)jssplr.c $(MINIJSS)jssplr.h $(MINIJSS)jss.h $(MINIJSS)jssmod.h $(MINIJSS)jssmix.h +$(OBJPATH)jssmix.o: $(MINIJSS)jssmix.c $(MINIJSS)jssmix.h $(OBJPATH)jssmod.o $(COMPILE_OBJ) -$(OBJPATH)jssmix.o: $(MINIJSS)jssmix.c $(MINIJSS)jssmix.h $(MINIJSS)jss.h +$(OBJPATH)jssplr.o: $(MINIJSS)jssplr.c $(MINIJSS)jssplr.h $(OBJPATH)jssmix.o $(COMPILE_OBJ) + $(OBJPATH)dmblit.o: $(DMLIB_SRC)dmblit.c $(DMLIB_SRC)dmscaledblit.h $(DMLIB_SRC)dmunscaledblit.h $(DMLIB_SRC)dmblitfunc.h $(DMLIB_SRC)dmlib.h $(COMPILE_OBJ) diff -r 519c8726b235 -r 25398f2eba64 minijss/jloadjss.c --- a/minijss/jloadjss.c Tue Jun 18 07:38:05 2019 +0300 +++ b/minijss/jloadjss.c Tue Jun 18 14:23:02 2019 +0300 @@ -1,7 +1,7 @@ /* * miniJSS - JSSMOD module loader * Programmed and designed by Matti 'ccr' Hamalainen - * (C) Copyright 2007-2015 Tecnic Software productions (TNSP) + * (C) Copyright 2007-2019 Tecnic Software productions (TNSP) */ #include "jssmod.h" @@ -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) { diff -r 519c8726b235 -r 25398f2eba64 minijss/jssmod.c --- a/minijss/jssmod.c Tue Jun 18 07:38:05 2019 +0300 +++ b/minijss/jssmod.c Tue Jun 18 14:23:02 2019 +0300 @@ -1,7 +1,7 @@ /* * miniJSS - Module structure and handling routines * Programmed and designed by Matti 'ccr' Hamalainen - * (C) Copyright 2006-2015 Tecnic Software productions (TNSP) + * (C) Copyright 2006-2019 Tecnic Software productions (TNSP) */ #include "jssmod.h" @@ -343,7 +343,7 @@ // Check arguments if (nrows <= 0 || nchannels <= 0) - JSSERROR(DMERR_INVALID_ARGS, NULL, "Invalid nrows=%i or nchannels=%i.\n", nrows, nchannels); + JSSERROR(DMERR_INVALID_ARGS, NULL, "Invalid nrows=%d or nchannels=%d.\n", nrows, nchannels); // Allocate a pattern structure if ((pattern = dmMalloc0(sizeof(JSSPattern))) == NULL) @@ -353,14 +353,15 @@ pattern->data = dmMalloc(nrows * nchannels * sizeof(JSSNote)); if (pattern->data == NULL) { - dmFree(pattern); - JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern data (nrows=%i, nchannels=%i).\n", nrows, - nchannels); + jssFreePattern(pattern); + JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern data (nrows=%d, nchannels=%d).\n", + nrows, nchannels); } // Initialize structure pattern->nrows = nrows; pattern->nchannels = nchannels; + pattern->nmap = nchannels; pnote = pattern->data; for (int row = 0; row < nrows; row++) @@ -372,6 +373,22 @@ pnote++; } + // Initialize pattern channel map + pattern->map = dmMalloc(nchannels * sizeof(pattern->map[0])); + pattern->used = dmMalloc(nchannels * sizeof(pattern->used[0])); + if (pattern->map == NULL || pattern->used == NULL) + { + jssFreePattern(pattern); + JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern map (nchannels=%d).\n", + nchannels); + } + + for (int chn = 0; chn < nchannels; chn++) + { + pattern->map[chn] = chn; + pattern->used[chn] = TRUE; + } + return pattern; } @@ -380,6 +397,8 @@ { if (pattern != NULL) { + dmFree(pattern->used); + dmFree(pattern->map); dmFree(pattern->data); dmFree(pattern); } diff -r 519c8726b235 -r 25398f2eba64 minijss/jssmod.h --- a/minijss/jssmod.h Tue Jun 18 07:38:05 2019 +0300 +++ b/minijss/jssmod.h Tue Jun 18 14:23:02 2019 +0300 @@ -1,7 +1,7 @@ /* * miniJSS - Module structure and handling routines * Programmed and designed by Matti 'ccr' Hamalainen - * (C) Copyright 2006-2015 Tecnic Software productions (TNSP) + * (C) Copyright 2006-2019 Tecnic Software productions (TNSP) */ #ifndef JSSMOD_H #define JSSMOD_H @@ -143,7 +143,9 @@ typedef struct { - int nrows, nchannels; + int nrows, nchannels, nmap; + BOOL *used; + Uint8 *map; JSSNote *data; } JSSPattern; @@ -180,7 +182,7 @@ #ifdef JSS_SUP_JSSMOD -#define JSSMOD_VERSION (0x20) +#define JSSMOD_VERSION (0x30) enum { @@ -290,6 +292,9 @@ { Uint32 size; Uint16 nrows; + Uint16 nchannels; // may differ from JSSMODHeader::nchannels + // IF nchannels != JSSMODHeader::nchannels, then: + // Uint8 map[JSSMODPattern::nchannels]; } JSSMODPattern; #endif diff -r 519c8726b235 -r 25398f2eba64 tools/ppl.c --- a/tools/ppl.c Tue Jun 18 07:38:05 2019 +0300 +++ b/tools/ppl.c Tue Jun 18 14:23:02 2019 +0300 @@ -1,7 +1,7 @@ /* * Cyrbe Pasci Player - A simple SDL-based UI for XM module playing * Programmed and designed by Matti 'ccr' Hamalainen - * (C) Copyright 2012-2018 Tecnic Software productions (TNSP) + * (C) Copyright 2012-2019 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ @@ -61,7 +61,8 @@ optOutFreq = 48000, optMuteOChannels = -1, optStartOrder = 0; -BOOL optUsePlayTime = FALSE; +BOOL optUsePlayTime = FALSE, + optUseGUI = TRUE; size_t optPlayTime; @@ -70,6 +71,7 @@ { 0, '?', "help", "Show this help", OPT_NONE }, { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, { 2, 0, "fs", "Fullscreen", OPT_NONE }, + { 12, 'C', "cli", "Do not open GUI window", OPT_NONE }, { 3, 'w', "window", "Initial window size/resolution -w 640x480", OPT_ARGREQ }, { 4, '1', "16bit", "16-bit output", OPT_NONE }, @@ -163,6 +165,10 @@ optUsePlayTime = TRUE; break; + case 12: + optUseGUI = FALSE; + break; + default: dmErrorMsg("Unimplemented option argument '%s'.\n", currArg); return FALSE; @@ -597,7 +603,7 @@ { dmErrorMsg("Error loading module file, %d: %s\n", result, dmErrorStr(result)); - goto error_exit; + goto exit; } // Try to convert it @@ -605,39 +611,15 @@ { dmErrorMsg("Could not convert module for playing, %d: %s\n", result, dmErrorStr(result)); - goto error_exit; + goto exit; } - // Get font - result = dmf_open_memio(NULL, "pplfont.fnt", engineSetupFont, sizeof(engineSetupFont), &file); - if (result != DMERR_OK) - { - dmErrorMsg("Error opening font file 'pplfont.fnt', #%d: %s\n", - result, dmErrorStr(result)); - goto error_exit; - } - result = dmLoadBitmapFont(file, &font); - dmf_close(file); - if (result != DMERR_OK) - { - dmErrorMsg("Could not load font from file, %d: %s\n", - result, dmErrorStr(result)); - goto error_exit; - } - - SDL_Color pal[DMFONT_NPALETTE]; - for (int n = 0; n < DMFONT_NPALETTE; n++) - { - pal[n].r = pal[n].g = pal[n].b = 0; - pal[n].a = n > 0 ? 255 : 0; - } - dmSetBitmapFontPalette(font, pal, 0, DMFONT_NPALETTE); // Initialize SDL components if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0) { dmErrorMsg("Could not initialize SDL: %s\n", SDL_GetError()); - goto error_exit; + goto exit; } initSDL = TRUE; @@ -650,7 +632,7 @@ if (eng.dev == NULL) { dmErrorMsg("jvmInit() returned NULL\n"); - goto error_exit; + goto exit; } switch (optOutFormat) @@ -662,7 +644,7 @@ default: dmErrorMsg("Unsupported audio format %d (could not set matching SDL format)\n", optOutFormat); - goto error_exit; + goto exit; } eng.afmt.freq = optOutFreq; @@ -676,7 +658,7 @@ { dmErrorMsg("Couldn't open SDL audio: %s\n", SDL_GetError()); - goto error_exit; + goto exit; } audioInit = TRUE; @@ -684,7 +666,7 @@ if ((eng.plr = jmpInit(eng.dev)) == NULL) { dmErrorMsg("jmpInit() returned NULL\n"); - goto error_exit; + goto exit; } jvmSetCallback(eng.dev, jmpExec, eng.plr); @@ -700,32 +682,60 @@ muteState = TRUE; } - // Open window - if ((eng.window = SDL_CreateWindow(dmProgName, - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - eng.optScrWidth, eng.optScrHeight, - eng.optVFlags | SDL_WINDOW_RESIZABLE - //| SDL_WINDOW_HIDDEN - )) == NULL) + if (optUseGUI) { - dmErrorMsg("Can't create an SDL window: %s\n", SDL_GetError()); - goto error_exit; - } + // Get font + result = dmf_open_memio(NULL, "pplfont.fnt", engineSetupFont, sizeof(engineSetupFont), &file); + if (result != DMERR_OK) + { + dmErrorMsg("Error opening font file 'pplfont.fnt', #%d: %s\n", + result, dmErrorStr(result)); + goto exit; + } + result = dmLoadBitmapFont(file, &font); + dmf_close(file); + if (result != DMERR_OK) + { + dmErrorMsg("Could not load font from file, %d: %s\n", + result, dmErrorStr(result)); + goto exit; + } + + SDL_Color pal[DMFONT_NPALETTE]; + for (int n = 0; n < DMFONT_NPALETTE; n++) + { + pal[n].r = pal[n].g = pal[n].b = 0; + pal[n].a = n > 0 ? 255 : 0; + } + dmSetBitmapFontPalette(font, pal, 0, DMFONT_NPALETTE); - SDL_SetWindowTitle(eng.window, dmProgDesc); + // Open window + if ((eng.window = SDL_CreateWindow(dmProgName, + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + eng.optScrWidth, eng.optScrHeight, + eng.optVFlags | SDL_WINDOW_RESIZABLE + //| SDL_WINDOW_HIDDEN + )) == NULL) + { + dmErrorMsg("Can't create an SDL window: %s\n", SDL_GetError()); + goto exit; + } - if ((eng.renderer = SDL_CreateRenderer(eng.window, -1, SDL_RENDERER_PRESENTVSYNC)) == NULL) - { - dmErrorMsg("Can't create an SDL renderer: %s\n", SDL_GetError()); - goto error_exit; + SDL_SetWindowTitle(eng.window, dmProgDesc); + + if ((eng.renderer = SDL_CreateRenderer(eng.window, -1, SDL_RENDERER_PRESENTVSYNC)) == NULL) + { + dmErrorMsg("Can't create an SDL renderer: %s\n", SDL_GetError()); + goto exit; + } + + if (!dmInitializeVideo()) + goto exit; + + //SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best"); + //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); } - if (!dmInitializeVideo()) - goto error_exit; - -// SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best"); -// SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); - // okay, main loop here ... "play" module and print out info SDL_LockAudio(); SDL_PauseAudio(0); @@ -809,7 +819,7 @@ case SDLK_f: eng.optVFlags ^= SDL_WINDOW_FULLSCREEN_DESKTOP; if (SDL_SetWindowFullscreen(eng.window, eng.optVFlags) != 0) - goto error_exit; + goto exit; needUpdate = TRUE; break; @@ -830,7 +840,7 @@ eng.optScrWidth = eng.event.window.data1; eng.optScrHeight = eng.event.window.data2; if (!dmInitializeVideo()) - goto error_exit; + goto exit; needUpdate = TRUE; break; @@ -842,98 +852,110 @@ break; } - -#if 1 + // Check for end of song JSS_LOCK(eng.plr); - JSSPattern *currPattern = eng.plr->pattern; - int currRow = eng.plr->row; if (!eng.plr->isPlaying) eng.exitFlag = TRUE; JSS_UNLOCK(eng.plr); - if (currRow != prevRow || needUpdate) - { - prevRow = currRow; - needUpdate = TRUE; - } - - // Draw frame - if (needUpdate) + if (optUseGUI) { - dmClearSurface(eng.screen, col.boxBg); - - dmDrawBMTextQ(eng.screen, font, DMD_TRANSPARENT, 5, 5, - "%s v%s by ccr/TNSP - (c) Copyright 2012-2018 TNSP", - dmProgDesc, dmProgVersion); - - dmDrawBMTextQ(eng.screen, font, DMD_TRANSPARENT, 5, 5 + (font->height + 2), - "Song: '%s'", - eng.mod->moduleName); - - dmDisplayPattern(eng.screen, 5, 5 + (font->height + 2) * 3 + 4, - eng.screen->w - 6, - eng.screen->h * 0.8, - currPattern, currRow); - + // Check if we need to update screen JSS_LOCK(eng.plr); - dmDrawBMTextQ(eng.screen, font, DMD_TRANSPARENT, 5, 5 + (font->height + 2) * 2, - "Tempo: %3d | Speed: %3d | Row: %3d/%-3d | Order: %3d/%-3d | Pattern: %3d/%-3d", - eng.plr->tempo, eng.plr->speed, - eng.plr->row, (eng.plr->pattern != NULL) ? eng.plr->pattern->nrows : 0, - eng.plr->order + 1, eng.mod->norders, - eng.plr->npattern, eng.mod->npatterns); + JSSPattern *currPattern = eng.plr->pattern; + int currRow = eng.plr->row; JSS_UNLOCK(eng.plr); - needRender = TRUE; - } + + if (currRow != prevRow || needUpdate) + { + prevRow = currRow; + needUpdate = TRUE; + } + + // Draw frame + if (needUpdate) + { + dmClearSurface(eng.screen, col.boxBg); - if (needUpdate || currTick - prevTick >= (eng.pauseFlag ? 100 : 20)) - { - JSS_LOCK(eng.dev); - dmDisplayChannels(eng.screen, 5, eng.screen->h * 0.8 + 5, - eng.screen->w - 5, eng.screen->h - 5, eng.dev); - JSS_UNLOCK(eng.dev); - needRender = TRUE; - } + dmDrawBMTextQ(eng.screen, font, DMD_TRANSPARENT, 5, 5, + "%s v%s by ccr/TNSP - (c) Copyright 2012-2019 TNSP", + dmProgDesc, dmProgVersion); - if (needUpdate) - prevTick = currTick; + dmDrawBMTextQ(eng.screen, font, DMD_TRANSPARENT, 5, 5 + (font->height + 2), + "Song: '%s'", + eng.mod->moduleName); + + dmDisplayPattern(eng.screen, 5, 5 + (font->height + 2) * 3 + 4, + eng.screen->w - 6, + eng.screen->h * 0.8, + currPattern, currRow); -#endif - // Flip screen - if (needRender) - { - SDL_Surface dst; - SDL_LockTexture(eng.texture, NULL, &dst.pixels, &dst.pitch); + JSS_LOCK(eng.plr); + dmDrawBMTextQ(eng.screen, font, DMD_TRANSPARENT, 5, 5 + (font->height + 2) * 2, + "Tempo: %3d | Speed: %3d | Row: %3d/%-3d | Order: %3d/%-3d | Pattern: %3d/%-3d", + eng.plr->tempo, eng.plr->speed, + eng.plr->row, (eng.plr->pattern != NULL) ? eng.plr->pattern->nrows : 0, + eng.plr->order + 1, eng.mod->norders, + eng.plr->npattern, eng.mod->npatterns); + JSS_UNLOCK(eng.plr); + needRender = TRUE; + } + + if (needUpdate || currTick - prevTick >= (eng.pauseFlag ? 100 : 20)) + { + JSS_LOCK(eng.dev); + dmDisplayChannels(eng.screen, 5, eng.screen->h * 0.8 + 5, + eng.screen->w - 5, eng.screen->h - 5, eng.dev); + JSS_UNLOCK(eng.dev); + needRender = TRUE; + } - if (dst.pitch != eng.screen->pitch) - eng.exitFlag = TRUE; - else - memcpy(dst.pixels, eng.screen->pixels, eng.screen->h * dst.pitch); + if (needUpdate) + prevTick = currTick; + + // Flip screen + if (needRender) + { + SDL_Surface dst; + SDL_LockTexture(eng.texture, NULL, &dst.pixels, &dst.pitch); - SDL_UnlockTexture(eng.texture); + if (dst.pitch != eng.screen->pitch) + eng.exitFlag = TRUE; + else + memcpy(dst.pixels, eng.screen->pixels, eng.screen->h * dst.pitch); - //SDL_RenderClear(eng.renderer); - SDL_RenderCopy(eng.renderer, eng.texture, NULL, NULL); - SDL_RenderPresent(eng.renderer); - } + SDL_UnlockTexture(eng.texture); + + //SDL_RenderClear(eng.renderer); + SDL_RenderCopy(eng.renderer, eng.texture, NULL, NULL); + SDL_RenderPresent(eng.renderer); + } + } // optUseGUI SDL_Delay(eng.pauseFlag ? 100 : 30); } -error_exit: - if (eng.texture != NULL) - SDL_DestroyTexture(eng.texture); +exit: + // Cleanup + if (optUseGUI) + { + dmMsg(1, "GUI shutdown.\n"); + if (eng.texture != NULL) + SDL_DestroyTexture(eng.texture); - if (eng.renderer != NULL) - SDL_DestroyRenderer(eng.renderer); + if (eng.renderer != NULL) + SDL_DestroyRenderer(eng.renderer); - if (eng.window != NULL) - SDL_DestroyWindow(eng.window); + if (eng.window != NULL) + SDL_DestroyWindow(eng.window); - if (eng.screen != NULL) - SDL_FreeSurface(eng.screen); + if (eng.screen != NULL) + SDL_FreeSurface(eng.screen); - dmMsg(0, "Audio shutdown.\n"); + dmFreeBitmapFont(font); + } + + dmMsg(1, "Audio shutdown.\n"); if (audioInit) { SDL_LockAudio(); @@ -946,8 +968,7 @@ jvmClose(eng.dev); jssFreeModule(eng.mod); - dmFreeBitmapFont(font); - + dmMsg(1, "Shut down SDL.\n"); if (initSDL) SDL_Quit(); diff -r 519c8726b235 -r 25398f2eba64 tools/xm2jss.c --- a/tools/xm2jss.c Tue Jun 18 07:38:05 2019 +0300 +++ b/tools/xm2jss.c Tue Jun 18 14:23:02 2019 +0300 @@ -1,7 +1,7 @@ /* * xm2jss - Convert XM module to JSSMOD * Programmed and designed by Matti 'ccr' Hamalainen - * (C) Copyright 2006-2017 Tecnic Software productions (TNSP) + * (C) Copyright 2006-2019 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ @@ -147,6 +147,15 @@ } +static inline const JSSNote * jssGetNotePtr(const JSSPattern *pattern, const int channel, const int row) +{ + if (!pattern->used[channel]) + return NULL; + else + return pattern->data + (pattern->nchannels * row) + channel; +} + + /* These functions and the macro mess are meant to make the * conversion routines themselves clearer and simpler. */ @@ -189,7 +198,7 @@ /* Convert a note */ -static int jssConvertNote( +static int jssDoConvertNote( Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *pnote) { @@ -220,7 +229,7 @@ /* Compress a note */ -static int jssCompressNote( +static int jssDoCompressNote( Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *pnote) { @@ -256,13 +265,33 @@ JSCOMPPUT(JM_COMP_VOLUME, pnote->volume, "Volume"); JSCOMPPUT(JM_COMP_EFFECT, pnote->effect, "Effect"); JSCOMPPUT(JM_COMP_PARAM, pnote->param, "Param"); + + return DMERR_OK; } else { // Was 4 bytes or more, just dump it all in .. - return jssConvertNote(patBuf, patBufSize, patSize, pnote); + return jssDoConvertNote(patBuf, patBufSize, patSize, pnote); } +} + +static int jssCompressNote( + Uint8 *patBuf, const size_t patBufSize, + size_t *patSize, const JSSPattern *pattern, + const int channel, const int row) +{ + const JSSNote *pnote = jssGetNotePtr(pattern, channel, row); + if (pnote != NULL) + { + int res = jssDoCompressNote(patBuf, patBufSize, patSize, pnote); + if (res != DMERR_OK) + { + JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", + patBuf, patBufSize, *patSize, row, channel); + return res; + } + } return DMERR_OK; } @@ -278,14 +307,9 @@ for (int row = 0; row < pattern->nrows; row++) for (int channel = 0; channel < pattern->nchannels; channel++) { - const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; - const int res = jssCompressNote(patBuf, patBufSize, patSize, pnote); + int res = jssCompressNote(patBuf, patBufSize, patSize, pattern, channel, row); if (res != DMERR_OK) - { - JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", - patBuf, patBufSize, *patSize, row, channel); return res; - } } return DMERR_OK; @@ -301,14 +325,9 @@ for (int channel = 0; channel < pattern->nchannels; channel++) for (int row = 0; row < pattern->nrows; row++) { - const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; - const int res = jssCompressNote(patBuf, patBufSize, patSize, pnote); + int res = jssCompressNote(patBuf, patBufSize, patSize, pattern, channel, row); if (res != DMERR_OK) - { - JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", - patBuf, patBufSize, *patSize, row, channel); return res; - } } return DMERR_OK; @@ -317,6 +336,26 @@ /* Convert a pattern */ +static int jssConvertNote( + Uint8 *patBuf, const size_t patBufSize, + size_t *patSize, const JSSPattern *pattern, + const int channel, const int row) +{ + const JSSNote *pnote = jssGetNotePtr(pattern, channel, row); + if (pnote != NULL) + { + int res = jssDoConvertNote(patBuf, patBufSize, patSize, pnote); + if (res != DMERR_OK) + { + JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", + patBuf, patBufSize, *patSize, row, channel); + return res; + } + } + return DMERR_OK; +} + + static int jssConvertPatternRawHoriz( Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) @@ -326,14 +365,9 @@ for (int row = 0; row < pattern->nrows; row++) for (int channel = 0; channel < pattern->nchannels; channel++) { - const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; - const int res = jssConvertNote(patBuf, patBufSize, patSize, pnote); + int res = jssConvertNote(patBuf, patBufSize, patSize, pattern, channel, row); if (res != DMERR_OK) - { - JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", - patBuf, patBufSize, *patSize, row, channel); return res; - } } return DMERR_OK; @@ -349,14 +383,9 @@ for (int channel = 0; channel < pattern->nchannels; channel++) for (int row = 0; row < pattern->nrows; row++) { - const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; - const int res = jssConvertNote(patBuf, patBufSize, patSize, pnote); + int res = jssConvertNote(patBuf, patBufSize, patSize, pattern, channel, row); if (res != DMERR_OK) - { - JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", - patBuf, patBufSize, *patSize, row, channel); return res; - } } return DMERR_OK; @@ -366,9 +395,11 @@ #define JSFOREACHNOTE1 \ for (channel = 0; channel < pattern->nchannels; channel++) \ for (row = 0; row < pattern->nrows; row++) { \ - const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; + const JSSNote *pnote = jssGetNotePtr(pattern, channel, row); \ + if (pnote != NULL) { -#define JSFOREACHNOTE2 } +#define JSFOREACHNOTE2 } } + static int jssConvertPatternRawElem( Uint8 *patBuf, const size_t patBufSize, @@ -546,12 +577,18 @@ // Convert and write patterns for (totalSize = index = 0; index < module->npatterns; index++) - if (module->patterns[index] != NULL) { JSSPattern *pattern = module->patterns[index]; size_t dataSize = 0; int ret; + if (pattern == NULL) + { + dmMsg(1, + "Pattern #%d is NULL.\n", index); + pattern = module->patterns[module->npatterns]; + } + if (pattern->nrows > jsetMaxRows) { JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, @@ -593,17 +630,30 @@ if (!dm_fwrite_le32(outFile, dataSize) || !dm_fwrite_le16(outFile, pattern->nrows) || - !dm_fwrite_str(outFile, patBuf, dataSize)) + !dm_fwrite_le16(outFile, pattern->nmap)) { JSSERROR(DMERR_FWRITE, DMERR_FWRITE, - "Error writing JSSMOD pattern #%d.\n", + "Error writing JSSMOD pattern header #%d.\n", index); } - } - else - { - JSSERROR(DMERR_NULLPTR, DMERR_NULLPTR, - "Pattern #%d was NULL.\n", index); + + if (pattern->nmap != pattern->nchannels) + { + if (!dm_fwrite_str(outFile, pattern->map, + sizeof(pattern->map[0]) * pattern->nmap)) + { + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, + "Error writing JSSMOD channel map for pattern #%d.\n", + index); + } + } + + if (!dm_fwrite_str(outFile, patBuf, dataSize)) + { + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, + "Error writing JSSMOD pattern data #%d.\n", + index); + } } dmFree(patBuf); @@ -620,8 +670,8 @@ { einst = &tmpEInst; memset(&tmpEInst, 0, sizeof(tmpEInst)); - JSSWARNING(DMERR_NULLPTR, DMERR_NULLPTR, - "Extended instrument #%d NULL!\n", + dmMsg(1, + "Extended instrument #%d is NULL!\n", index); } @@ -744,18 +794,19 @@ /* Scan given pattern for used instruments and channels. * Also checks if the pattern is empty. */ -void scanPattern(const JSSModule *module, const JSSPattern *pattern, - const int npattern, BOOL *usedExtInstruments, BOOL *usedChannels, BOOL *empty) +BOOL jssScanPattern(const JSSModule *module, const JSSPattern *pattern, + const int npattern, BOOL *usedExtInstruments, BOOL *usedChannels) { JSSNote *n = pattern->data; - *empty = FALSE; + BOOL empty = TRUE; // Check all notes in this pattern for (int row = 0; row < pattern->nrows; row++) for (int channel = 0; channel < pattern->nchannels; channel++, n++) { // Is the instrument set? - if (n->instrument != jsetNotSet) + if (usedExtInstruments != NULL && + n->instrument != jsetNotSet) { // Is it valid? if (n->instrument >= 0 && n->instrument < module->nextInstruments) @@ -774,16 +825,20 @@ n->effect != jsetNotSet || n->param != jsetNotSet) { - usedChannels[channel] = TRUE; - *empty = FALSE; + if (usedChannels != NULL) + usedChannels[channel] = TRUE; + + empty = FALSE; } } + + return empty; } /* Check if two given patterns are dupes */ -BOOL comparePattern(const JSSPattern *pat1, const JSSPattern *pat2) +BOOL jssComparePattern(const JSSPattern *pat1, const JSSPattern *pat2) { return pat1->nrows == pat2->nrows && @@ -794,12 +849,11 @@ /* Optimize a given module */ -JSSModule *optimizeModule(JSSModule *m) +JSSModule *jssOptimizeModule(JSSModule *m) { BOOL usedPatterns[jsetMaxPatterns + 1], usedInstruments[jsetMaxInstruments + 1], - usedExtInstruments[jsetMaxInstruments + 1], - usedChannels[jsetMaxChannels]; + usedExtInstruments[jsetMaxInstruments + 1]; int mapExtInstruments[jsetMaxInstruments + 1], mapInstruments[jsetMaxInstruments + 1], mapPatterns[jsetMaxPatterns + 1], @@ -827,7 +881,6 @@ for (int i = 0; i < jsetNChannels; i++) { r->defPanning[i] = m->defPanning[i]; - usedChannels[i] = FALSE; } // Initialize values @@ -859,13 +912,8 @@ JSSPattern *pattern = m->patterns[npat]; if (pattern != NULL) { - BOOL empty; - - // Mark this pattern as used - usedPatterns[npat] = TRUE; - - // Scan for used instruments and channels - scanPattern(m, pattern, npat, usedExtInstruments, usedChannels, &empty); + // Scan for used instruments etc + BOOL empty = jssScanPattern(m, pattern, npat, usedExtInstruments, NULL); // Empty patterns with known number of rows are "removed" if (empty && pattern->nrows == jsetDefaultRows) @@ -873,6 +921,8 @@ m->orderList[norder] = jsetNotSet; usedPatterns[npat] = FALSE; } + else + usedPatterns[npat] = TRUE; } else { @@ -951,7 +1001,7 @@ for (int pat2 = 0; pat2 < m->npatterns; pat2++) if (pat1 != pat2 && m->patterns[pat2] != NULL && dupPatterns[pat2] == jsetNotSet && - comparePattern(m->patterns[pat1], m->patterns[pat2])) + jssComparePattern(m->patterns[pat1], m->patterns[pat2])) { dmPrint(1, " * %d and %d are dupes.\n", pat1, pat2); dupPatterns[pat2] = pat1; @@ -1077,19 +1127,6 @@ r->nextInstruments, nunused); // - // Check for actually used channels - // XXX TODO: Actually remove the unused channels. - // - nunused = 0; - for (int i = 0; i < r->nchannels; i++) - { - if(!usedChannels[i]) - nunused++; - } - dmMsg(1, "%d channels (%d unused).\n", - r->nchannels - nunused, nunused); - - // // Remap pattern data with remapped instrument data // for (int i = 0; i < r->npatterns; i++) @@ -1150,6 +1187,29 @@ if (nunused) dmPrint(2, "\n"); + // + // Do final pass on patterns to remove unused channels + // + for (int i = 0; i < r->npatterns; i++) + { + JSSPattern *p = r->patterns[i]; + + jssScanPattern(r, p, i, NULL, p->used); + + p->nmap = 0; + for (int i = 0; i < r->nchannels; i++) + { + if (p->used[i]) + p->map[p->nmap++] = i; + } + + if (p->nmap != p->nchannels) + { + dmMsg(2, "Pattern %d: %d/%d used channels (%d unused).\n", + i, p->nchannels - p->nmap, p->nchannels, p->nmap); + } + } + return r; } @@ -1161,7 +1221,7 @@ JSSModule *sm, *dm; int result; - dmInitProg("xm2jss", "XM to JSSMOD converter", "0.7", NULL, NULL); + dmInitProg("xm2jss", "XM to JSSMOD converter", "0.8", NULL, NULL); dmVerbosity = 0; // Parse arguments @@ -1252,7 +1312,7 @@ if (optOptimize) { dmMsg(1, "Optimizing module data...\n"); - dm = optimizeModule(sm); + dm = jssOptimizeModule(sm); } else dm = sm;