# HG changeset patch # User Matti Hamalainen # Date 1348786463 -10800 # Node ID 32250b436bca27d4412f3c6ede70fdb0b444f918 Initial re-import. diff -r 000000000000 -r 32250b436bca .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,11 @@ +syntax: glob +ChangeLog +*~ +*.orig +*.diff +*.o +*.a +*.mak +modules/ +exe/ +fgcl/ diff -r 000000000000 -r 32250b436bca Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,13 @@ +# +# Generic UNIX targets +# +SDL_CFLAGS=`sdl-config --cflags` +SDL_LDFLAGS=`sdl-config --static-libs` + +RANLIB=ranlib + +BINPATH ?= ./exe/ +OBJPATH ?= ./unix/ +EXEEXT ?= + +include Makefile.gen diff -r 000000000000 -r 32250b436bca Makefile.gen --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.gen Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,261 @@ +### +### Get settings from config.mak +### +include config.mak + +ifeq ($(EXTRA_CFLAGS),) +EXTRA_CFLAGS=-march=core2 +endif + + +### +### Form additional compilation defines based on settings +### + +# If C routines are not explicitly disabled, we enable them +ifeq ($(DM_USE_C),) +DM_USE_C=yes +endif + +ifeq ($(DM_USE_C),yes) +DM_CFLAGS+=-DDM_USE_C +endif + +# Enable SIMD routines, if requested +ifeq ($(DM_USE_SIMD),yes) +DM_CFLAGS+=-DDM_USE_SIMD +endif + + +### +### Filesystem +### +ifeq ($(DMRES_PACKFS),yes) +DM_CFLAGS += -DDMRES_PACKFS +DMLIB_OBJS += dmpack.o dmpackutil.o +DM_LDFLAGS += -lz +BINARIES += packed +endif + +ifeq ($(DMRES_STDIO),yes) +DM_CFLAGS += -DDMRES_STDIO +endif + + +### +### Graphics support +### + +ifeq ($(DM_GFX_MISC),yes) +DM_CFLAGS += -DDM_GFX_MISC +DMLIB_OBJS += dmgfx.o +endif + +ifeq ($(DM_GFX_LINES),yes) +DM_CFLAGS += -DDM_GFX_LINES +DMLIB_OBJS += dmline.o +endif + +ifeq ($(DM_GFX_BLITS),yes) +DM_CFLAGS += -DDM_GFX_BLITS +DMLIB_OBJS += dmblit.o +endif + +ifeq ($(DM_GFX_TTF_TEXT),yes) +DM_CFLAGS += -DDM_GFX_TTF_TEXT +DMLIB_OBJS += dmtext.o + +ifeq ($(DM_GFX_BLITS),yes) +BINARIES += blittest vview vptest efu +endif +endif + +ifeq ($(DM_GFX_8BIT),yes) +DM_CFLAGS += -DDM_GFX_8BIT +endif + +ifeq ($(DM_GFX_15BIT),yes) +DM_CFLAGS += -DDM_GFX_15BIT +endif + +ifeq ($(DM_GFX_16BIT),yes) +DM_CFLAGS += -DDM_GFX_15BIT +endif + +ifeq ($(DM_GFX_32BIT),yes) +DM_CFLAGS += -DDM_GFX_15BIT +endif + + +### +### miniJSS +### +ifeq ($(JSS),yes) + +ifeq ($(JSS_LIGHT),yes) +DM_CFLAGS+=-DJSS_LIGHT -Os +else +ifeq ($(JSS_USE_ASSERTS),yes) +DM_CFLAGS+=-DJSS_USE_ASSERTS +endif +ifeq ($(JSS_DEBUG),yes) +DM_CFLAGS+=-DJSS_DEBUG +endif +endif + +ifeq ($(JSS_SUP_THREADS),yes) +DM_CFLAGS+=-DJSS_SUP_THREADS +endif + +DMLIB_OBJS += jss.o jssmix.o jssmod.o jssplr.o + +SUP_MODLOAD=no +ifeq ($(JSS_SUP_XM),yes) +SUP_MODLOAD=yes +DMLIB_OBJS+= jloadxm.o +DM_CFLAGS+=-DJSS_SUP_XM +endif + +ifeq ($(JSS_SUP_JSSMOD),yes) +SUP_MODLOAD=yes +DMLIB_OBJS+= jloadjss.o +DM_CFLAGS+=-DJSS_SUP_JSSMOD $(JSSMOD_FLAGS) +endif + +ifeq ($(DM_USE_C),yes) +DMLIB_OBJS+=jmix_c.o +endif + +#ifeq ($(DM_USE_SIMD),yes) +#DMLIB_OBJS+=jmix_simd.o +#endif + + +### Dependancies +ifeq ($(SUP_MODLOAD),yes) +BINARIES+= viewmod mod2wav testpl +endif + + +ifeq ($(JSS_SUP_JSSMOD),yes) +ifeq ($(JSS_SUP_XM),yes) +BINARIES+= xm2jss +endif +endif + +endif + +### +### Rest of the rules +### +CFLAGS += -g -W -Wall -DHAVE_STRING_H -O3 $(EXTRA_CFLAGS) +CXXFLAGS=$(CFLAGS) + +DM_CFLAGS += $(SDL_CFLAGS) +DM_LDFLAGS += $(LDFLAGS) $(SDL_LDFLAGS) + + +DMLIB_A=$(OBJPATH)dmlib.a +DMLIB_OBJS += dmfile.o dmlib.o dmlerp.o dmstring.o \ + dmargs.o dmvecmat.o dmperlin.o dmimage.o dmres.o + +BINARIES += vecmattest fptest + +TARGETS=$(DMLIB_A) $(addprefix $(BINPATH),$(addsuffix $(EXEEXT),$(BINARIES))) + +all: $(TARGETS) + +### +### Generic rules +### + +$(OBJPATH)%.o: %.c %.h + @echo " CC $<" + @$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS) + +$(OBJPATH)%.o: %.c + @echo " CC $<" + @$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS) + + +### +### dmlib rules +### + +jmix_c.c: jmix_c.in.c jmix_c.h jmix_post.h + (echo "#include \"jssmix.h\"" && cpp $<) | sed "s/^# .*//g" > $@ + + +$(OBJPATH)dmimage.o: dmimage.c stb_image.c + @echo " CC $+" + @$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS) + +$(OBJPATH)dmblit.o: dmblit.c dmscaledblit.h dmblitfunc.h + @echo " CC $+" + @$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS) + +$(OBJPATH)dmline.o: dmline.c dmdrawline.h dmlinefunc.h + @echo " CC $+" + @$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS) + +$(OBJPATH)dmargs.o: dmargs.c dmargs.h dmargs_int.c + @echo " CC $+" + @$(CC) $(CFLAGS) -c -o $@ $< $(DM_CFLAGS) + + +$(DMLIB_A): $(addprefix $(OBJPATH),$(DMLIB_OBJS)) Makefile.gen config.mak + @echo " AR $@ $(addprefix $(OBJPATH),$(DMLIB_OBJS))" + @$(AR) cru $@ $+ $(addprefix $(OBJPATH),$(DMLIB_OBJS)) + @$(RANLIB) $@ + + +### +### Tests and binaries +### +$(BINPATH)vview$(EXEEXT): $(OBJPATH)vview.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $+ $(DM_LDFLAGS) -lSDL_ttf + +$(BINPATH)blittest$(EXEEXT): $(OBJPATH)blittest.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $+ $(DM_LDFLAGS) -lSDL_ttf + +$(BINPATH)efu$(EXEEXT): $(OBJPATH)efu.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $+ $(DM_LDFLAGS) -lSDL_ttf -lSDL_image + +$(BINPATH)vptest$(EXEEXT): $(OBJPATH)vptest.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $+ $(DM_LDFLAGS) -lSDL_ttf + +$(BINPATH)%test$(EXEEXT): $(OBJPATH)%test.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $+ $(DM_LDFLAGS) + +$(BINPATH)packed$(EXEEXT): $(OBJPATH)packed.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $+ $(DM_LDFLAGS) + +$(BINPATH)mod2wav$(EXEEXT): $(OBJPATH)mod2wav.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $+ $(DM_LDFLAGS) + +$(BINPATH)xm2jss$(EXEEXT): $(OBJPATH)xm2jss.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $+ $(DM_LDFLAGS) + +$(BINPATH)viewmod$(EXEEXT): $(OBJPATH)viewmod.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $+ $(DM_LDFLAGS) + +$(BINPATH)testpl$(EXEEXT): $(OBJPATH)testpl.o $(DMLIB_A) + @echo " LINK $+" + @$(CC) -o $@ $+ $(DM_LDFLAGS) + + +### +### Special targets +### +clean: + $(RM) $(TARGETS) $(OBJPATH)*.o + diff -r 000000000000 -r 32250b436bca Makefile.w32 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.w32 Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,24 @@ +# +# For win32 version cross-compilation with MinGW suite @ Linux +# +MINGW_PREFIX=i686-w64-mingw32- +MINGW_PATH=/usr/i686-w64-mingw32 + +BINPATH=./exe/ +OBJPATH=./win32/ +EXEEXT=.exe + +# C-compiler, flags and linker flags +CC=$(MINGW_PREFIX)gcc +AR=$(MINGW_PREFIX)ar +RANLIB=$(MINGW_PREFIX)ranlib +RM=rm + +SDL_CFLAGS=`$(MINGW_PATH)/bin/sdl-config --cflags` +SDL_LDFLAGS=`$(MINGW_PATH)/bin/sdl-config --libs` -lSDL -L$(BINPATH) + +#CFLAGS=-mwindows +LDFLAGS=-lmingw32 -s + + +include Makefile.gen diff -r 000000000000 -r 32250b436bca TODO.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TODO.txt Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,95 @@ +- HOX! envelope sustain ei toimi ihan oikein ilmeisesti! + kts. crstrvl.xm + +- volume envelopen asettama volyymi tarvitsee erillisen muuttujan + vs. jmpCSet. sama homma panning envelopelle. + +- lukitukset: + - device + - playeri + +- tremolo, vibrato ym ... jääkö viimeinen taajuus/volume "päälle"? + +- volume vibrato: miten volslide + volvibrato käyttäytyy? + +- arpeggion toiminta? [korjattu nyt?] + +- note delay: viivyttää nuottia ja samplea jne. + +=============================================================================== +FT2-emuloiva miksausrutiini +--------------------------- + - tarvitaan volume ramp-tuki, joka interpoloi halutulla aikavälillä + volumen arvoa (per kanava). aikaväli voi olla lyhyempi kuin 1 frame, + ts. rutiinin on tuettava miten pitkiä ramppeja vain + + - virtuaalikanavat: instrumentit soivat 0.005s päällekkäin, eli + jonkinlainen virtuaalikanavointi on luultavasti käytössä + + +=============================================================================== +Ext. instrument envelopejen toiminta +------------------------------------ +suoritetaanko ext.instrumentteja jos pattern delay on voimassa? + - kyllä. ja myös jos note delay. näillä ei ole mitään vaikutusta + instrumentin envelopejen jne. toimintaan. + + +samplejen alussa oleva "volume ramp": + + - on ns. frame #0:n sisällä (mutta ei välttämättä framen pituinen) + - ei riipu bpm:stä, speedistä tai sampletaajuudesta jne. + - pituus aina 0.005 sekuntia, ts. + len = 48000 * 0.005 = 240 + + +frame = aika / tempo, eli esim. 125 bpm tempolla: + + 1 frame = 1 sekunti / ((125 * 2) / 5) + 1 frame = 1 / 50 + +eli miksaustaajuudella "mixfreq", yhden framen pituus on + + frame.len = mixfreq / ((tempo * 2) / 5) + +esim. 48kHz, 125bpm + + frame.len = 48000 / ((125 * 2) / 5) + frame.len = 48000 / 50 + frame.len = 960 (samplea) + +eli kyseessä on sama käsite/aikaväli millä playerin tick-funktioita +suoritetaan. on kuitenkin huomioitava seuraavat seikat: + +1) FT2:n miksausrutiini interpoloi envelopejen arvot lineaarisesti + yhden framen sisällä. + +2) ensimmäinen envelopen piste (0) alkaa itseasiassa framesta #1! + frame #0 sisältää inherentin + ts. jos on määritelty seuraavanlainen envelope: + + [0,32]-[16,63] + + jossa luvut ovat aika eli framen numero (-1) ja arvo, esim. + äänenvoimakkuus, niin lopputulos on miksatussa datassa: + + | | | **|63 <- loppuarvo saavutetaan vasta + | | |**** | 17. framen vaihteessa + | | ****| | + | | **** | | + | | **** | | + 32|....****|**** | |32 + | *: | | | + | * : | | | + | * : | | | + |* : | | | + 0 : 1 15 16 + ...... + volume ramp (0.005s) + + +3) jostain syystä FT2:n envelopen interpoloinnissa näkyy joskus omalaatuista + epälineaarisuutta, mutta se saattaa olla täysin luonnollista. luultavasti + ko. omituisuuden emulointi ei ole kannattavaa. + +=============================================================================== diff -r 000000000000 -r 32250b436bca blittest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/blittest.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,545 @@ +#include "dmlib.h" +#include "dmargs.h" +#include "dmvecmat.h" +#include +#ifdef USE_IMG +#include +#endif + +#define DM_COLORS (256) + +char *optFontFile = "font.ttf", + *optBitmapFilename = "tnsp.pcx"; +BOOL optBenchmark = FALSE; +int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE; +int optScrWidth = 640, optScrHeight = 480, optFontSize = 20, optScrDepth = 32; +int optBenchmarkLen = 20; + +DMOptArg optList[] = { + { 0, '?', "help", "Show this help", OPT_NONE }, + { 2, 'v', "verbose", "Be more verbose", OPT_NONE }, + { 3, 'f', "full", "Fullscreen", OPT_NONE }, + { 4, 'h', "hw", "Use SDL hardware surface", OPT_NONE }, + { 5, 's', "size", "Initial window size/resolution -s 640x480", OPT_ARGREQ }, + { 6, 'd', "depth", "Color depth of mode/window in bits (8/15/16/32)", OPT_ARGREQ }, + { 7, 'b', "bench", "Run in benchmark mode", OPT_NONE }, +}; + +const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void argShowHelp() +{ + dmArgsPrintHelp(stdout, optList, optListN); +} + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + switch (optN) { + case 0: + argShowHelp(); + exit(0); + break; + + case 2: + dmVerbosity++; + break; + + case 3: + optVFlags |= SDL_FULLSCREEN; + break; + + case 6: + if (optArg) + optScrDepth = atoi(optArg); + break; + + case 5: + { + int w, h; + if (sscanf(optArg, "%dx%d", &w, &h) == 2) + { + if (w < 320 || h < 200 || w > 3200 || h > 3200) + { + dmError("Invalid width or height: %d x %d\n", w, h); + return FALSE; + } + optScrWidth = w; + optScrHeight = h; + } + else + { + dmError("Invalid size argument '%s'.\n", optArg); + return FALSE; + } + } + break; + + case 7: + optBenchmark = TRUE; + break; + + default: + dmError("Unknown option '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +void DM_MakePalette(SDL_Surface *scr) +{ + SDL_Color pal[DM_COLORS]; + int n; + + for (n = 0; n < 256; n++) + { + pal[n].r = n; + pal[n].g = n; + pal[n].b = n; + } + + SDL_SetColors(scr, pal, 0, DM_COLORS); +} + + +void DM_PrintRect(FILE *f, SDL_Rect *r) +{ + fprintf(f, "SDL_Rect <%d, %d : %d, %d>\n", + r->x, r->y, r->w, r->h); +} + +BOOL DM_InitializeVideo(SDL_Surface **screen) +{ + *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE); + if (*screen == NULL) + { + dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError()); + return FALSE; + } + +#if 0 + SDL_Rect r; + r.x = -50; + r.y = 50; + r.w = 700; + r.h = 300; + DM_PrintRect(stderr, &r); + SDL_SetClipRect(*screen, &r); + DM_PrintRect(stderr, &r); + DM_PrintRect(stderr, &((*screen)->clip_rect)); +#endif + + return TRUE; +} + +void DM_Random(SDL_Surface *screen, int q) +{ + Uint8 *pix = screen->pixels; + int xc, yc; + + for (yc = 0; yc < screen->h; yc++) + { + Uint8 *dp = pix; + + for (xc = 0; xc < screen->w; xc++) + *dp++ = yc + (xc ^ q) + (yc & q); + + pix += screen->pitch; + } +} + +void DM_Perlin(SDL_Surface *screen, float f) +{ + Uint8 *pix = screen->pixels; + int xc, yc; + + for (yc = 0; yc < screen->h; yc++) + { + Uint8 *dp = pix; + + for (xc = 0; xc < screen->w; xc++) + { + *dp++ = 128 + dmPerlinNoise2D(xc, yc, 0.01, 0.1, 3) / 34.0; + } + + pix += screen->pitch; + } +} + + +#define QWIDTH 256 +#define QHEIGHT 160 + +typedef Uint8 DMBlockMap[QHEIGHT][QWIDTH]; + + +static DMFloat dmClip(DMFloat a) +{ + return (a < 0.0f ? 0.0f : (a > 1.0f ? 1.0f : a)); +} + + +void dmMakeBumpMap(DMBlockMap map, DMFloat q, DMFloat m) +{ + int x, y; + for (y = 0; y < QHEIGHT; y++) + for (x = 0; x < QWIDTH; x++) + { + DMFloat f = 0.40f + dmPerlinNoise2D(x, y, 1.1f, q, 2); + map[y][x] = (int) (dmClip(f) * m); + } +} + + +void dmShadowTraceHeightMap(DMBlockMap lightMap, DMBlockMap pheightMap, DMVector *light) +{ + int i, j; + + for (j = 0; j < QHEIGHT; j++) + for (i = 0; i < QWIDTH; i++) + { + DMVector vr, vl, va; + DMFloat vrayLen, vfactor; + int vlen; + BOOL wasHit; + + // Perform shadow occlusion via simplistic raytracing + vr.x = i; + vr.y = j; + vr.z = light->z; // - 10.0; +// vr.z = pheightMap[j][i]; + + // Calculate light vector vector + dm_vector_sub_r(&vl, &vr, light); + vrayLen = dm_vector_length(&vl); + +#if 1 + dm_vector_copy(&va, &vl); + dm_vector_normalize(&va); + dm_vector_scale(&va, 0.6f); + dm_vector_copy(&vr, light); + + vlen = 0; + wasHit = FALSE; + do + { + float h; + + // If ray is inside the heightmap, get value + if (vr.x >= 0 && vr.y >= 0 && vr.x < QWIDTH && vr.y < QHEIGHT) + h = pheightMap[(int) vr.y][(int) vr.x]; + else + break; + + // Check for hits + if (h > vr.z) + wasHit = TRUE; + else + { + // Move forwards + dm_vector_add(&vr, &va); + vlen++; + } + } + while (!wasHit && vlen <= vrayLen); + + // Check if the ray hit something, e.g. is this point occluded? + if (wasHit && vlen < vrayLen) + { + vfactor = vlen * 0.01; + } + else + vfactor = vlen * 0.02; +#endif + +#if 1 + { + /* Calculate light's intensity based on the angle it "hits" + * + * 1) Calculate the vectors that form the imaginary "plane" + * 2) Cross-product -> normal vector of the plane + * 2) Normalize the normal vector + * 3) Calculate light vector's hit angle by dot product + */ + DMVector v1, v2; + DMFloat c; + + v1.x = 2.0f; + v1.y = 0.0f; + v1.z = (DMFloat) (pheightMap[j][i] - pheightMap[j][i + 1]); + + v2.x = 0.0f; + v2.y = 2.0f; + v2.z = (DMFloat) (pheightMap[j][i] - pheightMap[j + 1][i]); + + dm_vector_cross(&vr, &v1, &v2); + dm_vector_normalize(&vr); + dm_vector_normalize(&vl); + c = dm_vector_dot(&vl, &vr); + + vrayLen = 255 - (vrayLen * 0.1) * vrayLen + (c * 128.0f) + (vfactor * vfactor * 1255); + } +#else + vrayLen = 255 - vrayLen * vrayLen * (vfactor * vfactor); + if (vrayLen < 0) vrayLen = 0; + vrayLen += pheightMap[j][i]; +#endif + + // Clip result + if (vrayLen < 0) + vrayLen = 0; + else if (vrayLen > 255.0f) + vrayLen = 255.0f; + + lightMap[j][i] = vrayLen; + } +} + + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen = NULL, *bmap = NULL, *logo = NULL; + TTF_Font *font = NULL; + SDL_Color fontcol={255,155,155,0}; + Uint32 lcol; + SDL_Event event; + int mouseX, mouseY; + BOOL initSDL = FALSE, initTTF = FALSE, exitFlag, showMap = FALSE, initImage = FALSE; + + dmInitProg("blittest", "dmlib blittest", "0.2", NULL, NULL); + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, NULL, FALSE)) + exit(1); + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) + { + dmError("Could not initialize SDL: %s\n", SDL_GetError()); + goto error_exit; + } + initSDL = TRUE; + + + if (TTF_Init() < 0) + { + dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError()); + goto error_exit; + } + initTTF = TRUE; + + font = TTF_OpenFont(optFontFile, optFontSize); + if (font == NULL) + { + dmError("Could not load TTF font '%s' (%d): %s\n", + optFontFile, optFontSize, SDL_GetError()); + goto error_exit; + } + TTF_SetFontStyle(font, TTF_STYLE_NORMAL); + +#ifdef USE_IMG + if (IMG_Init(IMG_INIT_PNG) < 0) + { + dmError("Could not initialize SDL_image: %s\n", SDL_GetError()); + goto error_exit; + } + initImage = TRUE; + + SDL_RWops *rwop = SDL_RWFromFile(optBitmapFilename, "rb"); + if (rwop == NULL) + { + dmError("Could not open file '%s'.\n", optBitmapFilename); + goto error_exit; + } + logo = IMG_LoadPCX_RW(rwop); + SDL_RWclose(rwop); + if (logo == NULL) + { + dmError("Could not load image file '%s'.\n", optBitmapFilename); + goto error_exit; + } +#endif + + if (optBenchmark) + { + screen = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0); + if (screen == NULL) + { + dmError("Could not create screen surface.\n"); + goto error_exit; + } + + dmMsg(0, "Benchmark mode, not opening window.\n"); + } + else + { + if (!DM_InitializeVideo(&screen)) + goto error_exit; + + SDL_WM_SetCaption("Halleluja", "DMT"); + } + + dmPerlinInit(); + + bmap = SDL_CreateRGBSurface(SDL_SWSURFACE, QWIDTH, QHEIGHT, 8, 0, 0, 0, 0); + DM_MakePalette(bmap); + DM_Random(bmap, 15); + + DMVector light; + DMBlockMap heightMap; + light.x = light.y = 128; + light.z = 128; + dmMakeBumpMap(heightMap, 0.06, 254); + + + + lcol = dmMapRGB(screen, 255,100,100); + + int numFrames = 0, startTime = SDL_GetTicks(), endTime = 0; + exitFlag = FALSE; + + if (optBenchmark) + dmMsg(0, "Starting benchmark, running for %d seconds.\n", optBenchmarkLen); + + while (!exitFlag) + { + dmMakeBumpMap(heightMap, 0.05 + sin(SDL_GetTicks() * 0.001) * 0.002 , 254); + if (!optBenchmark) + { + while (SDL_PollEvent(&event)) + switch (event.type) + { + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: exitFlag = TRUE; break; + + case SDLK_F5: + showMap = !showMap; + break; + + default: + break; + } + + break; + + case SDL_VIDEORESIZE: + optScrWidth = event.resize.w; + optScrHeight = event.resize.h; + + if (!DM_InitializeVideo(&screen)) + goto error_exit; + + break; + + case SDL_VIDEOEXPOSE: + break; + + case SDL_QUIT: + exit(0); + } + + SDL_GetMouseState(&mouseX, &mouseY); + light.x = ((DMFloat) mouseX * QWIDTH) / (DMFloat) optScrWidth; + light.y = ((DMFloat) mouseY * QHEIGHT) / (DMFloat) optScrHeight; + } + + if (!optBenchmark && SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0) + { + dmError("Can't lock surface.\n"); + goto error_exit; + } + + + float f = SDL_GetTicks() / 250.0f; + +#if 1 + if (showMap) + memcpy(bmap->pixels, heightMap, QWIDTH * QHEIGHT); + else + dmShadowTraceHeightMap(bmap->pixels, heightMap, &light); + + dmScaledBlitSurfaceAny(bmap, 0, 0, screen->w, screen->h, screen, DMD_NONE); +#else + dmScaledBlitSurfaceAny(bmap, + 100 + sin(f) * 50, + 100 + cos(f) * 50, + screen->w - 200 + 50 * cos(f), + screen->h - 200 + 50 * sin(f), + screen, DMD_NONE); + + dmScaledBlitSurfaceAny(bmap, + 200 + sin(f) * 50, + 200 + cos(f) * 50, + 50 + 50 * cos(f), + 50 + 50 * sin(f), + screen, DMD_NONE); + + dmScaledBlitSurfaceAny(bmap, + 400, + 200, + 50 + 50 * cos(f), + 50 + 50 * sin(f), + screen, DMD_NONE); + +/* + dmDrawLineAny(screen, 30, 30, 600 + 200 * sin(f), 250 , lcol, DMD_NONE); + dmDrawLineAny(screen, 20, 30, 100 , 250 + 100 * sin(f), lcol, DMD_NONE); + dmDrawLineAny(screen, 100, 100, 100 + cos(f) * 50, 100 + sin(f) * 50, lcol, DMD_NONE); +*/ +#endif + + if (!optBenchmark) + { + dmDrawTTFText(screen, font, fontcol, 0, 0, "%3.1f FPS", + (float) (numFrames * 1000.0f) / (float) (endTime - startTime)); + + if (SDL_MUSTLOCK(screen) != 0) + SDL_UnlockSurface(screen); + + SDL_Flip(screen); + SDL_Delay(10); + } + + endTime = SDL_GetTicks(); + numFrames++; + + if (optBenchmark) + { + if (endTime - startTime > optBenchmarkLen * 1000) + exitFlag = TRUE; + } + } + + // Print benchmark results + dmMsg(0, "%d frames in %d ms, fps = %1.3f\n", + numFrames, endTime - startTime, + (float) (numFrames * 1000.0f) / (float) (endTime - startTime)); + + +error_exit: + dmMsg(0, "Shutting down dmlib.\n"); + if (screen) + SDL_FreeSurface(screen); + + if (logo) + SDL_FreeSurface(logo); + + if (font) + TTF_CloseFont(font); + + if (initSDL) + SDL_Quit(); + +#ifdef USE_IMG + if (initImage) + IMG_Quit(); +#endif + + if (initTTF) + TTF_Quit(); + + return 0; +} diff -r 000000000000 -r 32250b436bca config.mak.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.mak.in Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,67 @@ +# Build with runtime asserts? +DM_USE_ASSERTS=yes + +# Use plain C routines? +DM_USE_C=yes + +# Use MMX/SSE SIMD assembler routines? +DM_USE_SIMD=yes + + +DMRES_PACKFS=yes +DMRES_STDIO=yes +DMRES_MEMIO=yes + + +DM_GFX_TTF_TEXT=yes +DM_GFX_LINES=no +DM_GFX_BLITS=yes +DM_GFX_MISC=yes + +DM_GFX_8BIT=yes +DM_GFX_32BIT=yes + + +### +### General build options for miniJSS +### +JSS=yes + +# Build a special "light"/minimized version by removing all +# error messages and few other things. +JSS_LIGHT=no + +# Build with extra debugging code? (disabled by JSS_LIGHT=yes) +JSS_DEBUG=yes + + +### Support loading of XM-format module files +JSS_SUP_XM=yes + + +### Generic support of JSSMOD-format modules +JSS_SUP_JSSMOD=yes + +# Enable support for different pattern storage modes +# in the JSSMOD _loader_. Saving in xm2jss is not affected. +JSSMOD_FLAGS = -DJM_SUP_PATMODE_ALL + +# If SUP_PATMODE_ALL is not defined, then you can enable modes individually +# by uncommenting lines below. Refer to 'xm2jss --help' for mode information. +#JSSMOD_FLAGS += -DJM_SUP_PATMODE_1 +#JSSMOD_FLAGS += -DJM_SUP_PATMODE_2 +#JSSMOD_FLAGS += -DJM_SUP_PATMODE_3 +#JSSMOD_FLAGS += -DJM_SUP_PATMODE_4 +#JSSMOD_FLAGS += -DJM_SUP_PATMODE_5 + +# Enable ext. instrument, instrument header and sampledata loading: +# (if you disable these, you better know what you are doing.) +JSSMOD_FLAGS += -DJM_SUP_EXT_INSTR +JSSMOD_FLAGS += -DJM_SUP_INSTR +JSSMOD_FLAGS += -DJM_SUP_SAMPLES + + +### +### Locking/thread support +### +JSS_SUP_THREADS=yes diff -r 000000000000 -r 32250b436bca dmargs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmargs.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,12 @@ +#include "dmargs.h" +#define TH_EXTERNAL 1 +#define optarg_t DMOptArg +#define th_args_process dmArgsProcess +#define th_args_help dmArgsPrintHelp + +#define THERR dmError +#define th_calloc dmCalloc +#define th_malloc dmMalloc +#define th_free dmFree + +#include "dmargs_int.c" diff -r 000000000000 -r 32250b436bca dmargs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmargs.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,44 @@ +/* + * Simple commandline argument processing functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2002-2008 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#ifndef DMARGS_H +#define DMARGS_H + +#include "dmlib.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Option flags + */ +#define OPT_NONE (0) // Simple option with no arguments +#define OPT_ARGREQ (1) // Option's argument is required +#define OPT_ARGOPT (3) // Option's argument is optional +#define OPT_ARGMASK (3) // Mask for option argument flags +#define OPT_REQUIRED (4) // This option is required to be given + +typedef struct { + int id; + char optShort; + char *optLong; + char *desc; + int flags; +} DMOptArg; + +BOOL dmArgsProcess(int argc, char *argv[], + DMOptArg argList[], int argListN, + BOOL (*handleOpt)(int, char *, char *), + BOOL (*handleFile)(char *), BOOL); + +void dmArgsPrintHelp(FILE *, DMOptArg optList[], int optListN); + +#ifdef __cplusplus +} +#endif +#endif // DMARGS_H diff -r 000000000000 -r 32250b436bca dmargs_int.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmargs_int.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,418 @@ +/* + * Simple commandline argument processing + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2002-2008 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +/* +Description +=========== +Structures and functions for simple commandline argument processing, +option argument handling (short and long forms supported). + +Output function for printing out short on-line help for said options +and program commandline usage. + + +Format for typical commandline: + +$ program -a -b -c --long-d -e argument-for-e --long-f="argument for f" + +where -a, -b and -c are short options without required arguments; +--long-d is a long option without argument; -e is short option with +argument and finally --long-f is long option with argument. + + +Introduction +============ +Handling of commandline options in th_args_process() has various +options, which effect how an option is handled. Also the error +situations (unknown option/no required argument) can be handled +in different ways. + +Typically th_args_process() is called as follows: + +BOOL optionHandlerCallback(int optionNumber, char *optionArgument, char *optionName) +{ + if (option is OK) + return TRUE; + else + return FALSE; +} + +BOOL nonoptionHandlerCallback(char *argumentString) +{ + // return value same as in optionHandlerCallback +} + + +int main(int argc, char *argv[]) +{ + if (th_args_process(argc, argv, optList, optListN, + optionHandlerCallback, nonoptionHandlerCallback)) { + ... arguments OK ... + } else { + ... arguments invalid or required arguments missing ... + } +} + + +NOTICE! +------- +The return value from handler callbacks affects the return value of +th_args_process(). Additionally, a failure in callback (returns FALSE) +effects the argument processing if bailOut argument of th_args_process() +is TRUE! + +If bailOut is TRUE, any error/failure in argument processing (including +callbacks) immediately stops the argument processing and FALSE is +returned from th_args_process(). + +If bailOut is FALSE, most errors are "ignored", but FALSE is still returned +if any errors occured. + + +NOTICE #2! +---------- +A small temporary buffer of N*sizeof(BOOL) (where N is number of +options in optList[]) is allocated for processing required options. +If this allocation fails, the program is immediately exited with +code 128. + + +Examples +======== +Example of different options, in a fictional optionlist struct: + +optarg_t optList[] = { + // Option without arguments + { 0, '?', "help", "Show this help", OPT_NONE }, + + // Option with a required argument + { 1, 'o', "output", "Output file name", OPT_ARGREQ }, + + // Option with optional argument + { 2, 'f', "foobar", "Use foobar, with optional string", OPT_ARGOPT }, + + // This option is required to be given, though without other flags + // it may not make much sense. + { 4, 'S', "stupid", "You must give this option", OPT_REQUIRED }, + + // The flags can be combined with OR operator: this option is both + // required to be specified, and also requires argument (the filename) + { 5, 'i', "input", "Input file name", OPT_REQUIRED | OPT_ARGREQ }, + + + // Option with only long form + { 0, 0, "only-long", "Long option", OPT_NONE }, + + // Option with only short form + { 0, 's', NULL, "Short option", OPT_NONE }, + +}; + +const int optListN = (sizeof(optList) / sizeof(optarg_t)); + + +*/ +#ifndef TH_EXTERNAL +#include "th_util.h" +#include "th_args.h" +#include "th_string.h" +#endif + + +/* Check if option requires an argument + */ +static BOOL th_args_check_arg(const optarg_t *o, const char *optArg) +{ + if ((o->flags & OPT_ARGMASK) == OPT_ARGREQ && optArg == NULL) + { + if (o->optShort != 0 && o->optLong != NULL) + { + THERR("Option '-%c ARG' (--%s=ARG) requires an argument!\n", + o->optShort, o->optLong); + } + else if (o->optShort != 0) + { + THERR("Option '-%c ARG' requires an argument!\n", o->optShort); + } + else if (o->optLong != NULL) + { + THERR("Option --%s=ARG requires an argument!\n", o->optLong); + } + + return FALSE; + } + else + return TRUE; +} + + +/* Handle short options + */ +static BOOL th_args_process_short(char *currArg, int *newArgIndex, + BOOL *wasGiven, int argc, char *argv[], + optarg_t optList[], int optListN, + BOOL (*handleOpt)(int, char *, char *)) +{ + char *tmpArg = currArg, *optArg; + int optN; + BOOL isFound; + + // Short options can be combined: -a -b -c == -abc + while (*tmpArg) + { + for (optN = 0, isFound = FALSE; (optN < optListN) && !isFound; optN++) + if (*tmpArg == optList[optN].optShort) + { + // Get possible option argument, if needed + if ((optList[optN].flags & OPT_ARGMASK) != 0 + && (++(*newArgIndex) < argc)) + optArg = argv[*newArgIndex]; + else + optArg = NULL; + + // Check if option argument is required + if (!th_args_check_arg(&optList[optN], optArg)) + return FALSE; + else + { + char tmpStr[2] = { 0, 0 }; + + // Option was given succesfully, try to handle it + wasGiven[optN] = TRUE; + + tmpStr[0] = *tmpArg; + + if (!handleOpt(optList[optN].id, optArg, tmpStr)) + return FALSE; + } + + isFound = TRUE; + } + + if (!isFound) + { + THERR("Unknown short option '%c' in argument '-%s'\n", + *tmpArg, currArg); + return FALSE; + } + + tmpArg++; + } + + return TRUE; +} + + +/* Handle long options + */ +static BOOL th_args_process_long(char *currArg, int *newArgIndex, + BOOL *wasGiven, int argc, char *argv[], + optarg_t optList[], int optListN, + BOOL (*handleOpt)(int, char *, char *)) +{ + int optN, optLen, i; + char *optArg; + + (void) argc; + (void) argv; + (void) newArgIndex; + + // Long option + for (optN = -1, optLen = i = 0; (i < optListN) && (optN < 0); i++) + if (optList[i].optLong) + { + optLen = strlen(optList[i].optLong); + if (strncmp(currArg, optList[i].optLong, optLen) == 0) + optN = i; + } + + // Get possible option argument, if needed + if (optN >= 0) + { + if ((optList[optN].flags & OPT_ARGMASK) != 0) + { + if (currArg[optLen] == '=') + optArg = &currArg[optLen + 1]; + else + optArg = NULL; + } + else + optArg = NULL; + + // Check if option argument is required + if (!th_args_check_arg(&optList[optN], optArg)) + return FALSE; + else + { + // Option was given succesfully, try to handle it + wasGiven[optN] = TRUE; + if (!handleOpt(optList[optN].id, optArg, currArg)) + return FALSE; + } + } + else + { + THERR("Unknown long option '--%s'\n", currArg); + return FALSE; + } + + return TRUE; +} + + +/* Process arguments, handling short and long options by + * calling the given callback functions. + */ +BOOL th_args_process(int argc, char *argv[], + optarg_t optList[], int optListN, + BOOL(*handleOpt) (int, char *, char *), + BOOL(*handleNonOption) (char *), BOOL bailOut) +{ + BOOL endOptions, optionsOK; + int argIndex, newArgIndex, i; + char *currArg; + BOOL *wasGiven; + + // Allocate wasGiven + wasGiven = (BOOL *) th_calloc(optListN, sizeof(BOOL)); + if (!wasGiven) + { + THERR("FATAL ERROR! Could not allocate wasGiven in th_args_process()!\n"); + exit(128); + } + + // Parse arguments + argIndex = 1; + optionsOK = TRUE; + endOptions = FALSE; + while (argIndex < argc) + { + currArg = argv[argIndex]; + if ((currArg[0] == '-') && !endOptions) + { + newArgIndex = argIndex; + currArg++; + if (*currArg == '-') + { + // Check for "--", which ends the options-list + currArg++; + if (*currArg == 0) + { + endOptions = TRUE; + continue; + } + + // Long options + if (!th_args_process_long(currArg, &newArgIndex, + wasGiven, argc, argv, optList, + optListN, handleOpt)) + optionsOK = FALSE; + } + else + { + // Short options + if (!th_args_process_short(currArg, &newArgIndex, + wasGiven, argc, argv, optList, + optListN, handleOpt)) + optionsOK = FALSE; + } + + argIndex = newArgIndex; + } + else + { + // Was not option argument + if (handleNonOption == NULL + || (handleNonOption != NULL && !handleNonOption(currArg))) + { + THERR("Invalid argument '%s'\n", currArg); + optionsOK = FALSE; + } + } + + // Check if we bail out on invalid argument + if (!optionsOK && bailOut) + { + th_free(wasGiven); + return FALSE; + } + + argIndex++; + } + + // Check wasGiven by isRequired + for (i = 0; i < optListN; i++) + if ((optList[i].flags & OPT_REQUIRED) != 0 && !wasGiven[i]) + { + THERR("Option -%s (--%s) is required.\n", + optList[i].optShort, optList[i].optLong); + + optionsOK = FALSE; + if (bailOut) + break; + } + + th_free(wasGiven); + return optionsOK; +} + + +/* Print help for commandline arguments/options + */ +void th_args_help(FILE *outFile, optarg_t optList[], int optListN) +{ + int i, nrequired; + + for (i = nrequired = 0; i < optListN; i++) + { + optarg_t *o = &optList[i]; + + // Print short option + if (o->optShort != 0) + fprintf(outFile, " -%c, ", o->optShort); + else + fprintf(outFile, " "); + + // Print long option + if (o->optLong) + { + char tmpStr[64], *p; + + if ((o->flags & OPT_ARGMASK) == OPT_ARGOPT) + { + snprintf(tmpStr, sizeof(tmpStr), "%s[=ARG]", + optList[i].optLong); + p = tmpStr; + } + else if ((o->flags & OPT_ARGMASK) == OPT_ARGREQ) + { + snprintf(tmpStr, sizeof(tmpStr), "%s=ARG", + optList[i].optLong); + p = tmpStr; + } + else + p = o->optLong; + + fprintf(outFile, "--%-15s", p); + } + else + fprintf(outFile, " "); + + fprintf(outFile, " %s.", optList[i].desc); + + if (o->flags & OPT_REQUIRED) + { + fprintf(outFile, " [*]\n"); + nrequired++; + } + else + fprintf(outFile, "\n"); + } + + if (nrequired > 0) + fprintf(outFile, "(Options marked with [*] are required)\n"); +} diff -r 000000000000 -r 32250b436bca dmblit.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmblit.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,134 @@ +/* + * DMLib + * -- Sprite / surface blitting functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#include "dmlib.h" + +//#define DM_CLIP_DEBUG + +typedef struct +{ + int v0, v1, voffs, vadd; + DMFixedPoint32 vdelta; +} DMQValue; + + +int dmClipCoord(DMQValue *pv, int v0, int v1, int v2, int clipMin, int clipMax) +{ + DMFixedPoint32 a, b; + + // Basic bounds check + if (v1 < 1 || v2 < 1 || v0 + v2 < clipMin || v0 >= clipMax) + { +#ifdef DM_CLIPDEBUG + printf("out of bounds\n"); +#endif + return -1; + } + + // Calculate delta + FP_SETHL(a, v1, 0); + FP_CONV(b, v2); + FP_DIV_R(pv->vdelta, a, b); + + // Perform clipping + if (v0 + v2 >= clipMax) + { + pv->vadd = v0; + pv->v1 = clipMax; + } + else + { + pv->vadd = clipMax - v2; + pv->v1 = v0 + v2; + } + + if (v0 < clipMin) + { + pv->voffs = clipMin - v0; + pv->v0 = clipMin; + pv->vadd -= v0 + clipMin; + } + else + { + pv->voffs = 0; + pv->v0 = v0; + } + +#ifdef DM_CLIP_DEBUG + printf("dmClipCoord(%d, (%d, %d), [%d, %d]): vdelta=", + v0, v1, v2, clipMin, clipMax); + FP_PRINTF(pv->vdelta); + printf(", v0=%d, v1=%d, voffs=%d, vadd=%d\n", + pv->v0, pv->v1, pv->voffs, pv->vadd); +#endif + + return 0; +} + +#include "dmblitfunc.h" + +static const DMScaledBlitFunc dmScaledBlitTable[DMD_NMODES][DMD_NBITDEPTHS][DMD_NBITDEPTHS] = +{ + // DMD_NONE + { + { dmScaledBlitSurface8to8 , dmScaledBlitSurface8to32 }, + { NULL , dmScaledBlitSurface32to32 }, + }, + // DMD_TRANSPARENT + { + { dmScaledBlitSurface8to8Transparent , dmScaledBlitSurface8to32Transparent }, + { NULL , dmScaledBlitSurface32to32Transparent }, + }, + // DMD_SATURATE + { + { dmScaledBlitSurface8to8Saturate , dmScaledBlitSurface8to32Saturate }, + { NULL , dmScaledBlitSurface32to32Saturate }, + }, +#if 0 + // DMD_NONE | DMD_ANTIALIAS + { + { dmScaledBlitSurface8to8Antialias , dmScaledBlitSurface8to32Antialias }, + { NULL , dmScaledBlitSurface32to32Antialias }, + }, + // DMD_TRANSPARENT | DMD_ANTIALIAS + { + { dmScaledBlitSurface8to8AATransp , dmScaledBlitSurface8to32AATransparent }, + { NULL , dmScaledBlitSurface32to32AATransparent }, + }, + // DMD_SATURATE | DMD_ANTIALIAS + { + { dmScaledBlitSurface8to8AASaturate , dmScaledBlitSurface8to32AASaturate }, + { NULL , dmScaledBlitSurface32to32AASaturate }, + }, +#endif +}; + +static const int ndmScaledBlitTable = sizeof(dmScaledBlitTable) / sizeof(dmScaledBlitTable[0]); + +DMScaledBlitFunc dmGetScaledBlitFunc(SDL_PixelFormat *src, SDL_PixelFormat *dst, int mode) +{ + int isrc, idst; + if (src == NULL || dst == NULL || mode < 0 || mode >= ndmScaledBlitTable) + return NULL; + + isrc = dmBitsPerPixel2Index(src->BitsPerPixel); + idst = dmBitsPerPixel2Index(dst->BitsPerPixel); + if (isrc < 0 || idst < 0) + return NULL; + + return dmScaledBlitTable[mode][isrc][idst]; +} + + +int dmScaledBlitSurfaceAny(SDL_Surface *src, const int x0, const int y0, const int dwidth, const int dheight, SDL_Surface *dst, int mode) +{ + DMScaledBlitFunc bfunc = dmGetScaledBlitFunc(src->format, dst->format, mode); + + if (bfunc == NULL) + return -15; + + return bfunc(src, x0, y0, dwidth, dheight, dst); +} diff -r 000000000000 -r 32250b436bca dmblitfunc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmblitfunc.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,251 @@ +/* + * DMLib + * -- Sprite / surface blitting functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011-2012 Tecnic Software productions (TNSP) + */ + +// ======================================================================= +// DMD_NONE +// ======================================================================= + +#define DM_SCALED_NAME dmScaledBlitSurface8to8 +#define DM_SCALED_SRC_BYTES 1 +#define DM_SCALED_DST_BYTES 1 +#define DM_SCALED_SRC_TYPE Uint8 +#define DM_SCALED_DST_TYPE Uint8 + +#define DM_SCALED_INNER *dp++ = sp[FP_GETH(xv)]; + +#include "dmscaledblit.h" + +// ----------------------------------------------------------------------- + +#define DM_SCALED_NAME dmScaledBlitSurface8to32 +#define DM_SCALED_SRC_BYTES 1 +#define DM_SCALED_DST_BYTES 4 +#define DM_SCALED_SRC_TYPE Uint8 +#define DM_SCALED_DST_TYPE Uint32 + +#define DM_SCALED_VARS const Uint32 *pal; +#define DM_SCALED_INIT \ + if (src->format->palette == NULL || src->format->palette->ncolors < 256) return -2; \ + pal = (Uint32 *) src->format->palette->colors; + +#define DM_SCALED_INNER *dp++ = pal[sp[FP_GETH(xv)]]; + +#include "dmscaledblit.h" + +// ----------------------------------------------------------------------- + +#define DM_SCALED_NAME dmScaledBlitSurface32to32 +#define DM_SCALED_SRC_BYTES 4 +#define DM_SCALED_DST_BYTES 4 +#define DM_SCALED_SRC_TYPE Uint32 +#define DM_SCALED_DST_TYPE Uint32 +#define DM_SCALED_INIT + +#define DM_SCALED_INNER *dp++ = sp[FP_GETH(xv)]; + +#include "dmscaledblit.h" + + +// ======================================================================= +// DMD_TRANSPARENT +// ======================================================================= + +#define DM_SCALED_NAME dmScaledBlitSurface8to8Transparent +#define DM_SCALED_SRC_BYTES 1 +#define DM_SCALED_DST_BYTES 1 +#define DM_SCALED_SRC_TYPE Uint8 +#define DM_SCALED_DST_TYPE Uint8 + +#define DM_SCALED_INNER \ + *dp = sp[FP_GETH(xv)] ? sp[FP_GETH(xv)] : *dp; dp++; + +#include "dmscaledblit.h" + +// ----------------------------------------------------------------------- + +#define DM_SCALED_NAME dmScaledBlitSurface8to32Transparent +#define DM_SCALED_SRC_BYTES 1 +#define DM_SCALED_DST_BYTES 4 +#define DM_SCALED_SRC_TYPE Uint8 +#define DM_SCALED_DST_TYPE DMRGBA32 +#define DM_SCALED_VARS const DMRGBA32 *pal; +#define DM_SCALED_INIT \ + if (src->format->palette == NULL || src->format->palette->ncolors < 256) return -2; \ + pal = (DMRGBA32 *) src->format->palette->colors; + +#define DM_SCALED_INNER \ + const DMRGBA32 q = pal[sp[FP_GETH(xv)]]; \ + dp->r += ((q.r - dp->r) * q.a) >> 8; \ + dp->g += ((q.g - dp->g) * q.a) >> 8; \ + dp->b += ((q.b - dp->b) * q.a) >> 8; \ + dp->a = q.a; \ + dp++; + +#include "dmscaledblit.h" + + +// ----------------------------------------------------------------------- + +#define DM_SCALED_NAME dmScaledBlitSurface32to32Transparent +#define DM_SCALED_SRC_BYTES 4 +#define DM_SCALED_DST_BYTES 4 +#define DM_SCALED_INIT + +#ifdef DM_USE_SIMD +#define DM_SCALED_INNER_INIT \ + const Uint32 qpdmask = 0x000000ff; + +#define DM_SCALED_SRC_TYPE Uint32 +#define DM_SCALED_DST_TYPE Uint32 +#define DM_SCALED_INNER \ + asm( \ + "movd %2, %%mm1\n" \ + "movq %%mm1, %%mm3\n" \ + "movd %3, %%mm5\n" \ + "pand %%mm5, %%mm3\n" \ + "movq %%mm3, %%mm4\n" \ + "psllq $8, %%mm4\n" \ + "por %%mm4, %%mm3\n" \ + "psllq $16, %%mm3\n" \ + "por %%mm3, %%mm4\n" \ + \ + "pxor %%mm2, %%mm2\n" \ + "punpcklbw %%mm2, %%mm4\n" \ + \ + "movd %1, %%mm3\n" \ + "punpcklbw %%mm2, %%mm1\n" \ + "punpcklbw %%mm2, %%mm3\n" \ + "psubw %%mm3, %%mm1\n" \ + "pmullw %%mm4, %%mm1\n" \ + "psrlw $8, %%mm1\n" \ + "packuswb %%mm2, %%mm1\n" \ + "movd %%mm1, %0\n" \ + : "=m" (*dp) \ + : "m" (*dp), "m" (sp[FP_GETH(xv)]), "m" (qpdmask) \ + : "memory" ); dp++; + +#define DM_SCALED_FINISH asm("emms\n"); +#else +#define DM_SCALED_SRC_TYPE DMRGBA32 +#define DM_SCALED_DST_TYPE DMRGBA32 +#define DM_SCALED_INNER \ + const DMRGBA32 q = sp[FP_GETH(xv)]; \ + dp->r += ((q.r - dp->r) * q.a) >> 8; \ + dp->g += ((q.g - dp->g) * q.a) >> 8; \ + dp->b += ((q.b - dp->b) * q.a) >> 8; \ + dp->a = q.a; \ + dp++; +#endif + +#include "dmscaledblit.h" + +// ----------------------------------------------------------------------- + +#define DM_SCALED_NAME dmScaledBlitSurface32to32TransparentX +#define DM_SCALED_SRC_BYTES 4 +#define DM_SCALED_DST_BYTES 4 +#define DM_SCALED_SRC_TYPE DMRGBA32 +#define DM_SCALED_DST_TYPE DMRGBA32 +#define DM_SCALED_INIT + +#define DM_SCALED_INNER \ + const DMRGBA32 q = sp[FP_GETH(xv)]; \ + dp->r = (q.r * q.a + dp->r * dp->a) >> 9; \ + dp->g = (q.g * q.a + dp->g * dp->a) >> 9; \ + dp->b = (q.b * q.a + dp->b * dp->a) >> 9; \ + dp->a = q.a ? q.a : dp->a; \ + dp++; + +#include "dmscaledblit.h" + + +// ======================================================================= +// DMD_SATURATE +// ======================================================================= + +#define DM_SCALED_NAME dmScaledBlitSurface8to8Saturate +#define DM_SCALED_SRC_BYTES 1 +#define DM_SCALED_DST_BYTES 1 +#define DM_SCALED_SRC_TYPE Uint8 +#define DM_SCALED_DST_TYPE Uint8 +#define DM_SCALED_INNER *(dp++) += sp[FP_GETH(xv)]; +#include "dmscaledblit.h" + +// ----------------------------------------------------------------------- + +#define DM_SCALED_NAME dmScaledBlitSurface8to32Saturate +#define DM_SCALED_SRC_BYTES 1 +#define DM_SCALED_DST_BYTES 4 +#define DM_SCALED_SRC_TYPE Uint8 +#define DM_SCALED_DST_TYPE DMRGBA32 +#define DM_SCALED_VARS const DMRGBA32 *pal; +#define DM_SCALED_INIT \ + if (src->format->palette == NULL || src->format->palette->ncolors < 256) return -2; \ + pal = (DMRGBA32 *) src->format->palette->colors; + +#ifdef DM_USE_SIMD +#define DM_SCALED_INNER \ + asm("movd %2, %%mm1\n" \ + "movd %1, %%mm2\n" \ + "paddusb %%mm2, %%mm1\n" \ + "movd %%mm1, %0\n" \ + : "=m" (*dp) \ + : "m" (*dp), "m" (pal[sp[FP_GETH(xv)]]) \ + : "memory" ); dp++; + +#define DM_SCALED_FINISH asm("emms\n"); +#else +#define DM_SCALED_INNER \ + const DMRGBA32 q = pal[sp[FP_GETH(xv)]]; \ + const int qr = dp->r + q.r, qg = dp->g + q.g, qb = dp->b + q.b; \ + dp->r = qr < 256 ? qr : 255; \ + dp->g = qg < 256 ? qg : 255; \ + dp->b = qb < 256 ? qb : 255; \ + dp->a = q.a; \ + dp++; +#endif + +#include "dmscaledblit.h" + +// ----------------------------------------------------------------------- + +#define DM_SCALED_NAME dmScaledBlitSurface32to32Saturate +#define DM_SCALED_SRC_BYTES 4 +#define DM_SCALED_DST_BYTES 4 +#define DM_SCALED_INIT + +#ifdef DM_USE_SIMD +#define DM_SCALED_SRC_TYPE Uint32 +#define DM_SCALED_DST_TYPE Uint32 +#define DM_SCALED_INNER \ + asm("movd %2, %%mm1\n" \ + "movd %1, %%mm2\n" \ + "paddusb %%mm2, %%mm1\n" \ + "movd %%mm1, %0\n" \ + : "=m" (*dp) \ + : "m" (*dp), "m" (sp[FP_GETH(xv)]) \ + : "memory" ); dp++; + +#define DM_SCALED_FINISH asm("emms\n"); +#else +#define DM_SCALED_SRC_TYPE DMRGBA32 +#define DM_SCALED_DST_TYPE DMRGBA32 +#define DM_SCALED_INNER \ + const DMRGBA32 q = sp[FP_GETH(xv)]; \ + const int qr = dp->r + q.r, qg = dp->g + q.g, qb = dp->b + q.b; \ + dp->r = qr < 256 ? qr : 255; \ + dp->g = qg < 256 ? qg : 255; \ + dp->b = qb < 256 ? qb : 255; \ + dp->a = q.a; \ + dp++; +#endif + +#include "dmscaledblit.h" + + +// ======================================================================= +// ======================================================================= diff -r 000000000000 -r 32250b436bca dmdrawline.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmdrawline.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,86 @@ + +#ifdef DM_USE_SIMD +static int DM_DRAWLINE_NAME_ASM (SDL_Surface *screen, int x0, int y0, int x1, int y1, const Uint32 col); +#endif + +#ifdef DM_USE_C +static int DM_DRAWLINE_NAME_C (SDL_Surface *screen, int x0, int y0, int x1, int y1, const Uint32 col) +{ + int dx, dy, xstep, ystep; + const int qpitch = screen->pitch / DM_DRAWLINE_DST_BYTES; + + // Clipping + if (dmClipLineCoords(&x0, &y0, &x1, &y1, screen)) + return -1; + + // Compute initial deltas + dx = (x1 - x0) * 2; + dy = (y1 - y0) * 2; + + if (dx < 0) + { + dx = -dx; + xstep = -1; + } + else + xstep = 1; + + if (dy < 0) + { + dy = -dy; + ystep = -qpitch; + } + else + ystep = qpitch; + + // Compute offsets + y0 *= qpitch; + y1 *= qpitch; + + DM_DRAWLINE_DST_TYPE *pix = screen->pixels; + + // Continue based on which delta is larger + if (dx > dy) + { + int afrac = dy - (dx / 2); + while (x0 != x1) + { + if (afrac >= 0) + { + y0 += ystep; + afrac -= dx; + } + + x0 += xstep; + afrac += dy; + + DM_DRAWLINE_INNER + } + } + else + { + int afrac = dx - (dy / 2); + while (y0 != y1) + { + if (afrac >= 0) + { + x0 += xstep; + afrac -= dy; + } + + y0 += ystep; + afrac += dx; + + DM_DRAWLINE_INNER + } + } + + return 0; +} +#endif + +#undef DM_DRAWLINE_NAME +#undef DM_DRAWLINE_NAME_ASM +#undef DM_DRAWLINE_NAME_C +#undef DM_DRAWLINE_DST_BYTES +#undef DM_DRAWLINE_DST_TYPE diff -r 000000000000 -r 32250b436bca dmeval.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmeval.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,862 @@ +#include "dmeval.h" +#include +#include + +#define DM_MAX_ID_LEN 16 +#define DM_MAX_BUF 512 +#define DM_STACK_SIZE 512 + +static DMValue int_clip(DMValue v) +{ + return (v < -1.0f) ? -1.0f : ((v > 1.0f) ? 1.0f : v); +} + + +/* Some basic functions + */ +static const DMEvalId dm_eval_basic[] = +{ + { "sin", ID_FUNC, sin, NULL }, + { "cos", ID_FUNC, cos, NULL }, + { "clip", ID_FUNC, int_clip, NULL }, +}; + +static const int ndm_eval_basic = sizeof(dm_eval_basic) / sizeof(dm_eval_basic[0]); + + +void dm_eval_err_v(DMEvalContext *ev, const char *fmt, va_list ap) +{ +#if 1 + char *tmp = dm_strdup_vprintf(fmt, ap); + + ev->err = TRUE; + + if (ev->errStr != NULL) + { + ev->errStr = dm_strdup_printf("%s%s", ev->errStr, tmp); + dmFree(tmp); + } + else + ev->errStr = tmp; +#else + vfprintf(stdout, fmt, ap); +#endif +} + + +void dm_eval_err(DMEvalContext *ev, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + dm_eval_err_v(ev, fmt, ap); + va_end(ap); +} + + +DMEvalId *dm_eval_find_id(DMEvalContext *ev, const char *name) +{ + int i; + if (ev->ids == NULL) + return NULL; + + for (i = 0; i < ev->nids; i++) + if (strcmp(ev->ids[i].name, name) == 0) + return &(ev->ids[i]); + + return NULL; +} + + +static DMEvalId *dm_eval_add_id(DMEvalContext *ev, const char *name, const int type) +{ + DMEvalId *id = dm_eval_find_id(ev, name); + if (id != NULL) + return NULL; + + ev->ids = dmRealloc(ev->ids, sizeof(DMEvalId) * (ev->nids + 1)); + if (ev->ids == NULL) + { + dm_eval_err(ev, "Could not reallocate eval ids array (#%d). Fatal error.\n", ev->nids + 1); + return NULL; + } + + id = &(ev->ids[ev->nids]); + ev->nids++; + + id->name = dm_strdup(name); + id->type = type; + id->func = NULL; + id->var = NULL; + + return id; +} + + +DMEvalId *dm_eval_add_var(DMEvalContext *ev, const char *name, DMValue *var) +{ + DMEvalId *id = dm_eval_add_id(ev, name, ID_VAR); + if (id == NULL) + return NULL; + + id->var = var; + return id; +} + + +DMEvalId *dm_eval_add_func(DMEvalContext *ev, const char *name, DMValue (*func)(DMValue)) +{ + DMEvalId *id = dm_eval_add_id(ev, name, ID_VAR); + if (id == NULL) + return NULL; + + id->func = func; + return id; +} + + + +DMEvalContext *dm_eval_new(void) +{ + int i; + DMEvalContext *ev = dmCalloc(1, sizeof(DMEvalContext)); + + if (ev == NULL) + return NULL; + + for (i = 0; i < ndm_eval_basic; i++) + { + const DMEvalId *id = &dm_eval_basic[i]; + DMEvalId *nid = dm_eval_add_id(ev, id->name, id->type); + nid->func = id->func; + } + + return ev; +} + +void dm_eval_free(DMEvalNode *node) +{ + while (node != NULL) + { + DMEvalNode *next = node->next; + + if (node->subexpr != NULL) + dm_eval_free(node->subexpr); + + dmFree(node); + node = next; + } +} + + +void dm_eval_clear_err(DMEvalContext *ev) +{ + if (ev == NULL) + return; + + dmFree(ev->errStr); + ev->err = FALSE; + ev->errStr = NULL; +} + + +void dm_eval_close(DMEvalContext *ev) +{ + int i; + + if (ev == NULL) + return; + + for (i = 0; i < ev->nids; i++) + dmFree(ev->ids[i].name); + + dmFree(ev->ids); + dm_eval_clear_err(ev); + dmFree(ev); +} + + +static DMEvalNode *dm_eval_pop_node(DMEvalNode **list) +{ + DMEvalNode *node; + + if (*list == NULL) + return NULL; + + node = (*list)->prev; + + if (*list != node) + { + node->prev->next = NULL; + (*list)->prev = node->prev; + } + else + *list = NULL; + + return node; +} + + +static DMEvalNode *dm_eval_insert_node(DMEvalNode **list, DMEvalNode *node) +{ + if (*list != NULL) + { + node->prev = (*list)->prev; + (*list)->prev->next = node; + (*list)->prev = node; + } + else + { + *list = node; + node->prev = *list; + } + + node->next = NULL; + return node; +} + + +static DMEvalNode *dm_eval_push_node(DMEvalNode **list, DMEvalNode *src) +{ + DMEvalNode *node = dmCalloc(1, sizeof(DMEvalNode)); + if (node == NULL) + return NULL; + + node->op = src->op; + node->val = src->val; + node->id = src->id; + node->subexpr = src->subexpr; + + return dm_eval_insert_node(list, node); +} + + +static DMEvalNode *dm_eval_add_node(DMEvalNode **list, const int op) +{ + DMEvalNode *node = dmCalloc(1, sizeof(DMEvalNode)); + if (node == NULL) + return NULL; + + node->op = op; + + return dm_eval_insert_node(list, node); +} + + +enum +{ + PARSE_NONE = 0x0000, + PARSE_START = 0x1000, + PARSE_END = 0x2000, + PARSE_ERROR = 0x8000, + + PARSE_IDENT = 0x0001, + PARSE_WS = 0x0002, + PARSE_CONST = 0x0004, + PARSE_OPER = 0x0008, + PARSE_SUBEXPR = 0x0010, +}; + +#define DM_CHECK(x) { if (mode & PARSE_ ## x ) { if (str[0]) strcat(str, " or "); strcat(str, # x ); } } + +static char *dm_get_mode(int mode) +{ + char str[128] = ""; + + + DM_CHECK(START); + DM_CHECK(END); + DM_CHECK(IDENT); + DM_CHECK(WS); + DM_CHECK(CONST); + DM_CHECK(OPER); + DM_CHECK(SUBEXPR); + + return dm_strdup(str); +} + + +static void dm_set_mode(DMEvalContext *ev, const int mode) +{ + if (mode != PARSE_ERROR && mode != PARSE_START && + ev->expect != PARSE_NONE && + (mode & ev->expect) == 0) + { + char *tmp1 = dm_get_mode(ev->expect), + *tmp2 = dm_get_mode(mode); + + dm_eval_err(ev, "Expected [%s], got %s.\n", tmp1, tmp2); + dmFree(tmp1); + dmFree(tmp2); + } + + ev->prev = ev->mode; + ev->mode = mode; +} + + +static int dm_eval_parse_expr_do(DMEvalContext *ev, DMEvalNode **list, char **str, int depth) +{ + char *c = *str; + char tmpStr[DM_MAX_BUF + 2]; + int tmpStrLen = 0; + DMEvalNode *node = NULL, *func = NULL; + BOOL first = FALSE, decimal = FALSE; + + ev->expect = PARSE_CONST | PARSE_IDENT | PARSE_SUBEXPR; + ev->mode = PARSE_START; + + while (ev->mode != PARSE_ERROR && ev->mode != PARSE_END) + { + switch (ev->mode) + { + case PARSE_WS: + // Skip whitespace + if (isspace(*c)) + c++; + else + dm_set_mode(ev, PARSE_START); + break; + + case PARSE_SUBEXPR: + { + char *tmp = c + 1; + + ev->expect = PARSE_SUBEXPR | PARSE_IDENT | PARSE_CONST; + + if (func != NULL) + { + if (dm_eval_parse_expr_do(ev, &(func->subexpr), &tmp, depth + 1) != 0) + { + dm_eval_err(ev, "Function argument subexpression starting at '%s' contained errors.\n", c); + dm_set_mode(ev, PARSE_ERROR); + } + + func = NULL; + } + else + { + if ((node = dm_eval_add_node(list, OP_SUBEXPR)) == NULL) + dm_set_mode(ev, PARSE_ERROR); + else + if (dm_eval_parse_expr_do(ev, &(node->subexpr), &tmp, depth + 1) != 0) + { + dm_eval_err(ev, "Subexpression starting at '%s' contained errors.\n", c); + dm_set_mode(ev, PARSE_ERROR); + } + } + if (ev->mode != PARSE_ERROR) + { + dm_set_mode(ev, PARSE_START); + ev->expect = PARSE_OPER | PARSE_END; + c = tmp; + } + } + break; + + case PARSE_START: + // Start + if (*c == 0) + dm_set_mode(ev, PARSE_END); + + else if (isspace(*c)) + { + ev->mode = PARSE_WS; + } + + else if (*c == ')') + { + if (depth > 0) + dm_set_mode(ev, PARSE_END); + else + { + dm_eval_err(ev, "Invalid nesting near '%s' (depth %d).\n", c, depth); + dm_set_mode(ev, PARSE_ERROR); + } + c++; + } + + else if (*c == '(') + dm_set_mode(ev, PARSE_SUBEXPR); + + else if (strchr("+-*/<>%&|!^", *c)) + dm_set_mode(ev, PARSE_OPER); + + else if (isdigit(*c) || *c == '.') + dm_set_mode(ev, PARSE_CONST); + + else if (isalpha(*c) || *c == '_') + dm_set_mode(ev, PARSE_IDENT); + + else + { + dm_eval_err(ev, "Syntax error near '%s' (depth %d).\n", c, depth); + dm_set_mode(ev, PARSE_ERROR); + } + + first = TRUE; + break; + + case PARSE_CONST: + if (first) + { + first = FALSE; + decimal = FALSE; + tmpStrLen = 0; + + if (isdigit(*c) || *c == '-' || *c == '+' || *c == '.') + { + if (*c == '.') + decimal = TRUE; + tmpStr[tmpStrLen++] = *c++; + } + else + { + dm_eval_err(ev, "Invalid constant expression near '%s'.\n", c); + dm_set_mode(ev, PARSE_ERROR); + } + } + else + { + if (isdigit(*c) || (*c == '.' && !decimal)) + { + if (*c == '.') + decimal = TRUE; + tmpStr[tmpStrLen++] = *c++; + } + else + { + tmpStr[tmpStrLen] = 0; + + if ((node = dm_eval_add_node(list, OP_CONST)) == NULL) + { + dm_set_mode(ev, PARSE_ERROR); + break; + } + + node->val = atof(tmpStr); + dm_set_mode(ev, PARSE_START); + ev->expect = PARSE_OPER | PARSE_END; + } + } + break; + + case PARSE_OPER: + { + int op = OP_NONE; + + switch (*c) + { + case '+': op = OP_ADD; c++; break; + case '-': op = OP_SUB; c++; break; + case '*': op = OP_MUL; c++; break; + case '/': op = OP_DIV; c++; break; + case '%': op = OP_MOD; c++; break; + case '&': op = OP_AND; c++; break; + case '|': op = OP_OR; c++; break; + case '>': + if (c[1] == '>') + { + c += 2; + op = OP_RSHIFT; + } + else + { + dm_eval_err(ev, "Syntax error near '%s'.\n", c); + dm_set_mode(ev, PARSE_ERROR); + } + break; + + case '<': + if (c[1] == '<') + { + c += 2; + op = OP_LSHIFT; + } + else + { + dm_eval_err(ev, "Syntax error near '%s'.\n", c); + dm_set_mode(ev, PARSE_ERROR); + } + break; + + default: + dm_eval_err(ev, "Unknown operator '%c'.\n", *c); + dm_set_mode(ev, PARSE_ERROR); + } + + if (op != OP_NONE) + { + if ((node = dm_eval_add_node(list, op)) != NULL) + { + ev->expect = PARSE_IDENT | PARSE_CONST | PARSE_SUBEXPR; + dm_set_mode(ev, PARSE_START); + } + else + dm_set_mode(ev, PARSE_ERROR); + } + } + break; + + case PARSE_IDENT: + if (isalnum(*c) || *c == '_') + { + if (first) + { + tmpStrLen = 0; + first = FALSE; + } + + if (tmpStrLen < DM_MAX_ID_LEN) + tmpStr[tmpStrLen++] = *c++; + else + { + tmpStr[tmpStrLen] = 0; + dm_eval_err(ev, "Identifier too long! ('%s')\n", tmpStr); + } + } + else + { + tmpStr[tmpStrLen] = 0; + DMEvalId *id = dm_eval_find_id(ev, tmpStr); + if (id != NULL) + { + if ((node = dm_eval_add_node(list, id->type == ID_FUNC ? OP_FUNC : OP_VAR)) != NULL) + { + node->id = id; + if (id->type == ID_FUNC) + { + func = node; + ev->expect = PARSE_SUBEXPR; + } + else + ev->expect = PARSE_END | PARSE_OPER; + + dm_set_mode(ev, PARSE_START); + } + else + dm_set_mode(ev, PARSE_ERROR); + } + else + { + dm_eval_err(ev, "No such identifier '%s'.\n", tmpStr); + dm_set_mode(ev, PARSE_ERROR); + } + } + break; + } + } + + *str = c; + + return (ev->mode == PARSE_ERROR) ? -1 : 0; +} + + +int dm_eval_parse_expr(DMEvalContext *ev, char *expr, DMEvalNode **result) +{ + int ret; + + if (ev == NULL || result == NULL) + return -1; + + ret = dm_eval_parse_expr_do(ev, result, &expr, 0); + + return ret; +} + + +static const char *dm_oper_names[OP_NOPERS] = +{ + "NONE", + "+", + "-", + "*", + "/", + "%", + + "<<", + ">>", + + "&", + "|", + "^", + + "FUNC", + "VAR", + "SUBEXPR", + "CONST" +}; + + +static void dm_print_optree_do(DMEvalContext *ev, DMEvalNode *node, const int level) +{ + while (node != NULL) + { + switch (node->op) + { + case OP_FUNC: + printf("%s(", node->id != NULL ? node->id->name : "?ERROR"); + dm_print_optree_do(ev, node->subexpr, level + 1); + printf(")"); + break; + + case OP_VAR: + printf("%s", node->id != NULL ? node->id->name : "?ERROR"); + break; + + case OP_CONST: + printf("%.1f", node->val); + break; + + case OP_SUBEXPR: + printf("("); + if (node->subexpr != NULL) + dm_print_optree_do(ev, node->subexpr, level + 1); + else + printf("?ERROR"); + printf(")"); + break; + + default: + if (node->op > 0 && node->op < OP_NOPERS) + printf("%s", dm_oper_names[node->op]); + else + printf("ERROR!"); + break; + } + node = node->next; + } +} + + +void dm_print_optree(DMEvalContext *ev, DMEvalNode *node) +{ + dm_print_optree_do(ev, node, 0); + printf("\n"); +} + + +/* + operator precedence: + HIGHEST + "()" + + "*" + "/" + "%" + + "+" + "-" + + "<<" + ">>" + + "&" + "^" + "|" + LOWEST +*/ + +int dm_eval_reorder(DMEvalContext *ev, DMEvalNode *node, DMEvalNode **result) +{ + DMEvalNode *list = NULL; + + while (node != NULL) + { + DMEvalNode *tmp; + + switch (node->op) + { + case OP_SUBEXPR: + case OP_FUNC: + if ((tmp = dm_eval_push_node(&list, node)) == NULL) + return -1; + + if (dm_eval_reorder(ev, node->subexpr, &(tmp->subexpr)) != 0) + return -2; + break; + + case OP_MUL: + case OP_DIV: + case OP_MOD: + if ((tmp = dm_eval_pop_node(&list)) != NULL) + { + DMEvalNode *sub; + + if ((sub = dm_eval_add_node(&list, OP_SUBEXPR)) == NULL) + return -3; + + dm_eval_push_node(&(sub->subexpr), tmp); + dm_eval_add_node(&(sub->subexpr), node->op); + node = node->next; + if (node != NULL) + { + if (node->op == OP_FUNC || node->op == OP_SUBEXPR) + { + if ((tmp = dm_eval_push_node(&(sub->subexpr), node)) == NULL) + return -178; + + if (dm_eval_reorder(ev, node->subexpr, &(tmp->subexpr)) != 0) + return -66; + } + else + dm_eval_push_node(&(sub->subexpr), node); + } + else + return -72; + } + else + return -24; + break; + + case OP_LSHIFT: + case OP_RSHIFT: + + case OP_ADD: + case OP_SUB: + + case OP_AND: + case OP_XOR: + case OP_OR: + + case OP_VAR: + case OP_CONST: + if (dm_eval_push_node(&list, node) == NULL) + return -32; + break; + + default: + dm_eval_err(ev, "Invalid opcode %d in node %p.\n", node->op, node); + return -4; + } + + node = node->next; + } + + *result = list; + return 0; +} + + +static int dm_eval_get(DMEvalContext *ev, DMEvalNode *node, DMValue *result) +{ + DMValue tmp; + + if (node == NULL) + return -32; + + switch (node->op) + { + case OP_VAR: + *result = *(node->id->var); + break; + + case OP_CONST: + *result = node->val; + break; + + case OP_SUBEXPR: + if (dm_eval_exec(ev, node->subexpr, result) != 0) + return -1; + break; + + case OP_FUNC: + if (dm_eval_exec(ev, node->subexpr, &tmp) != 0) + return -1; + + *result = node->id->func(tmp); + break; + + default: + return -16; + } + + return 0; +} + + +int dm_eval_exec(DMEvalContext *ev, DMEvalNode *tree, DMValue *presult) +{ + DMValue result; + DMEvalNode *node = tree; + + while (node != NULL) + { + DMValue tmp; + + switch (node->op) + { + case OP_LSHIFT: + case OP_RSHIFT: + + case OP_AND: + case OP_XOR: + case OP_OR: + + case OP_MUL: + case OP_DIV: + case OP_MOD: + if (dm_eval_get(ev, node->next, &tmp) != 0) + return -6; + + switch (node->op) + { + case OP_DIV: + if (tmp == 0) + { + dm_eval_err(ev, "Division by zero.\n"); + return -1; + } + result /= tmp; + break; + + case OP_MUL: result *= tmp; break; + + case OP_MOD: + if (tmp == 0) + { + dm_eval_err(ev, "Division by zero.\n"); + return -1; + } + result = DMCONVTYPE result % DMCONVTYPE tmp; + break; + + case OP_LSHIFT: result = DMCONVTYPE result << DMCONVTYPE tmp; break; + case OP_RSHIFT: result = DMCONVTYPE result >> DMCONVTYPE tmp; break; + + case OP_AND: result = DMCONVTYPE result & DMCONVTYPE tmp; break; + case OP_OR: result = DMCONVTYPE result | DMCONVTYPE tmp; break; + case OP_XOR: result = DMCONVTYPE result ^ DMCONVTYPE tmp; break; + } + + node = node->next; + break; + + case OP_ADD: + case OP_SUB: + if (dm_eval_get(ev, node->next, &tmp) != 0) + return -6; + + switch (node->op) + { + case OP_ADD: result += tmp; break; + case OP_SUB: result -= tmp; break; + } + + node = node->next; + break; + + default: + if (dm_eval_get(ev, node, &result) != 0) + { + dm_eval_err(ev, "Invalid opcode %d in node %p.\n", node->op, node); + return -4; + } + } + + node = node->next; + } + + *presult = result; + return 0; +} diff -r 000000000000 -r 32250b436bca dmeval.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmeval.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,90 @@ +#ifndef DMEVAL_H +#define DMEVAL_H + +#include "dmlib.h" + + +typedef double DMValue; +#define DMCONVTYPE (int) + + +enum +{ + OP_NONE, + + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + + OP_LSHIFT, + OP_RSHIFT, + + OP_AND, + OP_OR, + OP_XOR, + + OP_FUNC, + OP_VAR, + OP_SUBEXPR, + OP_CONST, + + OP_NOPERS +} DMOperType; + +enum +{ + ID_FUNC, + ID_VAR +} DMEvalIdType; + + +typedef struct +{ + char *name; + int type; + DMValue (*func)(DMValue); + DMValue *var; +} DMEvalId; + + +typedef struct DMEvalNode +{ + int op; + DMValue val; + DMEvalId *id; + + struct DMEvalNode *subexpr, *next, *prev; +} DMEvalNode; + + +typedef struct +{ + BOOL err; + char *errStr; + + int nids; + DMEvalId *ids; + + int mode, prev, expect; +} DMEvalContext; + + +DMEvalId *dm_eval_find_id(DMEvalContext *ev, const char *name); +DMEvalId *dm_eval_add_var(DMEvalContext *ev, const char *name, DMValue *var); +DMEvalId *dm_eval_add_func(DMEvalContext *ev, const char *name, DMValue (*func)(DMValue)); + +DMEvalContext *dm_eval_new(void); +void dm_eval_close(DMEvalContext *ev); +void dm_eval_free(DMEvalNode *node); +void dm_eval_clear_err(DMEvalContext *ev); + +int dm_eval_parse_expr(DMEvalContext *ev, char *expr, DMEvalNode **result); +int dm_eval_reorder(DMEvalContext *ev, DMEvalNode *node, DMEvalNode **result); +int dm_eval_exec(DMEvalContext *ev, DMEvalNode *tree, DMValue *presult); + +void dm_print_optree(DMEvalContext *ev, DMEvalNode *node); + + +#endif // DMEVAL_H diff -r 000000000000 -r 32250b436bca dmfile.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmfile.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,46 @@ +/* + * DMLib + * -- Plain STDIO file endianess functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#include "dmfile.h" + + +BOOL dm_fread_str(FILE *f, void *buf, size_t len) +{ + return fread(buf, len, 1, f) == 1; +} + + +#define DM_DEFINE_FFUNC(xname, xtype, xmacro) \ +BOOL dm_fread_ ## xname (FILE *f, xtype *v) { \ + xtype result; \ + if (fread(&result, sizeof( xtype ), 1, f) != 1) \ + return FALSE; \ + *v = DM_ ## xmacro ## _TO_NATIVE (result); \ + return TRUE; \ +} + +#include "dmfiletmpl.h" + + +#undef DM_DEFINE_FFUNC + +BOOL dm_fwrite_str(FILE *f, void *buf, size_t len) +{ + return fwrite(buf, len, 1, f) == 1; +} + + +#define DM_DEFINE_FFUNC(xname, xtype, xmacro) \ +BOOL dm_fwrite_ ## xname (FILE *f, xtype v) { \ + xtype result = DM_NATIVE_TO_ ## xmacro (v); \ + if (fwrite(&result, sizeof( xtype ), 1, f) != 1) \ + return FALSE; \ + return TRUE; \ +} + +#include "dmfiletmpl.h" + +#undef DM_DEFINE_FFUNC diff -r 000000000000 -r 32250b436bca dmfile.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmfile.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,30 @@ +/* + * DMLib + * -- Main header file + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#ifndef DMFILE_H +#define DMFILE_H + +#include "dmlib.h" + + +/* Plain file endianess functions + */ +#define DM_DEFINE_FFUNC(xname, xtype, z) \ +BOOL dm_fread_ ## xname (FILE *f, xtype *v); \ +BOOL dm_fwrite_ ## xname (FILE *f, xtype v); + +#include "dmfiletmpl.h" + +#undef DM_DEFINE_FFUNC + +BOOL dm_fread_str(FILE *f, void *, size_t); +BOOL dm_fwrite_str(FILE *f, void *, size_t); + + +#ifdef __cplusplus +} +#endif +#endif // DMFILE_H diff -r 000000000000 -r 32250b436bca dmfiletmpl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmfiletmpl.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,17 @@ +/* + * DMLib + * -- Plain streamed file I/O endianess functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ + +DM_DEFINE_FFUNC(le16, Uint16, LE16) +DM_DEFINE_FFUNC(le32, Uint32, LE32) + +DM_DEFINE_FFUNC(be16, Uint16, BE16) +DM_DEFINE_FFUNC(be32, Uint32, BE32) + +#ifdef DM_HAVE_64BIT +DM_DEFINE_FFUNC(le64, Uint64, LE64) +DM_DEFINE_FFUNC(be64, Uint64, BE64) +#endif diff -r 000000000000 -r 32250b436bca dmgfx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmgfx.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,152 @@ +#include "dmlib.h" + + +void dmFillRect(SDL_Surface *screen, int x0, int y0, int x1, int y1, Uint32 col) +{ + SDL_Rect rc; + rc.x = x0; + rc.y = y0; + rc.w = x1 - x0 + 1; + rc.h = y1 - y0 + 1; + SDL_FillRect(screen, &rc, col); +} + +#define DM_SWAP(T, A, B) { if ((B) < (A)) { T swtmp = (B); B = (A); A = swtmp; } } + +void dmDrawHLine(SDL_Surface *screen, int x0, int x1, int yc, const Uint32 col) +{ + int x; + const int bpp = screen->format->BytesPerPixel, + cx0 = screen->clip_rect.x, cy0 = screen->clip_rect.y, + cx1 = screen->clip_rect.x + screen->clip_rect.w - 1, + cy1 = screen->clip_rect.y + screen->clip_rect.h - 1; + + DM_SWAP(int, x0, x1); + if (yc < cy0|| yc > cy1 || x1 < cx0 || x0 > cx1) return; + if (x0 < cx0) x0 = cx0; + if (x1 > cx1) x1 = cx1; + + Uint8 *pix = screen->pixels + yc * screen->pitch + (x0 * bpp); + switch (screen->format->BitsPerPixel) + { + case 8: + for (x = x0; x <= x1; x++) + *pix++ = col; + break; + + case 15: + case 16: + { + Uint16 *p = (Uint16 *) pix; + for (x = x0; x <= x1; x++) + *p++ = col; + } + break; + + case 24: + for (x = x0; x <= x1; x++) + { + *pix++ = col; + *pix++ = col; + *pix++ = col; + } + break; + + case 32: + { + Uint32 *p = (Uint32 *) pix; + for (x = x0; x <= x1; x++) + *p++ = col; + } + break; + } +} + + +void dmDrawVLine(SDL_Surface *screen, int y0, int y1, int xc, const Uint32 col) +{ + int y; + const int bpp = screen->format->BytesPerPixel, + pitch = screen->pitch / bpp, + cx0 = screen->clip_rect.x, cy0 = screen->clip_rect.y, + cx1 = screen->clip_rect.x + screen->clip_rect.w - 1, + cy1 = screen->clip_rect.y + screen->clip_rect.h - 1; + + DM_SWAP(int, y0, y1); + if (xc < cx0 || xc > cx1 || y1 < cy0 || y0 > cy1) return; + if (y0 < cy0) y0 = cy0; + if (y1 > cy1) y1 = cy1; + + Uint8 *pix = screen->pixels + y0 * screen->pitch + (xc * bpp); + switch (screen->format->BitsPerPixel) + { + case 8: + for (y = y0; y <= y1; y++, pix += pitch) + *pix = col; + break; + + case 16: + case 15: + { + Uint16 *p = (Uint16 *) pix; + for (y = y0; y <= y1; y++, p += pitch) + *p = col; + } + break; + + case 24: + for (y = y0; y <= y1; y++, pix += pitch) + pix[0] = pix[1] = pix[2] = col; + break; + + case 32: + { + Uint32 *p = (Uint32 *) pix; + for (y = y0; y <= y1; y++, p += pitch) + *p = col; + } + break; + } +} + + +void dmDrawBox3D(SDL_Surface *screen, int x0, int y0, int x1, int y1, Uint32 bgcol, Uint32 ucol, Uint32 dcol) +{ + SDL_Rect rc; + + rc.x = x0 + 1; + rc.y = y0 + 1; + rc.w = x1 - x0 - 1; + rc.h = y1 - y0 - 1; + SDL_FillRect(screen, &rc, bgcol); + + dmDrawHLine(screen, x0 , x1 - 1, y0, ucol); + dmDrawHLine(screen, x0 + 1, x1 , y1, dcol); + + dmDrawVLine(screen, y0 , y1 - 1, x0, ucol); + dmDrawVLine(screen, y0 + 1, y1 , x1, dcol); +} + + +void dmClearSurface(SDL_Surface *screen, const Uint32 col) +{ + SDL_FillRect(screen, NULL, col); +} + + +Uint32 dmMapRGB(SDL_Surface *screen, int r, int g, int b) +{ + return SDL_MapRGB(screen->format, r, g, b); +} + + +Uint32 dmMapRGBA(SDL_Surface *screen, int r, int g, int b, int a) +{ + return SDL_MapRGBA(screen->format, r, g, b, a); +} + + +int dmDirectBlitSurface(SDL_Surface *bmp, SDL_Surface *screen) +{ + return SDL_BlitSurface(bmp, NULL, screen, NULL); +} diff -r 000000000000 -r 32250b436bca dmimage.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmimage.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,134 @@ +/* + * DMLib + * -- Bitmap image conversion and loading + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2012 Tecnic Software productions (TNSP) + */ +#include "dmimage.h" + + +#define STBI_FAILURE_USERMSG 1 +#define STBI_NO_STDIO 1 +#define STBI_NO_HDR 1 +#include "stb_image.c" + + +SDL_Surface *dmCreateRGBSurfaceFrom(void *data, const int width, const int height, const int depth, const int pitch, const int rmask, const int gmask, const int bmask, const int amask) +{ + SDL_Surface *tmp, *pixtmp, *result = NULL; + + tmp = SDL_CreateRGBSurfaceFrom(data, width, height, depth, pitch, bmask, gmask, rmask, amask); + + pixtmp = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, 10, 10, 32, rmask, gmask, bmask, amask); + if (tmp != NULL && pixtmp != NULL) + { + result = SDL_ConvertSurface(tmp, pixtmp->format, SDL_SWSURFACE | SDL_SRCALPHA); + SDL_FreeSurface(tmp); + } + if (pixtmp != NULL) + SDL_FreeSurface(pixtmp); + + return result; +} + + +SDL_Surface *dmCreatePaletteSurfaceFrom(void *data, const int width, const int height, const int pitch) +{ + int yc; + Uint8 *dst, *src; + SDL_Surface *result; + result = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0,0,0,0); + if (result == NULL) + return NULL; + + dst = result->pixels; + src = data; + for (yc = 0; yc < height; yc++) + { + memcpy(dst, src, width * sizeof(Uint8)); + dst += result->pitch; + src += pitch; + } + + return result; +} + + + +static int dmSTBIread(void *user, char *data, int size) +{ + return dmfread(data, 1, size, (DMResource *) user); +} + + +static void dmSTBIskip(void *user, unsigned int n) +{ + dmfseek((DMResource *) user, n, SEEK_CUR); +} + + +static int dmSTBIeof(void *user) +{ + return dmfeof((DMResource *) user); +} + + +static const stbi_io_callbacks dmSTBICallbacks = +{ + dmSTBIread, + dmSTBIskip, + dmSTBIeof, +}; + + +SDL_Surface *dmLoadImage(DMResource *file) +{ + Uint32 rmask, gmask, bmask, amask; + SDL_Surface *result = NULL; + int width, height, comp; + Uint8 *data; + + data = stbi_load_from_callbacks(&dmSTBICallbacks, file, &width, &height, &comp, 0); + + if (data == NULL) + { + dmError("Error decoding image resource %p '%s' [%d, %d, %d]: %s\n", + file, file->filename, width, height, comp, stbi_failure_reason()); + return NULL; + } + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + rmask = 0xff000000; + gmask = 0x00ff0000; + bmask = 0x0000ff00; + amask = 0x000000ff; +#else + rmask = 0x000000ff; + gmask = 0x0000ff00; + bmask = 0x00ff0000; + amask = 0xff000000; +#endif + + switch (comp) + { + case 4: + case 3: + result = dmCreateRGBSurfaceFrom(data, width, height, comp * 8, + width * comp, rmask, gmask, bmask, amask); + break; + + case 1: + result = dmCreatePaletteSurfaceFrom(data, width, height, width * comp); + break; + } + + stbi_image_free(data); + + if (result == NULL) + { + dmError("Format conversion failed for image resource %p '%s' [%d, %d, %d].\n", + file, file->filename, width, height, comp); + } + + return result; +} diff -r 000000000000 -r 32250b436bca dmimage.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmimage.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,27 @@ +/* + * DMLib + * -- Bitmap image conversion and loading + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2012 Tecnic Software productions (TNSP) + */ +#ifndef DMIMAGE_H +#define DMIMAGE_H + +#include "dmlib.h" +#include "dmres.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +SDL_Surface *dmCreateRGBSurfaceFrom(void *data, const int width, const int height, const int depth, const int pitch, const int rmask, const int gmask, const int bmask, const int amask); +SDL_Surface *dmCreatePaletteSurfaceFrom(void *data, const int width, const int height, const int pitch); +SDL_Surface *dmLoadImage(DMResource *file); + + +#ifdef __cplusplus +} +#endif + +#endif // DMIMAGE_H diff -r 000000000000 -r 32250b436bca dmlerp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmlerp.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,35 @@ +#include "dmlib.h" + +void dmLerpInit(DMLerpContext *ctx, DMFloat start, DMFloat end, DMFloat nsteps) +{ + ctx->start = start; + ctx->end = end; + ctx->nsteps = nsteps; +} + + +DMFloat dmLerpSCurve(DMLerpContext *ctx, const DMFloat step) +{ + const DMFloat n = step / ctx->nsteps; + const DMFloat v = DMM_S_CURVE(n); + return DMM_LERP(v, ctx->start, ctx->end); +} + + +DMFloat dmLerp1(DMLerpContext *ctx, const DMFloat step) +{ + const DMFloat v = step / ctx->nsteps; + return DMM_LERP(v, ctx->start, ctx->end); +} + + +DMFloat dmCatmull_Rom(const DMFloat t, const DMFloat p0, const DMFloat p1, const DMFloat p2, const DMFloat p3) +{ + const DMFloat q = t * t; + return ( + (2 * p1) + + (-p0 + p2) * t + + (2 * p0 - 5 * p1 + 4 * p2 - p3) * q + + ( -p0 + 3 * p1 - 3 * p2 + p3) * q * t + ) * 0.5f; +} diff -r 000000000000 -r 32250b436bca dmlib.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmlib.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,199 @@ +#include "dmlib.h" +#include + + +int dmVerbosity = 0; +char *dmProgName = NULL, + *dmProgDesc = NULL, + *dmProgVersion = NULL, + *dmProgAuthor = NULL, + *dmProgLicense = NULL; + + +void dmInitProg(char *name, char *desc, char *version, char *author, char *license) +{ + dmProgName = name; + dmProgDesc = desc; + dmProgVersion = version; + + if (author) + dmProgAuthor = author; + else + dmProgAuthor = DM_PROG_AUTHOR; + + if (license) + dmProgLicense = license; + else + dmProgLicense = DM_PROG_LICENSE; + +} + + +void dmPrintBanner(FILE *outFile, const char *name, const char *usage) +{ + fprintf(outFile, + "\n%s v%s (%s)\n" + "%s\n" + "%s\n" + "Usage: %s %s\n", + dmProgName, dmProgVersion, dmProgDesc, + dmProgAuthor, dmProgLicense, name, usage); +} + + +void dmMsgVA(int level, const char *fmt, va_list ap) +{ + if (dmVerbosity >= level) + { + fprintf(stderr, "%s: ", dmProgName); + vfprintf(stderr, fmt, ap); + } +} + + +void dmPrintVA(int level, const char *fmt, va_list ap) +{ + if (dmVerbosity >= level) + { + vfprintf(stderr, fmt, ap); + } +} + + +void dmErrorVA(const char *fmt, va_list ap) +{ + fprintf(stderr, "%s: ", dmProgName); + vfprintf(stderr, fmt, ap); +} + + +void dmError(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + dmErrorVA(fmt, ap); + va_end(ap); +} + + +void dmMsg(int level, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + dmMsgVA(level, fmt, ap); + va_end(ap); +} + + +void dmPrint(int level, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + dmPrintVA(level, fmt, ap); + va_end(ap); +} + + +/* Memory handling routines + */ +void *dmMalloc(size_t l) +{ + return malloc(l); +} + + +void *dmMalloc0(size_t l) +{ + return calloc(1, l); +} + + +void *dmCalloc(size_t n, size_t l) +{ + return calloc(n, l); +} + + +void *dmRealloc(void *p, size_t l) +{ + return realloc(p, l); +} + + +void dmFree(void *p) +{ + /* Check for NULL pointers for portability due to some libc + * implementations not handling free(NULL) too well. + */ + if (p) free(p); +} + + +BOOL dmGetIntVal(const char *s, int *i) +{ + if (s[0] == '$') + { + if (sscanf(&s[1], "%x", i) < 1) + return FALSE; + } + else if (s[0] == '0' && s[1] == 'x') + { + if (sscanf(&s[2], "%x", i) < 1) + return FALSE; + } + else + { + if (sscanf(s, "%u", i) < 1) + return FALSE; + } + return TRUE; +} + + +/* Error handling + */ +#define DM_SYSTEM_ERRORS 100000 + +int dmGetErrno() +{ + return DM_SYSTEM_ERRORS + errno; +} + + +const char *dmErrorStr(int error) +{ + if (error >= DM_SYSTEM_ERRORS) + return strerror(error - DM_SYSTEM_ERRORS); + + switch (error) + { + case DMERR_OK: return "No error"; + case DMERR_FOPEN: return "File open error"; + case DMERR_FREAD: return "Read error"; + case DMERR_FWRITE: return "Write error"; + case DMERR_FSEEK: return "Seek error"; + case DMERR_NOT_FOUND: return "Resource not found"; + + case DMERR_INVALID_DATA: return "Invalid data"; + case DMERR_MALLOC: return "Memory allocation failure"; + case DMERR_ALREADY_INIT: return "Already initialized"; + case DMERR_INIT_FAIL: return "Initialization failed"; + case DMERR_INVALID_ARGS: return "Invalid arguments"; + + case DMERR_NULLPTR: return "NULL pointer"; + case DMERR_NOT_SUPPORTED: return "Operation not supported"; + case DMERR_OUT_OF_DATA: return "Out of data"; + case DMERR_EXTRA_DATA: return "Extra data"; + case DMERR_BOUNDS: return "Bounds check failed"; + + case DMERR_NOTPACK: return "File is not a PACK"; + case DMERR_VERSION: return "Unsupported PACK version"; + case DMERR_INVALID: return "Invalid data, corrupted file"; + case DMERR_COMPRESSION: return "Error in compression"; + + default: return "Unknown error"; + } +} diff -r 000000000000 -r 32250b436bca dmlib.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmlib.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,364 @@ +/* + * DMLib + * -- Main header file + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#ifndef DMLIB_H +#define DMLIB_H + +#include +#ifdef DM_GFX_TTF_TEXT +#include +#endif +#include + +#ifdef DM_USE_ASSERTS +#include +#else +#define assert(NEXPR) // stub +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Defaults +//#define DM_PROG_AUTHOR "By Matti 'ccr' Hmlinen (C) Copyright 2012 TNSP" +//#define DM_PROG_LICENSE "This software is licensed under GNU GPL version 2" + +#define DM_PROG_AUTHOR "" +#define DM_PROG_LICENSE "" + +/* Error codes + */ +enum { + // General error codes + DMERR_OK = 0, + DMERR_FOPEN, + DMERR_FREAD, + DMERR_FWRITE, + DMERR_FSEEK, + DMERR_NOT_FOUND, + + DMERR_INVALID_DATA, + DMERR_MALLOC, + DMERR_ALREADY_INIT, + DMERR_INIT_FAIL, + DMERR_INVALID_ARGS, + + DMERR_NULLPTR, + DMERR_NOT_SUPPORTED, + DMERR_OUT_OF_DATA, + DMERR_EXTRA_DATA, + DMERR_BOUNDS, + + // PACK-file subsystem + DMERR_NOTPACK, + DMERR_VERSION, + DMERR_INVALID, + DMERR_COMPRESSION, +}; + + +// Resource management defines +#define DMRES_NAME_LEN 32 +#define DMRES_DATA_PACK "data.pak" // Name of the data-file +#define DMRES_DATA_PATH "DATA/" // Sub-directory path +#define DMRES_RES_FILE "res.txt" // Resource data file + + +// Mutexes +#define DMMutex SDL_mutex +#define dmCreateMutex() SDL_CreateMutex() +#define dmDestroyMutex(x) SDL_DestroyMutex(x) +#define dmMutexLock(x) SDL_mutexP(x) +#define dmMutexUnlock(x) SDL_mutexV(x) + + +/* Define a boolean type + */ +#if !defined(FALSE) && !defined(TRUE) && !defined(BOOL) +typedef enum { FALSE = 0, TRUE = 1 } BOOL; +#endif + +#ifndef BOOL +#ifdef bool +#define BOOL bool +#else +#define BOOL int +#endif +#endif + + +/* Fixed point math type + */ +typedef union +{ + Uint64 dw; + Uint32 w[2]; +} DMFixedPoint; + + +typedef union +{ + Uint32 dw; + Uint16 w[2]; +} DMFixedPoint32; + + +/* Macros for fixed point math + */ +#if __GNUC__ >= 3 +# define FP_SET(a, k) a.dw = k ## ULL +#else +# define FP_SET(a, k) a.dw = k +#endif + +#define FP_CONV(a, k) a.dw = k + +#ifndef SDL_BYTEORDER +# error Undefined byteorder! +#endif + +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) +# define FP_SETH(a, k) a.w[0] = (k) +# define FP_SETL(a, k) a.w[1] = (k) +# define FP_SETHL(a, h, l) { a.w[0] = (h); a.w[1] = (l); } +# define FP_GETH(a) a.w[0] +# define FP_GETL(a) a.w[1] +#elif (SDL_BYTEORDER == SDL_LIL_ENDIAN) +# define FP_SETH(a, k) a.w[1] = (k) +# define FP_SETL(a, k) a.w[0] = (k) +# define FP_SETHL(a, h, l) { a.w[1] = (h); a.w[0] = (l); } +# define FP_GETH(a) a.w[1] +# define FP_GETL(a) a.w[0] +#else +# error Unsupported byte order! +#endif + +#define FP_PRINTF(a) printf("%.8x:%.8x", FP_GETH(a), FP_GETL(a)) +#define FP_PRINTF32(a) printf("%.4x:%.4x", FP_GETH(a), FP_GETL(a)) + +#define FP_ADD(a, b) a.dw += b.dw +#define FP_SUB(a, b) a.dw -= b.dw +#define FP_ADD_R(r, a, b) r.dw = a.dw + b.dw +#define FP_SUB_R(r, a, b) r.dw = a.dw - b.dw +#define FP_DIV(a, b) a.dw /= b.dw +#define FP_MUL(a, b) a.dw *= b.dw +#define FP_DIV_R(r, a, b) r.dw = (a.dw / b.dw) +#define FP_MUL_R(r, a, b) r.dw = (a.dw * b.dw) + + +/* Miscellaneous types + */ +typedef struct +{ +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + Uint8 a,g,b,r; +#elif (SDL_BYTEORDER == SDL_LIL_ENDIAN) + Uint8 r,g,b,a; +#endif +} DMRGBA32; + + +typedef float DMFloat; + + +/* Drawing modes used by blitting and some other functions. + */ +enum +{ + DMD_NONE = 0x0000, + DMD_TRANSPARENT = 0x0001, + DMD_SATURATE = 0x0002, + + DMD_ANTIALIAS = 0x0004, + + DMD_NMODES = 6 +}; + + +// Available bitdepths. Not all functions may support every one of these. +enum +{ + DMD_8BIT = 0, + DMD_32BIT, + + DMD_NBITDEPTHS +}; + + +static inline int dmBitsPerPixel2Index(int bpp) +{ + return (bpp == 8 ? 0 : (bpp == 32 ? 1 : -1)); +} + + +/* Generic parameter interpolation + */ +#define DMM_S_CURVE(t) ((t) * (t) * (3.0f - 2.0f * (t))) +#define DMM_LERP(t, a, b) ((a) + (t) * ((b) - (a))) + +typedef struct +{ + DMFloat start, end, nsteps; +} DMLerpContext; + + +void dmLerpInit(DMLerpContext *ctx, DMFloat start, DMFloat end, DMFloat nsteps); +DMFloat dmLerpSCurve(DMLerpContext *ctx, const DMFloat step); +DMFloat dmLerp1(DMLerpContext *ctx, const DMFloat step); +DMFloat dmCatmull_Rom(const DMFloat t, const DMFloat p0, const DMFloat p1, const DMFloat p2, const DMFloat p3); + + +/* Perlin noise + */ +void dmPerlinInit(void); +DMFloat dmPerlinNoise2D(DMFloat x, DMFloat y, DMFloat alpha, DMFloat beta, int n); + + +/* TTF text drawing + */ +#ifdef DM_GFX_TTF_TEXT +void dmDrawTTFTextConst(SDL_Surface *screen, TTF_Font *font, SDL_Color col, int x, int y, const char *fmt); +void dmDrawTTFTextVA(SDL_Surface *screen, TTF_Font *font, SDL_Color col, int x, int y, const char *fmt, va_list ap); +void dmDrawTTFText(SDL_Surface *screen, TTF_Font *font, SDL_Color col, int x, int y, const char *fmt, ...); +#endif + +/* Arbitrary line drawing + */ +#ifdef DM_GFX_LINES +typedef int (*DMDrawLineFunc)(SDL_Surface *src, int x0, int y0, int x1, int y1, const Uint32 color); +DMDrawLineFunc dmGetDrawLineFunc(SDL_PixelFormat *dst, int mode); +int dmDrawLineAny(SDL_Surface *src, int x0, int y0, int x1, int y1, const Uint32 color, int mode); +#endif + +/* Scaled blitting functions + */ +#ifdef DM_GFX_BLITS +typedef int (*DMScaledBlitFunc)(SDL_Surface *src, const int x0, const int y0, const int dwidth, const int dheight, SDL_Surface *dst); + +DMScaledBlitFunc dmGetScaledBlitFunc(SDL_PixelFormat *src, SDL_PixelFormat *dst, int mode); +int dmScaledBlitSurfaceAny(SDL_Surface *src, const int x0, const int y0, const int dwidth, const int dheight, SDL_Surface *dst, int mode); +#endif + +/* Misc functions + */ +#ifdef DM_GFX_MISC +void dmFillRect(SDL_Surface *screen, int x0, int y0, int x1, int y1, const Uint32 col); +void dmDrawHLine(SDL_Surface *screen, int x0, int x1, int yc, const Uint32 col); +void dmDrawVLine(SDL_Surface *screen, int y0, int y1, int xc, const Uint32 col); +void dmDrawBox3D(SDL_Surface *screen, int x0, int y0, int x1, int y1, Uint32 bgcol, Uint32 ucol, Uint32 dcol); +void dmClearSurface(SDL_Surface *screen, const Uint32 col); +Uint32 dmMapRGB(SDL_Surface *screen, int r, int g, int b); +Uint32 dmMapRGBA(SDL_Surface *screen, int r, int g, int b, int a); +int dmDirectBlitSurface(SDL_Surface *bmp, SDL_Surface *screen); +#endif + + +/* Global variables + */ +extern char *dmProgName, + *dmProgDesc, + *dmProgVersion, + *dmProgAuthor, + *dmProgLicense; + +extern int dmVerbosity; +void dmInitProg(char *name, char *desc, char *version, char *author, char *license); +void dmPrintBanner(FILE *outFile, const char *name, const char *usage); + +void dmMsgVA(int level, const char *fmt, va_list ap); +void dmMsg(int level, const char *fmt, ...); +void dmPrintVA(int level, const char *fmt, va_list ap); +void dmPrint(int level, const char *fmt, ...); +void dmErrorVA(const char *fmt, va_list); +void dmError(const char *fmt, ...); + +void * dmMalloc(size_t); +void * dmMalloc0(size_t); +void * dmRealloc(void *, size_t); +void * dmCalloc(size_t, size_t); +void dmFree(void *); + +BOOL dmGetIntVal(const char *s, int *i); + +int dmGetErrno(); +const char *dmErrorStr(int error); + +char * dm_strdup(const char *); +char * dm_strdup_vprintf(const char *, va_list); +char * dm_strdup_printf(const char *, ...); + + +/* Endianess swapping macros + */ +#define DM_SWAP_16_LE_BE(value) ((Uint16) ( \ + (Uint16) ((Uint16) (value) >> 8) | \ + (Uint16) ((Uint16) (value) << 8)) ) + + +#define DM_SWAP_32_LE_BE(value) ((Uint32) ( \ + (((Uint32) (value) & (Uint32) 0x000000ffU) << 24) | \ + (((Uint32) (value) & (Uint32) 0x0000ff00U) << 8) | \ + (((Uint32) (value) & (Uint32) 0x00ff0000U) >> 8) | \ + (((Uint32) (value) & (Uint32) 0xff000000U) >> 24))) + +#ifdef DM_HAVE_64BIT +#define DM_SWAP_64_LE_BE(value) ((Uint64) ( \ + (((Uint64) (value) & (Uint64) 0x00000000000000ffULL) << 56) | \ + (((Uint64) (value) & (Uint64) 0x000000000000ff00ULL) << 40) | \ + (((Uint64) (value) & (Uint64) 0x0000000000ff0000ULL) << 24) | \ + (((Uint64) (value) & (Uint64) 0x00000000ff000000ULL) << 8) | \ + (((Uint64) (value) & (Uint64) 0x000000ff00000000ULL) >> 8) | \ + (((Uint64) (value) & (Uint64) 0x0000ff0000000000ULL) >> 24) | \ + (((Uint64) (value) & (Uint64) 0x00ff000000000000ULL) >> 40) | \ + (((Uint64) (value) & (Uint64) 0xff00000000000000ULL) >> 56))) +#endif + +/* Macros that swap only when needed ... + */ +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) +# define DM_LE16_TO_NATIVE(value) DM_SWAP_16_LE_BE(value) +# define DM_LE32_TO_NATIVE(value) DM_SWAP_32_LE_BE(value) +# define DM_NATIVE_TO_LE16(value) DM_SWAP_16_LE_BE(value) +# define DM_NATIVE_TO_LE32(value) DM_SWAP_32_LE_BE(value) + +# define DM_BE16_TO_NATIVE(value) ((Uint16) (value)) +# define DM_BE32_TO_NATIVE(value) ((Uint32) (value)) +# define DM_NATIVE_TO_BE16(value) ((Uint16) (value)) +# define DM_NATIVE_TO_BE32(value) ((Uint32) (value)) + +# ifdef DM_HAVE_64BIT +# define DM_LE64_TO_NATIVE(value) DM_SWAP_64_LE_BE(value) +# define DM_NATIVE_TO_LE64(value) DM_SWAP_64_LE_BE(value) +# define DM_BE64_TO_NATIVE(value) ((Uint64) (value)) +# define DM_NATIVE_TO_BE64(value) ((Uint64) (value)) +# endif + +#elif (SDL_BYTEORDER == SDL_LIL_ENDIAN) + +# define DM_LE16_TO_NATIVE(value) ((Uint16) (value)) +# define DM_LE32_TO_NATIVE(value) ((Uint32) (value)) +# define DM_NATIVE_TO_LE16(value) ((Uint16) (value)) +# define DM_NATIVE_TO_LE32(value) ((Uint32) (value)) + +# define DM_BE16_TO_NATIVE(value) DM_SWAP_16_LE_BE(value) +# define DM_BE32_TO_NATIVE(value) DM_SWAP_32_LE_BE(value) +# define DM_NATIVE_TO_BE16(value) DM_SWAP_16_LE_BE(value) +# define DM_NATIVE_TO_BE32(value) DM_SWAP_32_LE_BE(value) + +# ifdef DM_HAVE_64BIT +# define DM_LE64_TO_NATIVE(value) ((Uint64) (value)) +# define DM_NATIVE_TO_LE64(value) ((Uint64) (value)) +# define DM_BE64_TO_NATIVE(value) DM_SWAP_64_LE_BE(value) +# define DM_NATIVE_TO_BE64(value) DM_SWAP_64_LE_BE(value) +# endif +#endif + + +#ifdef __cplusplus +} +#endif +#endif // DMLIB_H diff -r 000000000000 -r 32250b436bca dmline.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmline.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,81 @@ +/* + * DMLib + * -- Arbitrary line drawing functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#include "dmlib.h" + + +/* Clip line coordinates. Return value: + * = 0 : No clipping needed. + * > 0 : Clipped. Line partially inside the clipping area. + * < 0 : Line completely outside the clipping area. + */ +int dmClipLineCoords(int *x0, int *y0, int *x1, int *y1, SDL_Surface *screen) +{ +#if 0 + const int clipX0 = screen->clip_rect.x, + clipY0 = screen->clip_rect.y; + const int clipX1 = clipX0 + screen->clip_rect.w - 1, + clipY1 = clipY0 + screen->clip_rect.h - 1; + const int dx = *x1 - *x0, + dy = *y1 - *y0; + DMFixedPoint k, kdx, kdy; + + // Is line completely outside the clipping area? + if ((*x0 < clipX0 && *x1 < clipX0) || (*x0 > clipX1 && *x1 > clipX1) || + (*y0 < clipY0 && *y1 < clipY0) || (*y0 > clipY1 && *y1 > clipY1)) + return -1; + + FP_SETHL(kdx, dx); + FP_SETHL(kdy, dy); +#endif + + return 0; +} + + +int dmTestLineCoords(int x0, int y0, int x1, int y1, SDL_Surface *screen) +{ + return dmClipLineCoords(&x0, &y0, &x1, &y1, screen); +} + + +#include "dmlinefunc.h" + +static const DMDrawLineFunc dmDrawLineTable[DMD_NMODES][DMD_NBITDEPTHS] = +{ + /* DMD_NONE */ { dmDrawLine8 , dmDrawLine32 }, + /* DMD_TRANSPARENT */ { dmDrawLine8Transparent , dmDrawLine32Transparent }, + /* DMD_SATURATE */ { dmDrawLine8Saturate , dmDrawLine32Saturate }, + /* DMD_NONE + AA */ { NULL, NULL }, + /* DMD_TRANSP + AA */ { NULL, NULL }, + /* DMD_SATURATE + AA */ { dmDrawLine8AASaturate , dmDrawLine32AASaturate }, +}; + + +DMDrawLineFunc dmGetDrawLineFunc(SDL_PixelFormat *dst, int mode) +{ + int index; + if (dst == NULL || mode < 0 || + mode >= sizeof(dmDrawLineTable) / sizeof(dmDrawLineTable[0])) + return NULL; + + if ((index = dmBitsPerPixel2Index(src->BitsPerPixel)) < 0) + return NULL; + + return dmDrawLineTable[mode] +} + + +int dmDrawLineAny(SDL_Surface *screen, int x0, int y0, int x1, int y1, const Uint32 col, int mode) +{ + DMDrawLineFunc bfunc = dmGetDrawLineFunc(screen->format, mode); + + if (bfunc == NULL) + return -15; + + return bfunc(screen, x0, y0, x1, y1, col); +} + diff -r 000000000000 -r 32250b436bca dmlinefunc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmlinefunc.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,58 @@ + +#define DM_DRAWLINE_INNER pix[y0 + x0] = col; + + +#define DM_DRAWLINE_NAME dmDrawLine8 +#define DM_DRAWLINE_DST_BYTES 1 +#define DM_DRAWLINE_DST_TYPE Uint8 +#include "dmdrawline.h" + + +#define DM_DRAWLINE_NAME dmDrawLine15 +#define DM_DRAWLINE_DST_BYTES 2 +#define DM_DRAWLINE_DST_TYPE Uint16 +#include "dmdrawline.h" + + +#define DM_DRAWLINE_NAME dmDrawLine16 +#define DM_DRAWLINE_DST_BYTES 2 +#define DM_DRAWLINE_DST_TYPE Uint16 +#include "dmdrawline.h" + + +#define DM_DRAWLINE_NAME dmDrawLine32 +#define DM_DRAWLINE_DST_BYTES 4 +#define DM_DRAWLINE_DST_TYPE Uint32 +#include "dmdrawline.h" + +/* +#undef DM_DRAWLINE_INNER +#define DM_DRAWLINE_INNER \ + int q = pix[y0 + x0] + col; + pix[y0 + x0] = q > + + +#define DM_DRAWLINE_NAME dmDrawLineAdd8 +#define DM_DRAWLINE_DST_BYTES 1 +#define DM_DRAWLINE_DST_TYPE Uint8 +#include "dmdrawline.h" + + +#define DM_DRAWLINE_NAME dmDrawLineAdd15 +#define DM_DRAWLINE_DST_BYTES 2 +#define DM_DRAWLINE_DST_TYPE Uint16 +#include "dmdrawline.h" + + +#define DM_DRAWLINE_NAME dmDrawLineAdd16 +#define DM_DRAWLINE_DST_BYTES 2 +#define DM_DRAWLINE_DST_TYPE Uint16 +#include "dmdrawline.h" + + +#define DM_DRAWLINE_NAME dmDrawLineAdd32 +#define DM_DRAWLINE_DST_BYTES 4 +#define DM_DRAWLINE_DST_TYPE Uint32 +#include "dmdrawline.h" +*/ + diff -r 000000000000 -r 32250b436bca dmpack.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmpack.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,192 @@ +/* + * DMLib + * -- PACK-file handling + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#include "dmpack.h" +#include "dmfile.h" +#include + + +DMPackEntry *dm_pack_entry_new() +{ + return (DMPackEntry *) dmMalloc0(sizeof(DMPackEntry)); +} + + +void dm_pack_entry_free(DMPackEntry * node) +{ + dmFree(node); +} + + +void dm_pack_entry_insert(DMPackEntry ** packDir, DMPackEntry * node) +{ + if (*packDir != NULL) + { + node->prev = (*packDir)->prev; + (*packDir)->prev->next = node; + (*packDir)->prev = node; + } + else + { + *packDir = node->prev = node; + } + + node->next = NULL; +} + + +void dm_pack_entry_delete(DMPackEntry ** packDir, DMPackEntry * node) +{ + if (node->prev) + node->prev->next = node->next; + + if (node->next) + node->next->prev = node->prev; + else + (*packDir)->prev = node->prev; + + node->prev = node->next = NULL; +} + + +DMPackEntry *dm_pack_find(DMPackEntry *list, const char *filename) +{ + DMPackEntry *node; + + for (node = list; node != NULL; node = node->next) + { + if (strcmp(node->filename, filename) == 0) + return node; + } + + return NULL; +} + + +/* + * OPEN a packfile + */ +int dm_pack_open(const char *filename, DMPackFile ** ppPack, BOOL readOnly) +{ + unsigned int i; + DMPackFile *pack; + DMPackFileHeader hdr; + + *ppPack = NULL; + + // Allocate packfile-structure + pack = (DMPackFile *) dmMalloc0(sizeof(DMPackFile)); + if (pack == NULL) + return DMERR_MALLOC; + + // Open the file + pack->file = fopen(filename, (readOnly) ? "rb" : "r+b"); + if (pack->file == NULL) + { + dmFree(pack); + return DMERR_FOPEN; + } + + pack->filename = dm_strdup(filename); + + // Read PACK header + fseek(pack->file, 0L, SEEK_SET); + if (!dm_fread_str(pack->file, (Uint8 *) &hdr.ident, sizeof(hdr.ident)) || + !dm_fread_le16(pack->file, &hdr.version) || + !dm_fread_le32(pack->file, &hdr.dirEntries) || + !dm_fread_le32(pack->file, &hdr.dirOffset)) + { + dm_pack_close(pack); + return DMERR_FREAD; + } + + // Check information + if (memcmp(&hdr.ident, DPACK_IDENT, sizeof(hdr.ident)) != 0) + { + dm_pack_close(pack); + return DMERR_NOTPACK; + } + + if (hdr.version != DPACK_VERSION) + { + dm_pack_close(pack); + return DMERR_VERSION; + } + + // Read directory + if (fseek(pack->file, hdr.dirOffset, SEEK_SET) != 0) + { + dm_pack_close(pack); + return DMERR_INVALID; + } + + for (i = 0; i < hdr.dirEntries; i++) + { + // Allocate and read directory entry + DMPackEntry *entry = dm_pack_entry_new(); + + if (entry == NULL) + { + dm_pack_close(pack); + return DMERR_MALLOC; + } + + if (!dm_fread_str(pack->file, (Uint8 *) &entry->filename, sizeof(entry->filename)) || + !dm_fread_le32(pack->file, &entry->size) || + !dm_fread_le32(pack->file, &entry->offset) || + !dm_fread_le32(pack->file, &entry->length) || + !dm_fread_le32(pack->file, &entry->resFlags)) + { + *ppPack = pack; + return DMERR_FREAD; + } + + // Insert into list + dm_pack_entry_insert(&pack->entries, entry); + } + + // Set the result + *ppPack = pack; + return DMERR_OK; +} + + +/* + * CLOSE the packfile + */ +int dm_pack_close(DMPackFile * pack) +{ + DMPackEntry *node, *next; + + if (pack == NULL) + return DMERR_OK; + + // Write the directory + node = pack->entries; + while (node != NULL) + { + next = node->next; + dm_pack_entry_free(node); + node = next; + } + + // Close the file + if (pack->file != NULL) + { + fclose(pack->file); + pack->file = NULL; + } + + // Free structures + dmFree(pack->filename); + pack->filename = NULL; + + // Free packfile + pack->entries = NULL; + dmFree(pack); + + return DMERR_OK; +} diff -r 000000000000 -r 32250b436bca dmpack.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmpack.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,61 @@ +/* + * DMLib + * -- PACK-file routines + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#ifndef DMPACK_H +#define DMPACK_H +#include "dmlib.h" + + +#define DPACK_IDENT "TNSPDPCK" // Magic ident +#define DPACK_VERSION (0x0120) // Version +#define DPACK_TMPSIZE (128 * 1024) + + +typedef struct _DMPackEntry +{ + char filename[DMRES_NAME_LEN]; + Uint32 size; // Size (UNCOMPRESSED) + Uint32 offset; // Offset in pack file + Uint32 length; // (Compressed) data length + + Uint32 resFlags; // Resource flags (used by resource handler) + + Uint32 privFlags; // Private flags + + struct _DMPackEntry *next, *prev; +} DMPackEntry; + + +typedef struct +{ + DMPackEntry *entries; + char * filename; // Filename & path + FILE * file; // File +} DMPackFile; + + +typedef struct +{ + char ident[8]; // Magic identifier + Uint16 version; // Version + Uint32 dirEntries; // Number of entries + Uint32 dirOffset; // Offset of the directory +} DMPackFileHeader; + + +DMPackEntry * dm_pack_entry_new(); +void dm_pack_entry_free(DMPackEntry *); +void dm_pack_entry_insert(DMPackEntry **, DMPackEntry *); +void dm_pack_entry_delete(DMPackEntry **, DMPackEntry *); + +DMPackEntry * dm_pack_find(DMPackEntry *list, const char *filename); + +int dm_pack_open(const char *, DMPackFile **, BOOL); +int dm_pack_close(DMPackFile *); +int dm_pack_read(DMPackFile *, const char *, Uint8 **, size_t *); + + +#endif // DMPACK_H diff -r 000000000000 -r 32250b436bca dmpackutil.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmpackutil.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,312 @@ +/* + * DMLib + * -- PACK-file additional utility routines + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#include "dmpackutil.h" +#include +#include "dmfile.h" + + +DMPackEntry *dm_pack_entry_copy(const DMPackEntry *src) +{ + DMPackEntry *node = dm_pack_entry_new(); + if (node == NULL) + return NULL; + + strncpy(node->filename, src->filename, sizeof(node->filename)); + node->filename[sizeof(node->filename) - 1] = 0; + + node->size = src->size; + node->offset = src->offset; + node->length = src->length; + node->resFlags = src->resFlags; + + return node; +} + + +/* + * CLOSE/WRITE the packfile + */ +int dm_pack_write(DMPackFile * pack) +{ + DMPackEntry *node; + DMPackFileHeader hdr; + + if (pack == NULL) + return DMERR_OK; + + if (pack->file == NULL) + return DMERR_FOPEN; + + // Compute directory offset and number of entries + memcpy(&hdr.ident, DPACK_IDENT, sizeof(hdr.ident)); + hdr.version = DPACK_VERSION; + hdr.dirEntries = 0; + hdr.dirOffset = + sizeof(hdr.ident) + sizeof(hdr.version) + + sizeof(hdr.dirEntries) + sizeof(hdr.dirOffset); + + node = pack->entries; + while (node != NULL) + { + hdr.dirEntries++; + hdr.dirOffset += node->length; + node = node->next; + } + + // Write PACK header + if (fseek(pack->file, 0L, SEEK_SET) != 0) + return -4; + + dm_fwrite_str(pack->file, (Uint8 *) & hdr.ident, sizeof(hdr.ident)); + dm_fwrite_le16(pack->file, hdr.version); + dm_fwrite_le32(pack->file, hdr.dirEntries); + dm_fwrite_le32(pack->file, hdr.dirOffset); + + // Write the directory + if (fseek(pack->file, hdr.dirOffset, SEEK_SET) != 0) + return -5; + + node = pack->entries; + while (node != NULL) + { + // Write one entry + dm_fwrite_str(pack->file, node->filename, sizeof(node->filename)); + dm_fwrite_le32(pack->file, node->size); + dm_fwrite_le32(pack->file, node->offset); + dm_fwrite_le32(pack->file, node->length); + dm_fwrite_le32(pack->file, node->resFlags); + + node = node->next; + } + + return DMERR_OK; +} + + +/* + * CREATE a packfile, for writing + */ +int dm_pack_create(const char *filename, DMPackFile ** pack) +{ + // Allocate packfile-structure + *pack = (DMPackFile *) dmCalloc(1, sizeof(DMPackFile)); + if (*pack == NULL) + return DMERR_MALLOC; + + // Open the file + (*pack)->file = fopen(filename, "wb"); + if ((*pack)->file == NULL) + { + dmFree(*pack); + return DMERR_FOPEN; + } + + (*pack)->filename = dm_strdup(filename); + + // Set the result + return DMERR_OK; +} + + +/* + * ADD a file into the PACK + */ +int dm_pack_add_file(DMPackFile * pack, const char *filename, BOOL doCompress, int resFlags, + DMPackEntry ** ppEntry) +{ + z_stream compStream; + off_t startOffs; + unsigned int compSize; + FILE *inFile; + Uint8 *inBuffer, *outBuffer; + DMPackEntry entry, *node; + int result; + + *ppEntry = NULL; + + if (pack == NULL) + return DMERR_OK; + + if (pack->file == NULL) + return DMERR_FOPEN; + + // Compute starting offset + startOffs = sizeof(DMPackFileHeader); + node = pack->entries; + while (node != NULL) + { + startOffs += node->length; + node = node->next; + } + + // Seek to the position + if (fseek(pack->file, startOffs, SEEK_SET) != 0) + return DMERR_INVALID; + + // Read file data + if ((inFile = fopen(filename, "rb")) == NULL) + return -1; + + // Allocate temporary buffer + inBuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE); + if (!inBuffer) + { + fclose(inFile); + return DMERR_MALLOC; + } + + outBuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE); + if (!outBuffer) + { + dmFree(inBuffer); + fclose(inFile); + return DMERR_MALLOC; + } + + // Read (and possibly compress) the data + compSize = 0; + compStream.zalloc = (alloc_func) Z_NULL; + compStream.zfree = (free_func) Z_NULL; + compStream.opaque = (voidpf) Z_NULL; + result = deflateInit(&compStream, (doCompress) ? Z_DEFAULT_COMPRESSION : 0); + if (result != Z_OK) + { + dmFree(inBuffer); + dmFree(outBuffer); + fclose(inFile); + return DMERR_COMPRESSION; + } + + // Initialize compression streams + result = Z_OK; + while (!feof(inFile) && result == Z_OK) + { + compStream.avail_in = fread(inBuffer, sizeof(Uint8), DPACK_TMPSIZE, inFile); + compStream.next_in = inBuffer; + compStream.next_out = outBuffer; + compStream.avail_out = DPACK_TMPSIZE; + compStream.total_out = 0; + result = deflate(&compStream, Z_FULL_FLUSH); + + if (result == Z_OK && compStream.total_out > 0) + { + compSize += compStream.total_out; + fwrite(outBuffer, sizeof(Uint8), compStream.total_out, pack->file); + } + } + + // Create directory entry + strncpy(entry.filename, filename, sizeof(entry.filename)); + entry.filename[sizeof(entry.filename) - 1] = 0; + entry.size = compStream.total_in; + entry.offset = startOffs; + entry.length = compSize; + entry.resFlags = resFlags; + + // Cleanup + deflateEnd(&compStream); + dmFree(inBuffer); + dmFree(outBuffer); + fclose(inFile); + + // Add directory entry + *ppEntry = dm_pack_entry_copy(&entry); + if (*ppEntry == NULL) + return DMERR_MALLOC; + + dm_pack_entry_insert(&pack->entries, *ppEntry); + + return DMERR_OK; +} + + +/* + * EXTRACT a file from the PACK + */ +int dm_pack_extract_file(DMPackFile *pack, DMPackEntry * entry) +{ + z_stream compStream; + FILE *outFile; + Uint8 *inBuffer, *outBuffer; + size_t inDataLeft; + int ret; + + if (pack == NULL) + return DMERR_OK; + + if (pack->file == NULL) + return DMERR_FOPEN; + + // Seek to the position + if (fseek(pack->file, entry->offset, SEEK_SET) != 0) + return DMERR_INVALID; + + // Open destination file + if ((outFile = fopen(entry->filename, "wb")) == NULL) + return -1; + + // Allocate temporary buffer + if ((inBuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE)) == NULL) + { + fclose(outFile); + return DMERR_MALLOC; + } + + if ((outBuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE)) == NULL) + { + dmFree(inBuffer); + fclose(outFile); + return DMERR_MALLOC; + } + + // Read and uncompress the data + compStream.zalloc = (alloc_func) Z_NULL; + compStream.zfree = (free_func) Z_NULL; + compStream.opaque = (voidpf) Z_NULL; + ret = inflateInit(&compStream); + if (ret != Z_OK) + { + dmFree(inBuffer); + dmFree(outBuffer); + fclose(outFile); + return DMERR_COMPRESSION; + } + + // Initialize compression streams + inDataLeft = entry->length; + ret = Z_OK; + while (inDataLeft > 0 && ret == Z_OK) + { + if (inDataLeft >= DPACK_TMPSIZE) + compStream.avail_in = fread(inBuffer, sizeof(Uint8), DPACK_TMPSIZE, pack->file); + else + compStream.avail_in = fread(inBuffer, sizeof(Uint8), inDataLeft, pack->file); + + inDataLeft -= compStream.avail_in; + compStream.next_in = inBuffer; + + while (compStream.avail_in > 0 && ret == Z_OK) + { + compStream.next_out = outBuffer; + compStream.avail_out = DPACK_TMPSIZE; + compStream.total_out = 0; + ret = inflate(&compStream, Z_FULL_FLUSH); + if (compStream.total_out > 0) + { + fwrite(outBuffer, sizeof(Uint8), compStream.total_out, outFile); + } + } + } + + // Cleanup + inflateEnd(&compStream); + dmFree(inBuffer); + dmFree(outBuffer); + fclose(outFile); + + return DMERR_OK; +} diff -r 000000000000 -r 32250b436bca dmpackutil.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmpackutil.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,17 @@ +/* + * DMLib + * -- PACK-file additional utility routines + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#ifndef DMPACKUTIL_H +#define DMPACKUTIL_H +#include "dmpack.h" + +DMPackEntry * dm_pack_entry_copy(const DMPackEntry *); +int dm_pack_write(DMPackFile *); +int dm_pack_create(const char *, DMPackFile **); +int dm_pack_add_file(DMPackFile *, const char *, BOOL, int resFlags, DMPackEntry **); +int dm_pack_extract_file(DMPackFile *, DMPackEntry *); + +#endif // DMPACKUTIL_H diff -r 000000000000 -r 32250b436bca dmperlin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmperlin.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,136 @@ +/* + * Coherent noise function over 1 or 2 dimensions. + */ +#include "dmlib.h" +#include + +#define B (0x100) +#define BM (0x0ff) +#define NP (12) +#define N (0x1000) +#define NM (0x0fff) + + +#define DM_PERLIN_SETUP(i, b0, b1, r0, r1) { \ + t = (vec[i] + N); \ + b0 = (((int) t) & BM); \ + b1 = ((b0 + 1) & BM); \ + r0 = (t - ((int) t)); \ + r1 = (r0 - 1.0f); \ + } + + +#define DM_PERLIN_AT2(rx,ry) ((rx) * q[0] + (ry) * q[1]) + + +static int p[B + B + 2]; +static DMFloat g2[B + B + 2][2]; +static DMFloat g1[B + B + 2]; + + +static DMFloat dmPerlinDoNoise2(DMFloat vec[2]) +{ + int bx0, bx1, by0, by1, b00, b10, b01, b11; + DMFloat rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v; + int i, j; + + DM_PERLIN_SETUP(0, bx0, bx1, rx0, rx1); + DM_PERLIN_SETUP(1, by0, by1, ry0, ry1); + + i = p[bx0]; + j = p[bx1]; + + b00 = p[i + by0]; + b10 = p[j + by0]; + b01 = p[i + by1]; + b11 = p[j + by1]; + + sx = DMM_S_CURVE(rx0); + sy = DMM_S_CURVE(ry0); + + q = g2[b00]; + u = DM_PERLIN_AT2(rx0, ry0); + q = g2[b10]; + v = DM_PERLIN_AT2(rx1, ry0); + a = DMM_LERP(sx, u, v); + + q = g2[b01]; + u = DM_PERLIN_AT2(rx0, ry1); + q = g2[b11]; + v = DM_PERLIN_AT2(rx1, ry1); + b = DMM_LERP(sx, u, v); + + return DMM_LERP(sy, a, b); +} + + +static void dmPerlinNormalize2(DMFloat v[2]) +{ + DMFloat s; + + s = sqrt(v[0] * v[0] + v[1] * v[1]); + + v[0] /= s; + v[1] /= s; +} + + +void dmPerlinInit(void) +{ + int i, j, k; + + srand(32); + + for (i = 0; i < B; i++) + { + p[i] = i; + g1[i] = (DMFloat) ((rand() % (B + B)) - B) / B; + + for (j = 0; j < 2; j++) + g2[i][j] = (DMFloat) ((rand() % (B + B)) - B) / B; + + dmPerlinNormalize2(g2[i]); + } + + while (--i) + { + k = p[i]; + p[i] = p[j = rand() % B]; + p[j] = k; + } + + for (i = 0; i < B + 2; i++) + { + p[B + i] = p[i]; + g1[B + i] = g1[i]; + + for (j = 0; j < 2; j++) + g2[B + i][j] = g2[i][j]; + } +} + + +/* Harmonic summing functions - PDB + * In what follows "alpha" is the weight when the sum is formed. + * Typically it is 2, As this approaches 1 the function is noisier. + * "beta" is the harmonic scaling/spacing, typically 2. + */ +DMFloat dmPerlinNoise2D(DMFloat x, DMFloat y, DMFloat alpha, DMFloat beta, int n) +{ + int i; + DMFloat val, sum = 0; + DMFloat p[2], scale = 1; + + p[0] = x; + p[1] = y; + for (i = 0; i < n; i++) + { + val = dmPerlinDoNoise2(p); + sum += val / scale; + scale *= alpha; + p[0] *= beta; + p[1] *= beta; + } + + return (sum); +} diff -r 000000000000 -r 32250b436bca dmres.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmres.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,951 @@ +/* + * dmlib + * -- Resource management + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2003-2012 Tecnic Software productions (TNSP) + */ +#include "dmres.h" +#include + +#if !defined(DMRES_PACKFS) && !defined(DMRES_STDIO) +#error At least one of DMRES_PACKFS, DMRES_STDIO must be defined. +#endif + +#define DMRES_LOCK(x) dmMutexLock(dfResourcesMutex) +#define DMRES_UNLOCK(x) dmMutexUnlock(dfResourcesMutex) + + +/* Global variables + */ +static BOOL dfResInitialized = FALSE; +static int dfResFlags = 0; +static char * dfResPath = NULL; +DMResource * dfResources = NULL; +DMMutex * dfResourcesMutex = NULL; + + +#ifdef DMRES_PACKFS +static DMPackFile *dfResPackFile = NULL; +static char * dfResPackFilename = NULL; +#endif + + +DMResource *dmres_new(const char *filename, int flags, size_t size) +{ + DMResource *res = dmMalloc0(sizeof(DMResource)); + if (res == NULL) + return NULL; + + res->filename = dm_strdup(filename); + res->flags = flags; + res->dataSize = size; + + return res; +} + + +void dmres_free(DMResource *res) +{ + if (res != NULL) + { + dmFree(res->filename); + dmFree(res->data); + dmFree(res); + } +} + + +void dmres_insert(DMResource * node) +{ + if (dfResources != NULL) + { + node->prev = dfResources->prev; + dfResources->prev->next = node; + dfResources->prev = node; + } + else + { + dfResources = node->prev = node; + } + + node->next = NULL; +} + + +void dmres_delete(DMResource * node) +{ + if (node->prev) + node->prev->next = node->next; + + if (node->next) + node->next->prev = node->prev; + else + dfResources->prev = node->prev; + + node->prev = node->next = NULL; +} + + +DMResource * dmres_find(const char *filename) +{ + DMResource *node, *found = NULL; + + DMRES_LOCK(); + + for (node = dfResources; node != NULL; node = node->next) + { + if (strcmp(node->filename, filename) == 0) + { + found = node; + break; + } + } + + DMRES_UNLOCK(); + + return found; +} + + +#ifdef DMRES_STDIO +/* Basic stdio file routines + */ +static int dm_stdio_fopen(DMResource *handle) +{ + char *rfilename = dm_strdup_printf("%s%s", DMRES_DATA_PATH, handle->filename); + if (rfilename == NULL) + return DMERR_MALLOC; + + handle->fh = fopen(rfilename, "rb"); + dmFree(rfilename); + + handle->error = dmGetErrno(); + return (handle->fh != NULL) ? DMERR_OK : DMERR_FOPEN; +} + + +static void dm_stdio_fclose(DMResource * f) +{ + if (f->fh != NULL) + { + fclose(f->fh); + f->fh = NULL; + } +} + + +static int dm_stdio_ferror(DMResource * f) +{ + return f->error; +} + + +static int dm_stdio_fseek(DMResource *f, const off_t pos, const int whence) +{ + int ret = fseek(f->fh, pos, whence); + f->error = dmGetErrno(); + return ret; +} + + +static off_t dm_stdio_fsize(DMResource *f) +{ + off_t savePos, fileSize; + + // Check if the size is cached + if (f->dataSize != 0) + return f->dataSize; + + // Get file size + savePos = ftell(f->fh); + if (fseek(f->fh, 0L, SEEK_END) != 0) + { + f->error = dmGetErrno(); + return -1; + } + + fileSize = ftell(f->fh); + if (fseek(f->fh, savePos, SEEK_SET) != 0) + { + f->error = dmGetErrno(); + return -1; + } + + f->dataSize = fileSize; + return fileSize; +} + + +static off_t dm_stdio_ftell(DMResource * f) +{ + return ftell(f->fh); +} + + +static BOOL dm_stdio_feof(DMResource * f) +{ + return feof(f->fh); +} + + +static int dm_stdio_fgetc(DMResource * f) +{ + int ret = fgetc(f->fh); + f->error = dmGetErrno(); + return ret; +} + + +static size_t dm_stdio_fread(void *ptr, size_t size, size_t nmemb, DMResource * f) +{ + size_t ret = fread(ptr, size, nmemb, f->fh); + f->error = dmGetErrno(); + return ret; +} + + +static int dm_stdio_preload(DMResource *handle) +{ + int ret = dm_stdio_fopen(handle); + if (ret != DMERR_OK) + return ret; + + dm_stdio_fsize(handle); + + handle->data = dmMalloc(handle->dataSize); + if (handle->data == NULL) + return DMERR_MALLOC; + + if (dm_stdio_fread(handle->data, sizeof(Uint8), handle->dataSize, handle) != handle->dataSize) + return DMERR_FREAD; + + return DMERR_OK; +} + + +DMResourceOps dfStdioFileOps = +{ + dm_stdio_ferror, + dm_stdio_fseek, + dm_stdio_fsize, + dm_stdio_ftell, + dm_stdio_feof, + dm_stdio_fgetc, + dm_stdio_fread, + + dm_stdio_fopen, + dm_stdio_fclose, + dm_stdio_preload +}; + +DMResourceOps dfStdioFHOps = +{ + dm_stdio_ferror, + dm_stdio_fseek, + dm_stdio_fsize, + dm_stdio_ftell, + dm_stdio_feof, + dm_stdio_fgetc, + dm_stdio_fread, + + NULL, + NULL, + NULL +}; +#endif + + +// Some mingw/windows headers define these as macros, which is bad for us +#ifdef __WIN32 +#undef ferror +#undef feof +#endif + + +/* + * PACK file routines + */ +#ifdef DMRES_PACKFS +static int dm_pack_preload(DMResource *handle) +{ + DMPackEntry *node; + int res = DMERR_OK, cres, cdataLeft; + z_stream cstream; + Uint8 * cbuffer = NULL; + + // Search PACK nodelist for file + if ((node = dm_pack_find(dfResPackFile->entries, handle->filename)) == NULL) + { + dmError("Entry '%s' not found in PACK file.\n", handle->filename); + res = DMERR_NOT_FOUND; + goto error; + } + + // Seek to entry + if (fseek(dfResPackFile->file, node->offset, SEEK_SET) == -1) + { + dmError("Could not seek node position in PACK file.\n"); + res = DMERR_FSEEK; + goto error; + } + + // Allocate a structures and buffers + cbuffer = (Uint8 *) dmMalloc(DPACK_TMPSIZE); + if (cbuffer == NULL) + { + res = DMERR_MALLOC; + goto error; + } + + // Initialize fields + handle->dataOffset = 0; + handle->dataSize = node->size; + handle->data = (Uint8 *) dmMalloc(node->size); + if (handle->data == NULL) + { + res = DMERR_MALLOC; + goto error; + } + + // Initialize decompression + cstream.zalloc = (alloc_func) Z_NULL; + cstream.zfree = (free_func) Z_NULL; + cstream.opaque = (voidpf) Z_NULL; + cstream.next_out = handle->data; + cstream.avail_out = handle->dataSize; + cdataLeft = node->length; + cres = inflateInit(&(cstream)); + if (cres != Z_OK) + { + dmError("Could not initialize zlib stream inflation.\n"); + res = DMERR_INIT_FAIL; + goto error; + } + + // Uncompress the data + while (cdataLeft > 0 && + cstream.avail_out > 0 && cres == Z_OK) + { + cstream.avail_in = fread( + cbuffer, sizeof(Uint8), + (cdataLeft >= DPACK_TMPSIZE) ? DPACK_TMPSIZE : cdataLeft, + dfResPackFile->file); + + cdataLeft -= cstream.avail_in; + cstream.next_in = cbuffer; + cres = inflate(&cstream, Z_FULL_FLUSH); + } + + // Cleanup + inflateEnd(&(cstream)); + +error: + dmFree(cbuffer); + return res; +} +#endif + + +static void dm_mem_fclose(DMResource * f) +{ + f->dataSize = 0; + f->dataOffset = 0; + dmFree(f->data); + f->data = NULL; +} + + +static int dm_mem_ferror(DMResource * f) +{ + return f->error; +} + + +static int dm_mem_fseek(DMResource * f, const off_t offset, const int whence) +{ + off_t newPos; + + // Calculate the new position + switch (whence) + { + case SEEK_SET: + newPos = offset; + break; + + case SEEK_CUR: + newPos = f->dataOffset + offset; + break; + + case SEEK_END: + newPos = f->dataSize + offset; + break; + + default: + return -1; + } + + // Set the new position + f->dataOffset = newPos; + + // Check the new position + if (newPos < 0 && (size_t) newPos >= f->dataSize) + return -1; + + return 0; +} + + +static off_t dm_mem_fsize(DMResource * f) +{ + return f->dataSize; +} + + +static off_t dm_mem_ftell(DMResource * f) +{ + return f->dataOffset; +} + + +static BOOL dm_mem_feof(DMResource * f) +{ + // Check for EOF + if ((size_t) f->dataOffset <= f->dataSize) + return FALSE; + else + return TRUE; +} + + +static int dm_mem_fgetc(DMResource * f) +{ + // Check for EOF + if ((size_t) f->dataOffset < f->dataSize) + return (int) f->data[f->dataOffset++]; + else + return EOF; +} + + +static size_t dm_mem_fread(void *buf, size_t size, size_t nmemb, DMResource * f) +{ + size_t length = (size * nmemb); + + // Check if we can read the whole chunk + if (((size_t) f->dataOffset + length) >= f->dataSize) + { + nmemb = (f->dataSize - f->dataOffset) / size; + length = size * nmemb; + } + + memcpy(buf, f->data + f->dataOffset, length); + f->dataOffset += length; + return nmemb; +} + + +DMResourceOps dfPackFileOps = +{ + dm_mem_ferror, + dm_mem_fseek, + dm_mem_fsize, + dm_mem_ftell, + dm_mem_feof, + dm_mem_fgetc, + dm_mem_fread, + + NULL, + dm_mem_fclose, + dm_pack_preload +}; + + +DMResourceOps dfMemIOFileOps = +{ + dm_mem_ferror, + dm_mem_fseek, + dm_mem_fsize, + dm_mem_ftell, + dm_mem_feof, + dm_mem_fgetc, + dm_mem_fread, + + NULL, + NULL, + NULL +}; + + +/* FS file handling functions. These functions call the actual + * functions depending on where the file is located. + */ +DMResource *dmf_open(const char *filename) +{ + int ret; + DMResource *handle; + + // Check master directory for resource + if ((handle = dmres_find(filename)) == NULL) + { +#ifdef DMRES_STDIO + // Hmm.. does not exist? Fall back to a stdio file + handle = dmres_new(filename, 0, 0); + if (handle == NULL) + return NULL; + + handle->fops = &dfStdioFileOps; + dmres_insert(handle); +#else + // Stdio not enabled, fail + return NULL; +#endif + } + + // Check fops + if (handle->fops == NULL) + { +#ifdef DMRES_PACKFS + if (dfResFlags & DRF_USE_PACK) + handle->fops = &dfPackFileOps; +#ifdef DMRES_STDIO + else + handle->fops = &dfStdioFileOps; +#else + handle->fops = &dfPackFileOps; +#endif + +#else + handle->fops = &dfStdioFileOps; +#endif + } + + // Check if the data is preloaded + if (handle->flags & DMF_LOADED) + { + dmres_ref(handle); + return handle; + } + + // Check if we want to preload .. + ret = DMERR_INIT_FAIL; + if ((handle->flags & DMF_PRELOAD) && + handle->fops->preload != NULL) + ret = handle->fops->preload(handle); + else + { + if (handle->fops->fopen != NULL) + ret = handle->fops->fopen(handle); + else + if (handle->fops->preload != NULL) + ret = handle->fops->preload(handle); + } + + if (ret == DMERR_OK) + return handle; + + dmres_ref(handle); + return NULL; +} + + +DMResource * dmf_open_memio(const char *filename, Uint8 *buf, size_t len) +{ + DMResource *handle; + + // Check master directory for resource + if ((handle = dmres_find(filename)) == NULL) + { + // Hmm.. does not exist? Fall back to a stdio file + handle = dmres_new(filename, DMF_LOADED, len); + if (handle == NULL) + return NULL; + + handle->fops = &dfMemIOFileOps; + handle->data = buf; + dmres_insert(handle); + } + + // Increase refcount + dmres_ref(handle); + + return handle; +} + + +#ifdef DMRES_STDIO +DMResource * dmf_create_stdio(const char *filename) +{ + DMResource *handle = dmres_new(filename, 0, 0); + if (handle == NULL) + return NULL; + + handle->fops = &dfStdioFileOps; + + handle->fh = fopen(filename, "rb"); + handle->error = dmGetErrno(); + + if (handle->fh != NULL) + { + dmres_ref(handle); + return handle; + } + else + { + dmres_free(handle); + return NULL; + } +} + + +DMResource * dmf_create_stdio_stream(FILE *fh) +{ + DMResource *handle = dmres_new("", 0, 0); + if (handle == NULL) + return NULL; + + handle->fops = &dfStdioFHOps; + handle->fh = fh; + dmres_ref(handle); + return handle; +} +#endif + + +void dmf_close(DMResource * f) +{ + if (f == NULL) + return; + + if (f->fops->fclose != NULL) + f->fops->fclose(f); + + dmres_unref(f); +} + + +int dmferror(DMResource * f) +{ + f->atime = time(NULL); + return f->fops->ferror(f); +} + +int dmfseek(DMResource * f, off_t offset, int whence) +{ + f->atime = time(NULL); + return f->fops->fseek(f, offset, whence); +} + +off_t dmfsize(DMResource * f) +{ + f->atime = time(NULL); + return f->fops->fsize(f); +} + +off_t dmftell(DMResource * f) +{ + f->atime = time(NULL); + return f->fops->ftell(f); +} + +BOOL dmfeof(DMResource * f) +{ + f->atime = time(NULL); + return f->fops->feof(f); +} + +int dmfgetc(DMResource * f) +{ + f->atime = time(NULL); + return f->fops->fgetc(f); +} + +size_t dmfread(void *ptr, size_t size, size_t nmemb, DMResource * f) +{ + f->atime = time(NULL); + return f->fops->fread(ptr, size, nmemb, f); +} + + +int dmres_ref(DMResource *res) +{ + DMRES_LOCK(); + res->atime = time(NULL); + res->refcount++; + DMRES_UNLOCK(); + + return res->refcount; +} + + +int dmres_unref(DMResource *res) +{ + DMRES_LOCK(); + res->refcount--; + DMRES_UNLOCK(); + + return res->refcount; +} + + +int dmres_load_resfile(const char *filename) +{ + int ret; + char line[256]; + FILE *f = fopen(filename, "r"); + if (f == NULL) + return DMERR_FOPEN; + + DMRES_LOCK(); + + while (fgets(line, sizeof(line) - 1, f) != NULL) + { + int fnstart, fsep; + for (fnstart = 0; isspace(line[fnstart]); fnstart++); + for (fsep = fnstart; line[fsep] && line[fsep] != '|'; fsep++); + if (line[fsep] == '|') + { + int flags, i; + for (i = fsep - 1; i > 0 && isspace(line[i]); i--) + line[i] = 0; + + for (i = fsep; isspace(line[i]); i++); + + if (sscanf(&line[i], "%x", &flags) == 1 && + strlen(&line[fnstart]) > 0) + { + + } + } + } + + DMRES_UNLOCK(); + fclose(f); + return ret; +} + + +int dmres_write_resfile(const char *filename) +{ + int ret; + DMResource *node; + FILE *f = fopen(filename, "w"); + if (f == NULL) + return DMERR_FOPEN; + + DMRES_LOCK(); + + for (node = dfResources; node != NULL; node = node->next) + { + if (fprintf(f, "%s|%08x\n", node->filename, node->flags) < 0) + { + ret = DMERR_FWRITE; + goto error; + } + } + +error: + DMRES_UNLOCK(); + fclose(f); + return ret; +} + + +/* Resources subsystem initialization and shutdown routines + */ +int dmres_init(const char *filename, const char *path, int flags) +{ + // Check if we are already initialized + if (dfResInitialized) + return DMERR_ALREADY_INIT; + + dfResFlags = flags; + dfResPath = dm_strdup((path != NULL) ? path : DMRES_DATA_PATH); + dfResourcesMutex = dmCreateMutex(); + + if (flags & DRF_USE_PACK) + { +#ifdef DMRES_PACKFS + int ret; + DMPackEntry *node; + + dfResPackFilename = dm_strdup((filename != NULL) ? filename : DMRES_DATA_PACK); + + // Initialize PACK, open as read-only + ret = dm_pack_open(dfResPackFilename, &dfResPackFile, TRUE); + if (ret != DMERR_OK) + { + dmError("Error opening PACK file '%s', #%i: %s\n", + dfResPackFilename, ret, dmErrorStr(ret)); + + return DMERR_INIT_FAIL; + } + + // Initialize resources from a PACK file + for (node = dfResPackFile->entries; node != NULL; node = node->next) + { + DMResource *res = dmres_new(node->filename, node->resFlags, node->size); + if (res == NULL) + { + dmError("Could not allocate memory for resource node '%s' [0x%08x], %d.\n", + node->filename, node->resFlags, node->size); + return DMERR_INIT_FAIL; + } + + dmres_insert(res); + } + +#else + // PACK not compiled in, FAIL! + return DMERR_INIT_FAIL; +#endif + } + else + { + // Initialize resources from a resource directory + char *resFilename = dm_strdup_printf("%s%s", dfResPath, DMRES_RES_FILE); + int ret = dmres_load_resfile(resFilename); + dmFree(resFilename); + + if (ret != DMERR_OK) + return DMERR_INIT_FAIL; + } + + + // Initialization complete + dfResInitialized = TRUE; + return DMERR_OK; +} + + +void dmres_close(void) +{ + DMResource *node; + DMRES_LOCK(); + + if (!dfResInitialized) + return; + + // Shutdown possible subsystems +#ifdef DMRES_PACKFS + if (dfResFlags & DRF_USE_PACK) + { + int res = dm_pack_close(dfResPackFile); + if (res != DMERR_OK) + { + dmError("Error closing PACK, #%i: %s\n", + res, dmErrorStr(res)); + } + + dmFree(dfResPackFilename); + } +#endif + + // Free resource entries + node = dfResources; + while (node != NULL) + { + DMResource *next = node->next; + dmres_free(node); + node = next; + } + + // Etc. + dmFree(dfResPath); + DMRES_UNLOCK(); + dmDestroyMutex(dfResourcesMutex); + dfResInitialized = FALSE; +} + + +BOOL dmres_preload(int *loaded, int *total) +{ + static DMResource *dfPreload = NULL; + + DMRES_LOCK(); + + if (dfPreload == NULL) + { + DMResource *node; + dfPreload = dfResources; + *loaded = 0; + *total = 0; + for (node = dfResources; node != NULL; node = node->next) + { + if (node->flags & DMF_PRELOAD) + (*total)++; + } + } + + if (dfPreload != NULL) + { + if (dfPreload->flags & DMF_PRELOAD) + { + (*loaded)++; + } + + dfPreload = dfPreload->next; + } + + DMRES_UNLOCK(); + + return (*total) == (*loaded); +} + + +void dmres_prune(int agems, int flags) +{ + DMResource *node, *next; + int currtime = time(NULL); + DMRES_LOCK(); + + node = dfResources; + while (node != NULL) + { + next = node->next; + // Check if node has refcount of 0 and is + // not marked as persistent resource + if (node->refcount == 0 && (node->flags & DMF_PERSIST) == 0) + { + // Check if we match either one of atime or mtime + if (((flags & DMPRUNE_ATIME) && + currtime - node->atime >= agems) || + ((flags & DMPRUNE_MTIME) && + currtime - node->mtime >= agems)) + { + dmres_delete(node); + } + } + node = next; + } + + DMRES_UNLOCK(); +} + + +/* Helper resource access routines + */ +int dmf_read_str(DMResource *f, Uint8 *s, size_t l) +{ + return dmfread(s, sizeof(Uint8), l, f) == l; +} + + +#define DM_DEFINE_FUNC(xname, xtype, xmacro) \ +BOOL dmf_read_ ## xname (DMResource *f, xtype *v) { \ + xtype result; \ + if (dmfread(&result, sizeof( xtype ), 1, f) != 1) \ + return FALSE; \ + *v = DM_ ## xmacro ## _TO_NATIVE (result); \ + return TRUE; \ +} + +DM_DEFINE_FUNC(le16, Uint16, LE16) +DM_DEFINE_FUNC(le32, Uint32, LE32) + +DM_DEFINE_FUNC(be16, Uint16, BE16) +DM_DEFINE_FUNC(be32, Uint32, BE32) + +#ifdef DM_HAVE_64BIT +DM_DEFINE_FUNC(le64, Uint64, LE64) +DM_DEFINE_FUNC(be64, Uint64, BE64) +#endif diff -r 000000000000 -r 32250b436bca dmres.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmres.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,141 @@ +/* + * DMLib + * -- Resource management + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011-2012 Tecnic Software productions (TNSP) + */ +#ifndef DMRES_H +#define DMRES_H + +#include "dmlib.h" + +#ifdef DMRES_PACKFS +#include +#include "dmpack.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Constants + */ +enum +{ + DMPRUNE_ATIME = 0x0001, + DMPRUNE_MTIME = 0x0002, +}; + +enum +{ + DRF_USE_PACK = 0x0001, +}; + +enum +{ + DMF_PRELOAD = 0x0001, // Preload this resource + DMF_PERSIST = 0x0002, // Persist loaded resource (only freed at shutdown/explicit prune) + //DMF_STREAM = 0x0004, // This resource is streamed + + DMF_LOADED = 0x1000, // Resource has been loaded +}; + + +/* Typedefs and structures + */ +struct DMResourceOps; + +typedef struct _DMResource +{ + // Timestamps (in seconds from time()) + int mtime, // When resource was loaded + atime; // Last accessed (dmres_ref()/unref) + int refcount; // Reference count + + int flags; // Resource flags (DMF_*) + char *filename; + + size_t dataSize; // Size of data + off_t dataOffset; // Current offset in data + Uint8 *data; // Pointer to data + + int error; // Error code + + struct DMResourceOps *fops; // Pointer to file handling functions struct + struct _DMResource *next, *prev; + + FILE * fh; +} DMResource; + + +typedef struct DMResourceOps +{ + int (*ferror)(DMResource *); + int (*fseek)(DMResource *, const off_t, const int); + off_t (*fsize)(DMResource *); + off_t (*ftell)(DMResource *); + BOOL (*feof)(DMResource *); + int (*fgetc)(DMResource *); + size_t (*fread)(void *, const size_t, const size_t, DMResource *); + + int (*fopen)(DMResource *); + void (*fclose)(DMResource *); + int (*preload)(DMResource *); +} DMResourceOps; + + +/* Functions + */ +int dmres_init(const char *filename, const char *path, int flags); +void dmres_close(void); + +void dmres_prune(int agems, int flags); +int dmres_preload(); + +int dmres_load_resfile(const char *filename); +int dmres_write_resfile(const char *filename); + +DMResource * dmres_find(const char *filename); +int dmres_ref(DMResource *); +int dmres_unref(DMResource *); + + +// Opening and closing resources +DMResource * dmf_open(const char *); +DMResource * dmf_open_memio(const char *, Uint8 *buf, size_t len); +#ifdef DMRES_STDIO +DMResource * dmf_create_stdio(const char *); +DMResource * dmf_create_stdio_stream(FILE *); +#endif +void dmf_close(DMResource *); + + +// Basic resource access functions +int dmferror(DMResource *); +int dmfseek(DMResource *, const off_t, const int); +off_t dmfsize(DMResource *); +off_t dmftell(DMResource *); +BOOL dmfeof(DMResource *); +int dmfgetc(DMResource *); +size_t dmfread(void *, const size_t, const size_t, DMResource *); + + +// Specialized functions for endianess based reading etc +int dmf_read_str(DMResource *, Uint8 *, size_t); +BOOL dmf_read_be16(DMResource *, Uint16 *); +BOOL dmf_read_be32(DMResource *, Uint32 *); +BOOL dmf_read_le16(DMResource *, Uint16 *); +BOOL dmf_read_le32(DMResource *, Uint32 *); + +#ifdef DM_HAVE_64BIT +BOOL dmf_read_be64(DMResource *, Uint64 *); +BOOL dmf_read_le64(DMResource *, Uint64 *); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif // DMRES_H diff -r 000000000000 -r 32250b436bca dmscaledblit.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmscaledblit.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,80 @@ +/* + * DMLib + * -- Sprite / surface blitting function template + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011-2012 Tecnic Software productions (TNSP) + */ + +int DM_SCALED_NAME (SDL_Surface *src, + const int x0, const int y0, + const int dwidth, const int dheight, + SDL_Surface *dst) +{ + int yc; + DMFixedPoint32 xv, yv, dx, dy; + DMQValue xr, yr; + +#ifdef DM_SCALED_VARS + DM_SCALED_VARS +#endif + + // Clip coordinates + if (dmClipCoord(&xr, x0, src->w, dwidth, + dst->clip_rect.x, dst->clip_rect.x + dst->clip_rect.w) + || + dmClipCoord(&yr, y0, src->h, dheight, + dst->clip_rect.y, dst->clip_rect.y + dst->clip_rect.h)) + return -1; + +#ifdef DM_SCALED_INIT + DM_SCALED_INIT +#endif + + // Calculate "final" initial source bitmap offsets + FP_CONV(dy, yr.voffs); + FP_MUL_R(yv, dy, yr.vdelta); + + FP_CONV(dx, xr.voffs); + FP_MUL_R(dx, dx, xr.vdelta); + + // Take pitch into account + const int xadd = xr.vadd - dst->clip_rect.w + dst->clip_rect.x + (dst->pitch / DM_SCALED_DST_BYTES); + + // Blit scaled + DM_SCALED_DST_TYPE * dp = ((DM_SCALED_DST_TYPE *) dst->pixels) + (yr.v0 * dst->pitch) / DM_SCALED_DST_BYTES + xr.v0; + for (yc = yr.v0; yc < yr.v1; yc++) + { + const DM_SCALED_SRC_TYPE * sp = ((DM_SCALED_SRC_TYPE *) src->pixels) + (FP_GETH(yv) * src->pitch) / DM_SCALED_SRC_BYTES; + int xc; + +#ifdef DM_SCALED_INNER_INIT + DM_SCALED_INNER_INIT +#endif + + for (xv.dw = dx.dw, xc = xr.v0; xc < xr.v1; xc++) + { + DM_SCALED_INNER + FP_ADD(xv, xr.vdelta); + } + FP_ADD(yv, yr.vdelta); + dp += xadd; + } + +#ifdef DM_SCALED_FINISH + DM_SCALED_FINISH +#endif + + return 0; +} + + +#undef DM_SCALED_NAME +#undef DM_SCALED_SRC_BYTES +#undef DM_SCALED_DST_BYTES +#undef DM_SCALED_SRC_TYPE +#undef DM_SCALED_DST_TYPE +#undef DM_SCALED_VARS +#undef DM_SCALED_INIT +#undef DM_SCALED_INNER_INIT +#undef DM_SCALED_INNER +#undef DM_SCALED_FINISH diff -r 000000000000 -r 32250b436bca dmstring.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmstring.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,61 @@ +#include "dmlib.h" +#include + +/* strdup with a NULL check + */ +char *dm_strdup(const char *s) +{ + char *res; + if (s == NULL) + return NULL; + + if ((res = dmMalloc(strlen(s) + 1)) == NULL) + return NULL; + + strcpy(res, s); + return res; +} + + +/* Simulate a sprintf() that allocates memory + */ +char *dm_strdup_vprintf(const char *fmt, va_list args) +{ + int size = 64; + char *buf; + + if ((buf = dmMalloc(size)) == NULL) + return NULL; + + while (1) + { + int n; + va_list ap; + va_copy(ap, args); + n = vsnprintf(buf, size, fmt, ap); + va_end(ap); + + if (n > -1 && n < size) + return buf; + if (n > -1) + size = n + 1; + else + size *= 2; + + if ((buf = dmRealloc(buf, size)) == NULL) + return NULL; + } +} + + +char *dm_strdup_printf(const char *fmt, ...) +{ + char *res; + va_list ap; + + va_start(ap, fmt); + res = dm_strdup_vprintf(fmt, ap); + va_end(ap); + + return res; +} diff -r 000000000000 -r 32250b436bca dmtext.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmtext.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,36 @@ +#include "dmlib.h" + +void dmDrawTTFTextConst(SDL_Surface *screen, TTF_Font *font, SDL_Color col, int x, int y, const char *fmt) +{ + SDL_Surface *text = TTF_RenderText_Blended(font, fmt, col); + if (text) + { + SDL_Rect rect; + rect.x = x; + rect.y = y; + rect.w = text->w; + rect.h = text->h; + SDL_BlitSurface(text, NULL, screen, &rect); + SDL_FreeSurface(text); + } +} + +void dmDrawTTFTextVA(SDL_Surface *screen, TTF_Font *font, SDL_Color col, int x, int y, const char *fmt, va_list ap) +{ + char *tmp = dm_strdup_vprintf(fmt, ap); + if (tmp != NULL) + { + dmDrawTTFTextConst(screen, font, col, x, y, tmp); + dmFree(tmp); + } +} + +void dmDrawTTFText(SDL_Surface *screen, TTF_Font *font, SDL_Color col, int x, int y, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + dmDrawTTFTextVA(screen, font, col, x, y, fmt, ap); + va_end(ap); +} + diff -r 000000000000 -r 32250b436bca dmvecmat.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmvecmat.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,217 @@ +/* + * DMLib + * -- Vector and matrix functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#include "dmvecmat.h" +#include + + +void dm_vector_copy(DMVector *vd, DMVector *vs) +{ + memcpy(vd, vs, sizeof(DMVector)); +} + + +/* Basic vector operations + */ +void dm_vector_add(DMVector *vr, const DMVector *v2) +{ + vr->x += v2->x; + vr->y += v2->y; + vr->z += v2->z; +} + + +void dm_vector_add_r(DMVector *vr, const DMVector *v1, const DMVector *v2) +{ + vr->x = v1->x + v2->x; + vr->y = v1->y + v2->y; + vr->z = v1->z + v2->z; +} + + +void dm_vector_sub(DMVector *vr, const DMVector *v2) +{ + vr->x -= v2->x; + vr->y -= v2->y; + vr->z -= v2->z; +} + + +void dm_vector_sub_r(DMVector *vr, const DMVector *v1, const DMVector *v2) +{ + vr->x = v1->x - v2->x; + vr->y = v1->y - v2->y; + vr->z = v1->z - v2->z; +} + + +/* Return vector length + */ +DMFloat dm_vector_length(const DMVector *vs) +{ + return sqrt((vs->x * vs->x) + (vs->y * vs->y) + (vs->z * vs->z)); +} + + +/* Normalize vector + */ +void dm_vector_normalize(DMVector *vec) +{ + DMFloat l = dm_vector_length(vec); + + if (l > 0.0f) + { + l = 1.0f / l; + vec->x *= l; + vec->y *= l; + vec->z *= l; + } +} + + +/* Scale given vector + */ +void dm_vector_scale(DMVector * vec, const DMFloat k) +{ + vec->x *= k; + vec->y *= k; + vec->z *= k; +} + + +/* Returns dot-product of two given vectors + */ +DMFloat dm_vector_dot(const DMVector *v1, const DMVector *v2) +{ + return (v1->x * v2->x) + (v1->y * v2->y) + (v1->z * v2->z); +} + + +/* Returns cross-product of two given vectors + */ +void dm_vector_cross(DMVector *vr, const DMVector *v1, const DMVector *v2) +{ + vr->x = (v1->y * v2->z) - (v1->z * v2->y); + vr->y = (v1->z * v2->x) - (v1->x * v2->z); + vr->z = (v1->x * v2->y) - (v1->y * v2->x); +} + + +/* Multiply given vector with a matrix + */ +void dm_vector_mul_by_mat(DMVector *vd, const DMVector *vs, const DMMatrix *mat) +{ + vd->x = (vs->x * mat->m[0][0]) + (vs->y * mat->m[1][0]) + (vs->z * mat->m[2][0]); + vd->y = (vs->x * mat->m[0][1]) + (vs->y * mat->m[1][1]) + (vs->z * mat->m[2][1]); + vd->z = (vs->x * mat->m[0][2]) + (vs->y * mat->m[1][2]) + (vs->z * mat->m[2][2]); +} + + +/* Multiply list of given vectors with given matrix. + */ +void dm_vector_mul_by_mat_n(DMVector *list, const int nlist, const DMMatrix *mat) +{ + int i; + + for (i = 0; i < nlist; i++) + { + DMVector q; + memcpy(&q, &list[i], sizeof(DMVector)); + + list[i].x = (q.x * mat->m[0][0]) + (q.y * mat->m[1][0]) + (q.z * mat->m[2][0]); + list[i].y = (q.x * mat->m[0][1]) + (q.y * mat->m[1][1]) + (q.z * mat->m[2][1]); + list[i].z = (q.x * mat->m[0][2]) + (q.y * mat->m[1][2]) + (q.z * mat->m[2][2]); + } +} + + +/* Set matrix to unit-matrix + */ +void dm_matrix_unit(DMMatrix *mat) +{ + memset(mat, 0, sizeof(DMMatrix)); + mat->m[0][0] = 1.0f; + mat->m[1][1] = 1.0f; + mat->m[2][2] = 1.0f; +} + + +/* Transpose the matrix mat2 to mat1 + */ +void dm_matrix_transpose(DMMatrix *mat1, const DMMatrix *mat2) +{ + int i, j; + + for (i = 0; i < DM_MATRIX_SIZE; i++) + for (j = 0; j < DM_MATRIX_SIZE; j++) + mat1->m[i][j] = mat2->m[j][i]; +} + + +/* Multiply matrices mat1 and mat2, putting result into mat1 + */ +void dm_matrix_mul(DMMatrix *mat1, const DMMatrix *mat2) +{ + int i, j; + DMMatrix tmpM; + + for (i = 0; i < DM_MATRIX_SIZE; i++) + for (j = 0; j < DM_MATRIX_SIZE; j++) + tmpM.m[i][j] = + (mat1->m[i][0] * mat2->m[0][j]) + + (mat1->m[i][1] * mat2->m[1][j]) + + (mat1->m[i][2] * mat2->m[2][j]); + + memcpy(mat1, &tmpM, sizeof(DMMatrix)); +} + + +/* Multiply given list of matrices (size of nMatrices units) with given matrix. + */ +void dm_matrix_mul_n(DMMatrix * list, const int nlist, const DMMatrix *mat) +{ + int i; + for (i = 0; i < nlist; i++) + dm_matrix_mul(&list[i], mat); +} + + +/* Optimized rotation matrix creation + */ +void dm_matrix_rot(DMMatrix *mat, + const DMFloat sx, const DMFloat sy, const DMFloat sz, + const DMFloat cx, const DMFloat cy, const DMFloat cz) +{ + const DMFloat + q = cx * sz, + l = cx * cz, + i = sx * sz, + j = sx * cz; + + mat->m[0][0] = cy * cz; + mat->m[0][1] = cy * sz; + mat->m[0][2] = -sy; + + + mat->m[1][0] = (sy * j) - q; + mat->m[1][1] = (sy * i) + l; + mat->m[1][2] = sx * cy; + + + mat->m[2][0] = (sy * l) + i; + mat->m[2][1] = (sy * q) - j; + mat->m[2][2] = cx * cy; +} + + +/* Make rotation matrix from given angles (radians) + */ +void dm_matrix_rot_a(DMMatrix *mat, const DMFloat ax, const DMFloat ay, const DMFloat az) +{ + dm_matrix_rot(mat, sin(ax), sin(ay), sin(az), cos(ax), cos(ay), cos(az)); +} + + diff -r 000000000000 -r 32250b436bca dmvecmat.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmvecmat.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,61 @@ +/* + * DMLib + * -- Vector and matrix functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#ifndef DMVECMAT_H +#define DMVECMAT_H +#include "dmlib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DM_MATRIX_SIZE (3) + +typedef struct +{ + DMFloat x, y, z; +} DMVector; + +typedef struct +{ + DMFloat m[DM_MATRIX_SIZE][DM_MATRIX_SIZE]; +} DMMatrix; + +void dm_vector_copy(DMVector *vd, DMVector *vs); + +void dm_vector_add(DMVector *vr, const DMVector *v2); +void dm_vector_add_r(DMVector *vr, const DMVector *v1, const DMVector *v2); +void dm_vector_sub(DMVector *vr, const DMVector *v2); +void dm_vector_sub_r(DMVector *vr, const DMVector *v1, const DMVector *v2); + +DMFloat dm_vector_length(const DMVector *vs); +void dm_vector_normalize(DMVector *vs); +void dm_vector_scale(DMVector * vec, const DMFloat k); +DMFloat dm_vector_dot(const DMVector *v1, const DMVector *v2); +void dm_vector_cross(DMVector *vr, const DMVector *v1, const DMVector *v2); + + +void dm_vector_mul_by_mat(DMVector *vd, const DMVector *vs, const DMMatrix *mat); +void dm_vector_mul_by_mat_n(DMVector *list, const int nlist, const DMMatrix *mat); + +void dm_matrix_unit(DMMatrix *mat); +void dm_matrix_transpose(DMMatrix *mat1, const DMMatrix *mat2); + +void dm_matrix_mul(DMMatrix *mat1, const DMMatrix *mat2); +void dm_matrix_mul_n(DMMatrix *list, const int nlist, const DMMatrix *mat); + +void dm_matrix_rot(DMMatrix *mat, + const DMFloat sx, const DMFloat sy, const DMFloat sz, + const DMFloat cx, const DMFloat cy, const DMFloat cz); + +void dm_matrix_rot_a(DMMatrix *mat, + const DMFloat ax, const DMFloat ay, const DMFloat az); + + +#ifdef __cplusplus +} +#endif +#endif // DMVECMAT_H diff -r 000000000000 -r 32250b436bca efu.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/efu.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,526 @@ +#define USE_IMG + +#include "dmlib.h" +#include "dmargs.h" +#include "dmvecmat.h" +#include +#ifdef USE_IMG +#include +#endif + +#define DM_COLORS (256) + +char *optFontFile = "font.ttf", + *optBitmapFilename = "tnsp.pcx"; +BOOL optBenchmark = FALSE; +int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE; +int optScrWidth = 640, optScrHeight = 480, optFontSize = 20, optScrDepth = 32; +int optBenchmarkLen = 20; + +DMOptArg optList[] = { + { 0, '?', "help", "Show this help", OPT_NONE }, + { 2, 'v', "verbose", "Be more verbose", OPT_NONE }, + { 3, 'f', "full", "Fullscreen", OPT_NONE }, + { 4, 'h', "hw", "Use SDL hardware surface", OPT_NONE }, + { 5, 's', "size", "Initial window size/resolution -s 640x480", OPT_ARGREQ }, + { 6, 'd', "depth", "Color depth of mode/window in bits (8/15/16/32)", OPT_ARGREQ }, + { 7, 'b', "bench", "Run in benchmark mode", OPT_NONE }, +}; + +const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void argShowHelp() +{ + dmArgsPrintHelp(stdout, optList, optListN); +} + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + switch (optN) { + case 0: + argShowHelp(); + exit(0); + break; + + case 2: + dmVerbosity++; + break; + + case 3: + optVFlags |= SDL_FULLSCREEN; + break; + + case 6: + if (optArg) + optScrDepth = atoi(optArg); + break; + + case 5: + { + int w, h; + if (sscanf(optArg, "%dx%d", &w, &h) == 2) + { + if (w < 320 || h < 200 || w > 3200 || h > 3200) + { + dmError("Invalid width or height: %d x %d\n", w, h); + return FALSE; + } + optScrWidth = w; + optScrHeight = h; + } + else + { + dmError("Invalid size argument '%s'.\n", optArg); + return FALSE; + } + } + break; + + case 7: + optBenchmark = TRUE; + break; + + default: + dmError("Unknown option '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +void DM_MakePalette(SDL_Surface *scr) +{ + SDL_Color pal[DM_COLORS]; + int n; + + for (n = 0; n < 256; n++) + { + pal[n].r = n; + pal[n].g = n; + pal[n].b = n; + } + + SDL_SetColors(scr, pal, 0, DM_COLORS); +} + + +void DM_PrintRect(FILE *f, SDL_Rect *r) +{ + fprintf(f, "SDL_Rect <%d, %d : %d, %d>\n", + r->x, r->y, r->w, r->h); +} + +BOOL DM_InitializeVideo(SDL_Surface **screen) +{ + *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE); + if (*screen == NULL) + { + dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError()); + return FALSE; + } + +#if 0 + SDL_Rect r; + r.x = -50; + r.y = 50; + r.w = 700; + r.h = 300; + DM_PrintRect(stderr, &r); + SDL_SetClipRect(*screen, &r); + DM_PrintRect(stderr, &r); + DM_PrintRect(stderr, &((*screen)->clip_rect)); +#endif + + return TRUE; +} + +void DM_Random(SDL_Surface *screen, int q) +{ + Uint8 *pix = screen->pixels; + int xc, yc; + + for (yc = 0; yc < screen->h; yc++) + { + Uint8 *dp = pix; + + for (xc = 0; xc < screen->w; xc++) + *dp++ = yc + (xc ^ q) + (yc & q); + + pix += screen->pitch; + } +} + +void DM_Perlin(SDL_Surface *screen, float f) +{ + Uint8 *pix = screen->pixels; + int xc, yc; + + for (yc = 0; yc < screen->h; yc++) + { + Uint8 *dp = pix; + + for (xc = 0; xc < screen->w; xc++) + { + *dp++ = 128 + dmPerlinNoise2D(xc, yc, 0.01, 0.1, 3) / 34.0; + } + + pix += screen->pitch; + } +} + + +#define QWIDTH 256 +#define QHEIGHT 160 + +typedef Uint8 DMBlockMap[QHEIGHT][QWIDTH]; + + +static DMFloat dmClip(DMFloat a) +{ + return (a < 0.0f ? 0.0f : (a > 1.0f ? 1.0f : a)); +} + + +void dmMakeBumpMap(DMBlockMap map, DMFloat q, DMFloat m) +{ + int x, y; + for (y = 0; y < QHEIGHT; y++) + for (x = 0; x < QWIDTH; x++) + { + DMFloat f = 0.40f + dmPerlinNoise2D(x, y, 1.1f, q, 2); + map[y][x] = (int) (dmClip(f) * m); + } +} + + +void dmShadowTraceHeightMap(DMBlockMap lightMap, DMBlockMap pheightMap, DMVector *light) +{ + int i, j; + + light->z = 150; + + for (j = 0; j < QHEIGHT; j++) + for (i = 0; i < QWIDTH; i++) + { + DMVector vr, vl, va; + DMFloat vrayLen, vfactor; + int vlen; + BOOL wasHit; + + // Perform shadow occlusion via simplistic raytracing + vr.x = i; + vr.y = j; + vr.z = 200; //light->z; // - 10.0; + + // Calculate light vector vector + dm_vector_sub_r(&vl, &vr, light); + vrayLen = dm_vector_length(&vl); + +#if 1 + dm_vector_copy(&va, &vl); + dm_vector_normalize(&va); + dm_vector_copy(&vr, light); + + vlen = 0; + wasHit = FALSE; + do + { + float h; + + // If ray is inside the heightmap, get value + if (vr.x >= 0 && vr.y >= 0 && vr.x < QWIDTH && vr.y < QHEIGHT) + h = pheightMap[(int) vr.y][(int) vr.x]; + else + break; + + // Check for hits + if (h > vr.z) + wasHit = TRUE; + else + { + // Move forwards + dm_vector_add(&vr, &va); + vlen++; + } + } + while (!wasHit && vlen <= vrayLen); + + // Check if the ray hit something, e.g. is this point occluded? + if (wasHit && vlen < vrayLen) + { + vfactor = vlen * 0.05; + } + else + vfactor = vlen * 0.001; +#endif + +#if 0 + { + /* Calculate light's intensity based on the angle it "hits" + * + * 1) Calculate the vectors that form the imaginary "plane" + * 2) Cross-product -> normal vector of the plane + * 2) Normalize the normal vector + * 3) Calculate light vector's hit angle by dot product + */ + DMVector v1, v2; + DMFloat c; + + v1.x = 2.0f; + v1.y = 0.0f; + v1.z = (DMFloat) (pheightMap[j][i] - pheightMap[j][i + 1]); + + v2.x = 0.0f; + v2.y = 2.0f; + v2.z = (DMFloat) (pheightMap[j][i] - pheightMap[j + 1][i]); + + dm_vector_cross(&vr, &v1, &v2); + dm_vector_normalize(&vr); + dm_vector_normalize(&vl); + c = dm_vector_dot(&vl, &vr); + + vrayLen = 255 - (vrayLen * 0.1) * vrayLen + (c * 128.0f) + (vfactor * vfactor * 1255); + } +#else + vrayLen = 255 - vrayLen * vrayLen * (vfactor * vfactor); + if (vrayLen < 0) vrayLen = 0; + vrayLen -= pheightMap[j][i]; +#endif + + // Clip result + if (vrayLen < 0) + vrayLen = 0; + else if (vrayLen > 255.0f) + vrayLen = 255.0f; + + lightMap[j][i] = vrayLen; + } +} + + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen = NULL, *bmap = NULL, *logo = NULL; + TTF_Font *font = NULL; + SDL_Color fontcol={255,155,155,0}; + Uint32 lcol; + SDL_Event event; + int mouseX, mouseY; + BOOL initSDL = FALSE, initTTF = FALSE, exitFlag, showMap = FALSE, initImage = FALSE; + + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, NULL, FALSE)) + exit(1); + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) + { + dmError("Could not initialize SDL: %s\n", SDL_GetError()); + goto error_exit; + } + initSDL = TRUE; + + + if (TTF_Init() < 0) + { + dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError()); + goto error_exit; + } + initTTF = TRUE; + + font = TTF_OpenFont(optFontFile, optFontSize); + if (font == NULL) + { + dmError("Could not load TTF font '%s' (%d): %s\n", + optFontFile, optFontSize, SDL_GetError()); + goto error_exit; + } + TTF_SetFontStyle(font, TTF_STYLE_NORMAL); + +#ifdef USE_IMG + if (IMG_Init(IMG_INIT_PNG) < 0) + { + dmError("Could not initialize SDL_image: %s\n", SDL_GetError()); + goto error_exit; + } + initImage = TRUE; + + SDL_RWops *rwop = SDL_RWFromFile(optBitmapFilename, "rb"); + if (rwop == NULL) + { + dmError("Could not open file '%s'.\n", optBitmapFilename); + goto error_exit; + } + logo = IMG_LoadPCX_RW(rwop); + SDL_RWclose(rwop); + if (logo == NULL) + { + dmError("Could not load image file '%s'.\n", optBitmapFilename); + goto error_exit; + } +#endif + + if (optBenchmark) + { + screen = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0); + if (screen == NULL) + { + dmError("Could not create screen surface.\n"); + goto error_exit; + } + + dmMsg(0, "Benchmark mode, not opening window.\n"); + } + else + { + if (!DM_InitializeVideo(&screen)) + goto error_exit; + + SDL_WM_SetCaption("Halleluja", "DMT"); + } + + dmPerlinInit(); + + bmap = SDL_CreateRGBSurface(SDL_SWSURFACE, QWIDTH, QHEIGHT, 8, 0, 0, 0, 0); + DM_MakePalette(bmap); + DM_Random(bmap, 15); + + DMVector light; + DMBlockMap heightMap; + light.x = light.y = 128; + light.z = 128; + dmMakeBumpMap(heightMap, 0.06, 254); + + + + lcol = dmMapRGB(screen, 255,100,100); + + int numFrames = 0, startTime = SDL_GetTicks(), endTime = 0; + exitFlag = FALSE; + + if (optBenchmark) + dmMsg(0, "Starting benchmark, running for %d seconds.\n", optBenchmarkLen); + + while (!exitFlag) + { + if (!optBenchmark) + { + while (SDL_PollEvent(&event)) + switch (event.type) + { + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: exitFlag = TRUE; break; + + case SDLK_F1: + DM_Random(bmap, (SDL_GetTicks() / 10) % 1000); + break; + + case SDLK_F2: + DM_Perlin(bmap, SDL_GetTicks() / 100); + break; + + case SDLK_F5: + showMap = !showMap; + break; + + default: + break; + } + + break; + + case SDL_VIDEORESIZE: + optScrWidth = event.resize.w; + optScrHeight = event.resize.h; + + if (!DM_InitializeVideo(&screen)) + goto error_exit; + + break; + + case SDL_VIDEOEXPOSE: + break; + + case SDL_QUIT: + exit(0); + } + + SDL_GetMouseState(&mouseX, &mouseY); + light.x = ((DMFloat) mouseX * QWIDTH) / (DMFloat) optScrWidth; + light.y = ((DMFloat) mouseY * QHEIGHT) / (DMFloat) optScrHeight; + } + + if (!optBenchmark && SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0) + { + dmError("Can't lock surface.\n"); + goto error_exit; + } + + + float f = SDL_GetTicks() / 250.0f; + + if (showMap) + memcpy(bmap->pixels, heightMap, QWIDTH * QHEIGHT); + else + dmShadowTraceHeightMap(bmap->pixels, logo->pixels, &light); + + dmScaledBlitSurfaceAny(bmap, 0, 0, screen->w, screen->h, screen, DMD_NONE); + + + + if (!optBenchmark) + { + dmDrawTTFText(screen, font, fontcol, 0, 0, "%3.1f FPS", + (float) (numFrames * 1000.0f) / (float) (endTime - startTime)); + + if (SDL_MUSTLOCK(screen) != 0) + SDL_UnlockSurface(screen); + + SDL_Flip(screen); + SDL_Delay(10); + } + + endTime = SDL_GetTicks(); + numFrames++; + + if (optBenchmark) + { + if (endTime - startTime > optBenchmarkLen * 1000) + exitFlag = TRUE; + } + } + + // Print benchmark results + dmMsg(0, "%d frames in %d ms, fps = %1.3f\n", + numFrames, endTime - startTime, + (float) (numFrames * 1000.0f) / (float) (endTime - startTime)); + + +error_exit: + dmMsg(0, "Shutting down dmlib.\n"); + if (screen) + SDL_FreeSurface(screen); + + if (logo) + SDL_FreeSurface(logo); + + if (font) + TTF_CloseFont(font); + + if (initSDL) + SDL_Quit(); + +#ifdef USE_IMG + if (initImage) + IMG_Quit(); +#endif + + if (initTTF) + TTF_Quit(); + + return 0; +} diff -r 000000000000 -r 32250b436bca font.ttf Binary file font.ttf has changed diff -r 000000000000 -r 32250b436bca fptest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fptest.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,99 @@ +#include "dmlib.h" +#include + +#define FP_DW_SIZE 8 +#define FP_W0_SIZE 4 + +void check(const char *str, DMFixedPoint v, Uint64 dw) +{ + DMFixedPoint *q = (DMFixedPoint *) &dw; + + printf("%-15s = ", str); FP_PRINTF(v); printf("\n"); + printf("should be = "); FP_PRINTF((*q)); printf("\n"); + + if (v.dw != dw) + { + printf("ERROR! A test value differs from expected!\n"); + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + int i; + DMFixedPoint a, b, delta; + char *s; + + (void) argc; + (void) argv; + + // Check host characteristics + switch (SDL_BYTEORDER) + { + case SDL_BIG_ENDIAN: s = "big endian"; break; + case SDL_LIL_ENDIAN: s = "little endian"; break; + default: s = "unknown?"; break; + } + + printf( + "SDL byte order = %s (%d)\n\n", s, SDL_BYTEORDER); + + printf( + "sizeof(DMFixedPoint) = %d bytes\n" + "sizeof(DMFixedPoint.dw = %d bytes (should be %d)\n" + "sizeof(DMFixedPoint.w[0] = %d bytes (should be %d)\n", + sizeof(a), sizeof(a.dw), FP_DW_SIZE, + sizeof(a.w[0]), FP_W0_SIZE); + + if (sizeof(a.dw) != FP_DW_SIZE || sizeof(a.w[0]) != FP_W0_SIZE) + { + printf("ERROR! Some type sizes DO NOT MATCH!\n"); + return -1; + } + + // TEST #1 + printf("\nTEST #1: set initial values\n"); + FP_SETHL(a, 55, 0); + check("a", a, 0x0000003700000000ULL); + + FP_CONV(b, 178); + check("b", b, 0x00000000000000b2ULL); + + FP_DIV_R(delta, a, b); + check("delta", delta, 0x000000004f19e33cULL); + + // Test #2 + printf("\nTEST #2: 50 x (a + delta)\n"); + for (i = 0; i < 50; i++) + { + FP_ADD(a, delta); + } + check("a end", a, 0x00000046730e61b8ULL); + + + // Test #3 + printf("\nTEST #3: 50 x (a - delta)\n"); + for (i = 0; i < 50; i++) + { + FP_SUB(a, delta); + } + check("a end", a, 0x0000003700000000ULL); + + // Test #4 + printf("\nTEST #4: 5 x (a * delta)\n"); + for (i = 0; i < 5; i++) + { + FP_MUL(a, delta); + } + check("a end", a, 0xd644e40000000000ULL); + + // Test #5 + printf("\nTEST #4: 2 x (a / delta)\n"); + for (i = 0; i < 2; i++) + { + FP_DIV(a, delta); + } + check("a end", a, 8ULL); + + return 0; +} diff -r 000000000000 -r 32250b436bca jloadjss.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jloadjss.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,533 @@ +/* + * miniJSS - JSSMOD module loader + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2007-2009 Tecnic Software productions (TNSP) + */ +#include "jssmod.h" +#include + + +#ifdef JM_SUP_PATMODE_ALL +#define JM_SUP_PATMODE_1 1 +#define JM_SUP_PATMODE_2 1 +#define JM_SUP_PATMODE_3 1 +#define JM_SUP_PATMODE_4 1 +#define JM_SUP_PATMODE_5 1 +#endif + + +static BOOL jsGetBufData(Uint8 **buf, size_t *bufLeft, void *data, const size_t dataSize) +{ + if (*bufLeft >= dataSize) + { + memcpy(data, *buf, dataSize); + *buf += dataSize; + *bufLeft -= dataSize; + return TRUE; + } + else + return FALSE; +} + + +static BOOL jsGetBufByte(Uint8 **buf, size_t *bufLeft, Uint8 *data) +{ + if (*bufLeft > 0) + { + *data = **buf; + (*buf)++; + (*bufLeft)--; + return TRUE; + } + else + return FALSE; +} + + +#define JSGETBUF(XV, XT) if (!jsGetBufData(buf, bufLeft, XV, sizeof(XT))) return DMERR_OUT_OF_DATA +#define JSGETBYTE(XV) if (!jsGetBufByte(buf, bufLeft, XV)) return DMERR_OUT_OF_DATA + + +#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_1) || defined(JM_SUP_PATMODE_3) +static int jssGetConvertedNote(Uint8 **buf, size_t *bufLeft, JSSNote *note) +{ + Uint8 tmp; + + JSGETBYTE(&tmp); + + if (tmp == 127) + note->note = jsetNoteOff; + else if (tmp == 0) + note->note = jsetNotSet; + else + note->note = tmp - 1; + + JSGETBYTE(&tmp); + note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet; + + JSGETBYTE(&tmp); + note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet; + + JSGETBYTE(&tmp); + note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet; + + JSGETBYTE(&tmp); + note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp; + + return DMERR_OK; +} +#endif + + +#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_2) || defined(JM_SUP_PATMODE_4) +static int jssGetCompressedNote(Uint8 **buf, size_t *bufLeft, JSSNote *note) +{ + Uint8 packb, tmp; + + JSGETBYTE(&packb); + if (packb & 0x80) + { + if (packb & COMP_NOTE) + { + JSGETBYTE(&tmp); + if (tmp == 127) + note->note = jsetNoteOff; + else + note->note = tmp; + } + + if (packb & COMP_INSTRUMENT) + { + JSGETBYTE(&tmp); + note->instrument = tmp; + } + + if (packb & COMP_VOLUME) + { + JSGETBYTE(&tmp); + note->volume = tmp; + } + + if (packb & COMP_EFFECT) + { + JSGETBYTE(&tmp); + note->effect = tmp; + note->param = 0; + } + + if (packb & COMP_PARAM) + { + JSGETBYTE(&tmp); + note->param = tmp; + } + } + else + { + tmp = packb; + + if (tmp == 127) + note->note = jsetNoteOff; + else if (tmp == 0) + note->note = jsetNotSet; + else + note->note = tmp - 1; + + JSGETBYTE(&tmp); + note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet; + + JSGETBYTE(&tmp); + note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet; + + JSGETBYTE(&tmp); + note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet; + + JSGETBYTE(&tmp); + note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp; + } + + return DMERR_OK; +} +#endif + + +#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_2) +static int jssGetPatternCompHoriz(Uint8 *buf, size_t *bufLeft, 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++) + { + JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; + int res = jssGetCompressedNote(&buf, bufLeft, note); + if (res != DMERR_OK) + JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n", row, channel); + } + + return DMERR_OK; +} +#endif + + +#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_4) +static int jssGetPatternCompVert(Uint8 *buf, size_t *bufLeft, 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++) + { + JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; + int res = jssGetCompressedNote(&buf, bufLeft, note); + if (res != DMERR_OK) + JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n", row, channel); + } + + return DMERR_OK; +} +#endif + + +#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_1) +static int jssGetPatternRawHoriz(Uint8 *buf, size_t *bufLeft, 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++) + { + JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; + int res = jssGetConvertedNote(&buf, bufLeft, note); + if (res != DMERR_OK) + JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n", row, channel); + } + + return DMERR_OK; +} +#endif + + +#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_3) +static int jssGetPatternRawVert(Uint8 *buf, size_t *bufLeft, 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++) + { + JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; + int res = jssGetConvertedNote(&buf, bufLeft, note); + if (res != DMERR_OK) + JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n", row, channel); + } + + return DMERR_OK; +} +#endif + + +#if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_5) +static int jssGetPatternRawVertElem(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern) +{ + int row, channel; + + assert(buf != NULL); + assert(pattern != NULL); + + return DMERR_OK; +} +#endif + + +#undef JSGETBUF +#undef JSGETBYTE +#define JSGETBUF(XV, XT) do { \ + if (!jsGetBufData(&buf, &bufLeft, XV, sizeof(XT))) \ + JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA, \ + "Out of data at getting " # XT " (%d bytes)\n", sizeof(XT)); \ +} while (0) +#define JSGETBYTE(XV) if (!jsGetBufByte(&buf, &bufLeft, XV)) return DMERR_OUT_OF_DATA + + +#ifdef JM_SUP_EXT_INSTR +static void jssCopyEnvelope(JSSEnvelope *e, JSSMODEnvelope *je) +{ + int i; + + e->flags = je->flags; + e->npoints = je->npoints; + e->sustain = je->sustain; + e->loopS = je->loopS; + e->loopE = je->loopE; + + for (i = 0; i < je->npoints; i++) + { + e->points[i].frame = je->points[i].frame; + e->points[i].value = je->points[i].value; + } +} +#endif + + +int jssLoadJSSMOD(Uint8 *bufStart, const size_t bufSize, JSSModule **ppModule) +{ + JSSModule *module; + JSSMODHeader jssH; + Uint8 *buf = bufStart; + size_t bufLeft = bufSize; + int index; + + assert(ppModule != NULL); + assert(bufStart != NULL); + *ppModule = NULL; + + // Check the JSSMOD header + JSGETBUF(&jssH, JSSMODHeader); + + if (memcmp(jssH.idMagic, "JM", 2) != 0) + { + JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Not a valid JSSMOD file, header signature missing!\n"); + } + + if (jssH.idVersion != JSSMOD_VERSION) + { + JSSERROR(DMERR_VERSION, DMERR_VERSION, + "Unsupported version of JSSMOD 0x%4x, this version only supports 0x%4x!\n", + jssH.idVersion, JSSMOD_VERSION); + } + + // Allocate the module + module = jssAllocateModule(); + if (module == NULL) + { + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Could not allocate memory for module structure.\n"); + } + *ppModule = module; + + // Copy header information + module->norders = jssH.norders; + module->npatterns = jssH.npatterns; + module->nchannels = jssH.nchannels; + module->nextInstruments = jssH.nextInstruments; + module->ninstruments = jssH.ninstruments; + module->defFlags = jssH.defFlags; + module->intVersion = jssH.intVersion; + module->defRestartPos = jssH.defRestartPos; + module->defSpeed = jssH.defSpeed; + module->defTempo = jssH.defTempo; + + // Get the orders list + for (index = 0; index < module->norders; index++) + { + Sint16 order; + JSGETBUF(&order, Sint16); + module->orderList[index] = order; + } + + // Parse the patterns + for (index = 0; index < module->npatterns; index++) + { + JSSMODPattern jssP; + int result = DMERR_INVALID_DATA; + size_t bufSize; + + // Get header and check size + JSGETBUF(&jssP, JSSMODPattern); + bufSize = jssP.size; + if (bufLeft < jssP.size) + { + JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA, + "Out of data for pattern #%d.\n", index); + } + + // Allocate pattern + module->patterns[index] = jssAllocatePattern(jssP.nrows, module->nchannels); + if (module->patterns[index] == NULL) + { + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Could not allocate memory for pattern #%d.\n", index); + } + + // Get pattern data + switch (jssH.patMode) + { +#ifdef JM_SUP_PATMODE_1 + case PATMODE_RAW_HORIZ: + result = jssGetPatternRawHoriz(buf, &bufSize, module->patterns[index]); + break; +#endif +#ifdef JM_SUP_PATMODE_2 + case PATMODE_COMP_HORIZ: + result = jssGetPatternCompHoriz(buf, &bufSize, module->patterns[index]); + break; +#endif +#ifdef JM_SUP_PATMODE_3 + case PATMODE_RAW_VERT: + result = jssGetPatternRawVert(buf, &bufSize, module->patterns[index]); + break; +#endif +#ifdef JM_SUP_PATMODE_4 + case PATMODE_COMP_VERT: + result = jssGetPatternCompVert(buf, &bufSize, module->patterns[index]); + break; +#endif +#ifdef JM_SUP_PATMODE_5 + case PATMODE_RAW_ELEM: + result = jssGetPatternRawVertElem(buf, &bufSize, module->patterns[index]); + break; +#endif + default: + JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Unsupported pattern mode %d. Check compilation options.", jssH.patMode); + break; + } + + if (bufSize > 0) + { + JSSWARNING(DMERR_EXTRA_DATA, DMERR_EXTRA_DATA, + "Unparsed data after pattern (%d bytes), possibly broken file.\n", bufSize); + } + + if (result != DMERR_OK) + { + JSSERROR(result, result, "Error in unpacking pattern #%i data\n", index); + } + + buf += jssP.size; + bufLeft -= jssP.size; + } + +#ifdef JM_SUP_EXT_INSTR + // Read extended instruments + for (index = 0; index < module->nextInstruments; index++) + { + JSSMODExtInstrument jssE; + JSSExtInstrument *einst; + int i; + + JSGETBUF(&jssE, JSSMODExtInstrument); + + if ((einst = jssAllocateExtInstrument()) == NULL) + { + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Could not allocate extended instrument structure #%i\n", index); + } + + module->extInstruments[index] = einst; + + einst->nsamples = jssE.nsamples; + einst->vibratoType = jssE.vibratoType; + einst->vibratoSweep = jssE.vibratoSweep; + einst->vibratoDepth = jssE.vibratoDepth; + einst->vibratoRate = jssE.vibratoRate; + einst->fadeOut = jssE.fadeOut; + + for (i = 0; i < jsetNNotes; i++) + { + int snum = jssE.sNumForNotes[i]; + einst->sNumForNotes[i] = (snum > 0) ? snum : jsetNotSet; + } + + jssCopyEnvelope(&(einst->volumeEnv), &jssE.volumeEnv); + jssCopyEnvelope(&(einst->panningEnv), &jssE.panningEnv); + } + +#ifdef JM_SUP_INSTR + // Read sample instrument headers + for (index = 0; index < module->ninstruments; index++) + { + JSSMODInstrument jssI; + JSSInstrument *inst; + + JSGETBUF(&jssI, JSSMODInstrument); + + if ((inst = jssAllocateInstrument()) == NULL) + { + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Could not allocate instrument structure #%i\n", index); + } + + module->instruments[index] = inst; + + inst->size = jssI.size; + inst->loopS = jssI.loopS; + inst->loopE = jssI.loopE; + inst->volume = jssI.volume; + inst->flags = jssI.flags; + inst->C4BaseSpeed = jssI.C4BaseSpeed; + inst->ERelNote = jssI.ERelNote; + inst->EFineTune = jssI.EFineTune; + inst->EPanning = jssI.EPanning; + inst->hasData = jssI.hasData; + inst->convFlags = jssI.convFlags; + } + +#ifdef JM_SUP_SAMPLES + // Read sample data + for (index = 0; index < module->ninstruments; index++) + { + JSSInstrument *inst = module->instruments[index]; + + if (inst && inst->hasData) + { + size_t sz; + + // Calculate data size + if (inst->flags & jsf16bit) + sz = inst->size * sizeof(Uint16); + else + sz = inst->size * sizeof(Uint8); + + // Check if we can get as much? + if (bufLeft < sz) + { + JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA, + "Out of data for instrument sample #%d (%d < %d)\n", + index, bufLeft, sz); + } + + // Allocate + if ((inst->data = dmMalloc(sz)) == NULL) + { + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Could not allocate sample data #%d\n", index); + } + + // Copy data + memcpy(inst->data, buf, sz); + buf += sz; + bufLeft -= sz; + + // Convert, if needed + if (inst->flags & jsf16bit) + jssDecodeSample16(inst->data, inst->size, inst->convFlags); + else + jssDecodeSample8(inst->data, inst->size, inst->convFlags); + } + } +#else +#warning Not including JSSMOD sample loading! +#endif // JM_SUP_SAMPLES +#else +#warning Not including JSSMOD instrument loading! +#endif // JM_SUP_INSTR +#else +#warning Not including JSSMOD ext.instrument loading! +#endif // JM_SUP_EXT_INSTR + + return DMERR_OK; +} diff -r 000000000000 -r 32250b436bca jloadxm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jloadxm.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,791 @@ +/* + * miniJSS - Fast Tracker ][ (XM) module loader + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2007 Tecnic Software productions (TNSP) + * + * TO DO: + * - Add support for 1.02/1.03 XM-format versions. + * (Not very useful, but if it's not too hard, then do it) + */ +#include "jssmod.h" +#include + + +/* XM value limit definitions + */ +#define XM_MaxChannels (32) +#define XM_MaxPatterns (256) +#define XM_MaxOrders (255) +#define XM_MaxInstruments (128) +#define XM_MaxInstSamples (16) +#define XM_MaxEnvPoints (12) +#define XM_MaxNotes (96) +#define XM_MaxSampleVolume (64) + + +/* XM format structures + */ +typedef struct +{ + char idMagic[17]; // XM header ID "Extended Module: " + char songName[20]; // Module song name + Uint8 unUsed1A; // ALWAYS 0x1a + char trackerName[20]; // ID-string of tracker software + Uint16 version; // XM-version 0x0104 + Uint32 headSize; // Module header size, FROM THIS POINT! + Uint16 norders, // Number of orders + defRestartPos, // Default song restart position + nchannels, // Number of channels + npatterns, // Number of patterns + ninstruments, // Number of instruments + flags, /* Module flags: + bit0: 0 = Amiga frequency table + 1 = Linear frequency table + */ + defSpeed, // Default speed + defTempo; // Default tempo + Uint8 orderList[256]; // Order list +} XMHeader; + + +typedef struct +{ + Uint32 headSize; // Instrument header size (see docs!) + char instName[22]; // Name/description + Uint8 instType; // Type + Uint16 nsamples; // Number of samples +} XMInstrument1; + + +typedef struct +{ + Uint16 frame, value; +} xm_envpoint_t; + + +typedef struct +{ + Uint8 flags, npoints, sustain, loopS, loopE; + xm_envpoint_t points[XM_MaxEnvPoints]; +} xm_envelope_t; + + +typedef struct +{ + Uint32 headSize; // Header size + Uint8 sNumForNotes[XM_MaxNotes]; // Sample numbers for notes + xm_envelope_t volumeEnv, panningEnv; + Uint8 vibratoType, vibratoSweep, vibratoDepth, vibratoRate; + + Uint16 fadeOut, ARESERVED; +} XMInstrument2; + + +typedef struct +{ + Uint32 size, loopS, loopL; + Uint8 volume; + int fineTune; + Uint8 type, panning; + int relNote; + Uint8 ARESERVED; + char sampleName[22]; +} XMSample; + + +typedef struct +{ + Uint32 headSize; + Uint8 packing; + Uint16 nrows, size; +} XMPattern; + + + +/* Unpack XM-format pattern from file-stream into JSS-pattern structure + */ +#define JSGETBYTE(XV) do { \ + size--; \ + if (size < 0) \ + JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA, \ + "Unexpected end of packed pattern data.\n"); \ + XV = dmfgetc(inFile); \ +} while (0) + + +static int jssXMUnpackPattern(DMResource *inFile, int size, JSSPattern *pattern) +{ + int packb, row, channel, tmp; + JSSNote *pnote; + assert(pattern != NULL); + + pnote = pattern->data; + + for (row = 0; (row < pattern->nrows) && (size > 0); row++) + for (channel = 0; (channel < pattern->nchannels) && (size > 0); channel++) + { + JSGETBYTE(packb); + if (packb & 0x80) + { + if (packb & 0x01) + { + // PACK 0x01: Read note + JSGETBYTE(tmp); + if (tmp < 1 || tmp > 97) + pnote->note = jsetNotSet; + else if (tmp == 97) + pnote->note = jsetNoteOff; + else + pnote->note = tmp - 1; + } + + if (packb & 0x02) + { + // PACK 0x02: Read instrument + JSGETBYTE(tmp); + pnote->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet; + } + + if (packb & 0x04) + { + // PACK 0x04: Read volume + JSGETBYTE(tmp); + pnote->volume = (tmp >= 0x10) ? tmp - 0x10 : jsetNotSet; + } + + if (packb & 0x08) + { + // PACK 0x08: Read effect + JSGETBYTE(pnote->effect); + pnote->param = 0; + } + + if (packb & 0x10) + { + // PACK 0x10: Read effect parameter + JSGETBYTE(pnote->param); + if (pnote->effect == jsetNotSet && pnote->param != 0) + pnote->effect = 0; + } + } + else + { + // All data available + tmp = (packb & 0x7f); + + if (tmp < 1 || tmp > 97) + pnote->note = jsetNotSet; + else if (tmp == 97) + pnote->note = jsetNoteOff; + else + pnote->note = tmp - 1; + + // Get instrument + JSGETBYTE(tmp); + pnote->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet; + + // Get volume + JSGETBYTE(tmp); + pnote->volume = (tmp >= 0x10) ? tmp - 0x10 : jsetNotSet; + + // Get effect + JSGETBYTE(pnote->effect); + + // Get parameter + JSGETBYTE(pnote->param); + if (pnote->effect == 0 && pnote->param == 0) + pnote->effect = pnote->param = jsetNotSet; + } + pnote++; + } + + // Check the state + if (size > 0) + { + // Some data left unparsed + JSSWARNING(DMERR_EXTRA_DATA, DMERR_EXTRA_DATA, + "Unparsed data after pattern (%i bytes), possibly broken file.\n", size); + } + + return DMERR_OK; +} + + +/* Convert XM envelope structure to JSS envelope structure + */ +static int jssXMConvertEnvelope(JSSEnvelope * d, xm_envelope_t * s, char * e, int instr) +{ + int i; + (void) e; (void) instr; + + // Convert envelope points + d->points[0].frame = s->points[0].frame; + d->points[0].value = s->points[0].value; + for (i = 0; i < XM_MaxEnvPoints; i++) + { + d->points[i + 1].frame = s->points[i].frame + 1; + d->points[i + 1].value = s->points[i].value; + } + + // Convert other values + d->npoints = s->npoints + 1; + d->sustain = s->sustain + 1; + d->loopS = s->loopS + 1; + d->loopE = s->loopE + 1; + + // Check if the envelope is used + if (s->flags & 0x01) + { + // Convert envelope flags + d->flags = jenvfUsed; + if (s->flags & 0x02) + d->flags |= jenvfSustain; + + if (s->flags & 0x04) + d->flags |= jenvfLooped; + + // Check other values + if (s->npoints > XM_MaxEnvPoints) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Inst#%i/%s-env: nPoints > MAX, possibly broken file.\n", instr, e); + s->npoints = XM_MaxEnvPoints; + } + + if ((d->flags & jenvfSustain) && s->sustain > s->npoints) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Inst#%i/%s-env: iSustain > nPoints (%i > %i), possibly broken file.\n", + instr, e, s->sustain, s->npoints); + s->sustain = s->npoints; + } + + if ((d->flags & jenvfLooped) && s->loopE > s->npoints) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Inst#%i/%s-env: loopE > nPoints (%i > %i), possibly broken file.\n", + instr, e, s->loopE, s->npoints); + s->loopE = s->npoints; + } + + if ((d->flags & jenvfLooped) && s->loopS > s->loopE) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Inst#%i/%s-env: loopS > loopE (%i > %i), possibly broken file.\n", + instr, e, s->loopS, s->loopE); + s->loopS = 0; + } + } + + return DMERR_OK; +} + + +/* Load XM-format extended instrument from file-stream into JSS module's given inst + */ +static int jssXMLoadExtInstrument(DMResource *inFile, int ninst, JSSModule *module) +{ + XMInstrument1 xmI1; + off_t pos, remainder; + + // Get instrument header #1 + pos = dmftell(inFile); + dmf_read_le32(inFile, &xmI1.headSize); + dmf_read_str(inFile, (Uint8 *) &xmI1.instName, sizeof(xmI1.instName)); + xmI1.instType = dmfgetc(inFile); + dmf_read_le16(inFile, &xmI1.nsamples); + + // If there are samples, there is header #2 + if (xmI1.nsamples > 0) + { + int i, nsample, tmp; + int xmConvTable[XM_MaxInstruments + 1]; + JSSExtInstrument *pEInst; + JSSInstrument *pInst; + XMInstrument2 xmI2; + + // Allocate instrument + if ((pEInst = jssAllocateExtInstrument()) == NULL) + { + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Could not allocate extended instrument structure #%i\n", ninst); + } + + module->extInstruments[ninst] = pEInst; + + // Get instrument header #2 + dmf_read_le32(inFile, &xmI2.headSize); + dmf_read_str(inFile, (Uint8 *) &xmI2.sNumForNotes, sizeof(xmI2.sNumForNotes)); + + for (i = 0; i < XM_MaxEnvPoints; i++) + { + dmf_read_le16(inFile, &xmI2.volumeEnv.points[i].frame); + dmf_read_le16(inFile, &xmI2.volumeEnv.points[i].value); + } + + for (i = 0; i < XM_MaxEnvPoints; i++) + { + dmf_read_le16(inFile, &xmI2.panningEnv.points[i].frame); + dmf_read_le16(inFile, &xmI2.panningEnv.points[i].value); + } + + xmI2.volumeEnv.npoints = dmfgetc(inFile); + xmI2.panningEnv.npoints = dmfgetc(inFile); + + xmI2.volumeEnv.sustain = dmfgetc(inFile); + xmI2.volumeEnv.loopS = dmfgetc(inFile); + xmI2.volumeEnv.loopE = dmfgetc(inFile); + + xmI2.panningEnv.sustain = dmfgetc(inFile); + xmI2.panningEnv.loopS = dmfgetc(inFile); + xmI2.panningEnv.loopE = dmfgetc(inFile); + + xmI2.volumeEnv.flags = dmfgetc(inFile); + xmI2.panningEnv.flags = dmfgetc(inFile); + + xmI2.vibratoType = dmfgetc(inFile); + xmI2.vibratoSweep = dmfgetc(inFile); + xmI2.vibratoDepth = dmfgetc(inFile); + xmI2.vibratoRate = dmfgetc(inFile); + + dmf_read_le16(inFile, &xmI2.fadeOut); + dmf_read_le16(inFile, &xmI2.ARESERVED); + + // Skip the extra data after header #2 + remainder = xmI1.headSize - (dmftell(inFile) - pos); + if (remainder > 0) + { + JSSDEBUG("Skipping: %li\n", remainder); + dmfseek(inFile, remainder, SEEK_CUR); + } + + // Check and convert all ext instrument information +#ifndef JSS_LIGHT + pEInst->desc = jssASCIItoStr(xmI1.instName, 0, sizeof(xmI1.instName)); +#endif + jssXMConvertEnvelope(&pEInst->volumeEnv, &xmI2.volumeEnv, "vol", ninst); + jssXMConvertEnvelope(&pEInst->panningEnv, &xmI2.panningEnv, "pan", ninst); + + switch (xmI2.vibratoType) + { + case 0: pEInst->vibratoType = jvibSine; break; + case 1: pEInst->vibratoType = jvibRamp; break; + case 2: pEInst->vibratoType = jvibSquare; break; + case 3: pEInst->vibratoType = jvibRandom; break; + default: + pEInst->vibratoType = jvibSine; + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Invalid extinstrument vibrato type %d for inst #%d\n", ninst); + break; + } + pEInst->vibratoSweep = xmI2.vibratoSweep; + pEInst->vibratoDepth = xmI2.vibratoDepth; + pEInst->vibratoRate = xmI2.vibratoRate; + pEInst->fadeOut = xmI2.fadeOut; + pEInst->nsamples = xmI1.nsamples; + + // Initialize the SNumForNotes conversion table + for (i = 0; i < XM_MaxInstruments; i++) + xmConvTable[i] = jsetNotSet; + + // Read sample headers + for (nsample = 0; nsample < xmI1.nsamples; nsample++) + { + XMSample xmS; + + // Read header data + dmf_read_le32(inFile, &xmS.size); + dmf_read_le32(inFile, &xmS.loopS); + dmf_read_le32(inFile, &xmS.loopL); + xmS.volume = dmfgetc(inFile); + xmS.fineTune = (signed char) dmfgetc(inFile); + xmS.type = dmfgetc(inFile); + xmS.panning = dmfgetc(inFile); + xmS.relNote = (signed char) dmfgetc(inFile); + xmS.ARESERVED = dmfgetc(inFile); + dmf_read_str(inFile, (Uint8 *) &xmS.sampleName, sizeof(xmS.sampleName)); + + if (xmS.size > 0) + { + // Allocate sample instrument + JSSDEBUG("Allocating sample #%i/%i [%i]\n", + ninst, nsample, module->ninstruments); + + xmConvTable[nsample] = module->ninstruments; + pInst = module->instruments[module->ninstruments] = jssAllocateInstrument(); + if (pInst == NULL) + { + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Could not allocate sample #%i/%i [%i]\n", + ninst, nsample, module->ninstruments); + } + module->ninstruments++; + } else + pInst = NULL; + + // Check and convert sample information + if (pInst != NULL) + { + // Copy values + if (xmS.volume > XM_MaxSampleVolume) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Samp #%i/%i: volume > MAX\n", ninst, nsample); + xmS.volume = XM_MaxSampleVolume; + } + + pInst->volume = xmS.volume; + pInst->ERelNote = xmS.relNote; + pInst->EFineTune = xmS.fineTune; + pInst->EPanning = xmS.panning; +#ifndef JSS_LIGHT + pInst->desc = jssASCIItoStr(xmS.sampleName, 0, sizeof(xmS.sampleName)); +#endif + + // Convert flags + switch (xmS.type & 0x03) + { + case 0: pInst->flags = 0; break; + case 1: pInst->flags = jsfLooped; break; + case 2: pInst->flags = jsfLooped | jsfBiDi; break; + default: + pInst->flags = 0; + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Samp #%i/%i: Invalid sample type 0x%x\n", + ninst, nsample, xmS.type); + break; + } + + if (xmS.type & 0x10) + { + // 16-bit sample + JSFSET(pInst->flags, jsf16bit); + + pInst->size = xmS.size / sizeof(Uint16); + pInst->loopS = xmS.loopS / sizeof(Uint16); + pInst->loopE = ((xmS.loopS + xmS.loopL) / sizeof(Uint16)); + } + else + { + // 8-bit sample + pInst->size = xmS.size; + pInst->loopS = xmS.loopS; + pInst->loopE = (xmS.loopS + xmS.loopL); + } + + if (xmS.loopL == 0) + { + // Always unset loop, if loop length is zero + JSFUNSET(pInst->flags, jsfLooped); + } + + if (pInst->flags & jsfLooped) + { + if (pInst->loopS >= pInst->size) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Samp #%i/%i: loopS >= size (%d >= %d)\n", + ninst, nsample, pInst->loopS, pInst->size); + JSFUNSET(pInst->flags, jsfLooped); + } + + if (pInst->loopE > pInst->size) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Samp #%i/%i: loopE > size (%d > %d)\n", + ninst, nsample, pInst->loopE, pInst->size); + JSFUNSET(pInst->flags, jsfLooped); + } + } + + + // Allocate memory for sample data + if (pInst->flags & jsf16bit) + pInst->data = dmCalloc(pInst->size, sizeof(Uint16)); + else + pInst->data = dmCalloc(pInst->size, sizeof(Uint8)); + + if (pInst->data == NULL) + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Could not allocate sample data #%i/%i.\n", ninst, nsample); + } + } + + // Read sample data + for (nsample = 0; nsample < xmI1.nsamples; nsample++) + if (xmConvTable[nsample] != jsetNotSet) + { + pInst = module->instruments[xmConvTable[nsample]]; + if (pInst) + { + JSSDEBUG("desc....: '%s'\n" + "size....: %i\n" + "loopS...: %i\n" + "loopE...: %i\n" + "volume..: %i\n" + "flags...: %x\n", + pInst->desc, + pInst->size, pInst->loopS, pInst->loopE, + pInst->volume, pInst->flags); + + if (pInst->flags & jsf16bit) + { + // Read sampledata + if (dmfread(pInst->data, sizeof(Uint16), pInst->size, inFile) != (size_t) pInst->size) + JSSERROR(DMERR_FREAD, DMERR_FREAD, + "Error reading sampledata for instrument #%i/%i, %i words.\n", + ninst, nsample, pInst->size); + + // Convert data + jssDecodeSample16((Uint16 *) pInst->data, pInst->size, +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + (jsampDelta | jsampSwapEndianess) +#else + (jsampDelta) +#endif + ); + } + else + { + // Read sampledata + if (dmfread(pInst->data, sizeof(Uint8), pInst->size, inFile) != (size_t) pInst->size) + JSSERROR(DMERR_FREAD, DMERR_FREAD, + "Error reading sampledata for instrument #%i/%i, %i bytes.\n", + ninst, nsample, pInst->size); + + // Convert data + jssDecodeSample8((Uint8 *) pInst->data, pInst->size, + (jsampDelta | jsampFlipSign)); + } + } + } + + // Apply new values to sNumForNotes values + for (i = 0; i < XM_MaxNotes; i++) + { + tmp = xmI2.sNumForNotes[i]; + if (tmp >= 0 && tmp < xmI1.nsamples) + pEInst->sNumForNotes[i] = xmConvTable[tmp]; + else + { + pEInst->sNumForNotes[i] = jsetNotSet; + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Ext.instrument #%d sNumForNotes[%d] out of range (%d).\n", + ninst, i, tmp); + } + } + } + else + { + // We may STILL need to skip extra data after 1st instr. header + remainder = xmI1.headSize - (dmftell(inFile) - pos); + if (remainder > 0) + { + JSSDEBUG("PPM! Skipping: %li\n", remainder); + dmfseek(inFile, remainder, SEEK_CUR); + } + } + + return 0; +} + + +/* Load XM-format module from given file-stream + */ +int jssLoadXM(DMResource *inFile, JSSModule **ppModule) +{ + JSSModule *module; + XMHeader xmH; + int result, index, tmp; + + assert(ppModule != NULL); + assert(inFile != NULL); + *ppModule = NULL; + + /* Get XM-header and check it + */ + dmf_read_str(inFile, (Uint8 *) &xmH.idMagic, sizeof(xmH.idMagic)); + dmf_read_str(inFile, (Uint8 *) &xmH.songName, sizeof(xmH.songName)); + xmH.unUsed1A = dmfgetc(inFile); + dmf_read_str(inFile, (Uint8 *) &xmH.trackerName, sizeof(xmH.trackerName)); + dmf_read_le16(inFile, &xmH.version); + dmf_read_le32(inFile, &xmH.headSize); + dmf_read_le16(inFile, &xmH.norders); + dmf_read_le16(inFile, &xmH.defRestartPos); + dmf_read_le16(inFile, &xmH.nchannels); + dmf_read_le16(inFile, &xmH.npatterns); + dmf_read_le16(inFile, &xmH.ninstruments); + dmf_read_le16(inFile, &xmH.flags); + dmf_read_le16(inFile, &xmH.defSpeed); + dmf_read_le16(inFile, &xmH.defTempo); + dmf_read_str(inFile, (Uint8 *)&xmH.orderList, sizeof(xmH.orderList)); + + + // Check the fields, none of these are considered fatal + if (strncmp(xmH.idMagic, "Extended Module: ", 17) != 0) + { + JSSERROR(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED, + "Not a FT2 Extended Module (XM), ident mismatch!\n"); + } + + if (xmH.version != 0x0104) + { + JSSWARNING(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED, + "Unsupported version of XM format 0x%04x instead of expected 0x0104.\n", + xmH.version); + } + + if (xmH.unUsed1A != 0x1a) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Possibly modified or corrupted XM [%x]\n", xmH.unUsed1A); + } + + if (xmH.norders > XM_MaxOrders) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Number of orders %i > %i, possibly broken module.\n", + xmH.norders, XM_MaxOrders); + xmH.norders = XM_MaxOrders; + } + + if (xmH.norders == 0) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Number of orders was zero.\n"); + } + + if (xmH.npatterns > XM_MaxPatterns) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Number of patterns %i > %i, possibly broken module.\n", + xmH.npatterns, XM_MaxPatterns); + xmH.npatterns = XM_MaxPatterns; + } + + if (xmH.npatterns == 0) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Number of patterns was zero.\n"); + } + + if (xmH.nchannels <= 0 || xmH.nchannels > XM_MaxChannels) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Number of channels was invalid, %i (should be 1 - %i).\n", + xmH.nchannels, XM_MaxChannels); + } + + if (xmH.ninstruments <= 0 || xmH.ninstruments > XM_MaxInstruments) + { + JSSWARNING(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Number of instruments was invalid, %i (should be 1 - %i).\n", + xmH.ninstruments, XM_MaxInstruments); + } + + /* Okay, allocate a module structure + */ + module = jssAllocateModule(); + if (module == NULL) + { + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Could not allocate memory for module structure.\n"); + } + *ppModule = module; + + + // Convert and check the header data + module->moduleType = jmdtXM; + module->intVersion = xmH.version; +#ifndef JSS_LIGHT + module->moduleName = jssASCIItoStr(xmH.songName, 0, sizeof(xmH.songName)); + module->trackerName = jssASCIItoStr(xmH.trackerName, 0, sizeof(xmH.trackerName)); +#endif + module->defSpeed = xmH.defSpeed; + module->defTempo = xmH.defTempo; + module->nextInstruments = xmH.ninstruments; + module->ninstruments = 0; + module->npatterns = xmH.npatterns; + module->norders = xmH.norders; + module->nchannels = xmH.nchannels; + module->defFlags = jmdfStereo | jmdfFT2Replay; + module->defRestartPos = xmH.defRestartPos; + + if ((xmH.flags & 1) == 0) + module->defFlags |= jmdfAmigaPeriods; + + // Setup the default pannings + for (index = 0; index < jsetNChannels; index++) + module->defPanning[index] = jchPanMiddle; + + /* Read patterns + */ + for (index = 0; index < module->npatterns; index++) + { + off_t pos; + XMPattern xmP; + + // Get the pattern header + dmf_read_le32(inFile, &xmP.headSize); + xmP.packing = dmfgetc(inFile); + dmf_read_le16(inFile, &xmP.nrows); + dmf_read_le16(inFile, &xmP.size); + pos = dmftell(inFile); + + // Check the header + if (xmP.packing != 0) + JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Pattern #%i packing type unsupported (%i)\n", + index, xmP.packing); + + if (xmP.nrows == 0) + JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Pattern #%i has %i rows, invalid data.\n", + index, xmP.nrows); + + if (xmP.size > 0) + { + // Allocate and unpack pattern + module->patterns[index] = jssAllocatePattern(xmP.nrows, module->nchannels); + if (module->patterns[index] == NULL) + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Could not allocate memory for pattern #%i\n", index); + + result = jssXMUnpackPattern(inFile, xmP.size, module->patterns[index]); + if (result != 0) + JSSERROR(result, result, "Error in unpacking pattern #%i data\n", index); + } + + // Skip extra data (if the file is damaged) + if (xmP.size > dmftell(inFile) - pos) + { + dmfseek(inFile, dmftell(inFile) - pos, SEEK_CUR); + } + } + + // Allocate the empty pattern + module->patterns[jsetMaxPatterns] = jssAllocatePattern(64, module->nchannels); + + /* Convert song orders list by replacing nonexisting patterns + * with pattern number jsetMaxPatterns. + */ + for (index = 0; index < module->norders; index++) + { + tmp = xmH.orderList[index]; + if (tmp >= module->npatterns || module->patterns[tmp] == NULL) + tmp = jsetMaxPatterns; + + module->orderList[index] = tmp; + } + + /* Read instruments + */ + for (index = 0; index < module->nextInstruments; index++) + { + result = jssXMLoadExtInstrument(inFile, index, module); + if (result != 0) + JSSERROR(result, result, "Errors while reading instrument #%i\n", index); + } + + return DMERR_OK; +} diff -r 000000000000 -r 32250b436bca jmix_c.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jmix_c.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,55 @@ +/* + * miniJSS - Mixing routine in C, innerloop template + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2007 Tecnic Software productions (TNSP) + */ + +#ifdef JMIXER_HEADER +int JMIXER_NAME (JSSMixer *, JSSChannel *, Sint32 *, const int, const Uint32); +#else +int JMIXER_NAME (JSSMixer *mixer, JSSChannel *chn, Sint32 *addBuffer, const int mixLength, const Uint32 endPos) +{ + const JSSVolumeTable *volTab8 = (const JSSVolumeTable *) &(mixer->volTab8); + Sint32 *ap = addBuffer; + Sint32 tr = chn->chPrevR, tl = chn->chPrevL; + DMFixedPoint tmpPos = chn->chPos; + const DMFixedPoint tmpDelta = chn->chDeltaO; + int strideLength = 0; + + if (chn->chFlags & jsf16bit) + { + Sint16 *sp = (Sint16 *) chn->chData; + JMIXER_INIT + + while (strideLength < mixLength && JMIXER_ENDCOND) + { + JMIXER_FUNC_16 + JMIXER_DEBUG + JMIXER_NEXT + strideLength++; + } + } + else + { + Uint8 *sp = (Uint8 *) chn->chData; + JMIXER_INIT + + while (strideLength < mixLength && JMIXER_ENDCOND) + { + JMIXER_FUNC_8 + JMIXER_DEBUG + JMIXER_NEXT + strideLength++; + } + + } + + chn->chPos.dw = tmpPos.dw; + chn->chPrevR = tr; chn->chPrevL = tl; + return strideLength; +} + +#undef JMIXER_NAME +#undef JMIXER_NEXT +#undef JMIXER_ENDCOND +#endif diff -r 000000000000 -r 32250b436bca jmix_c.in.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jmix_c.in.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,103 @@ +/* + * miniJSS - Mixing routines in C + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2007 Tecnic Software productions (TNSP) + */ + +#define JMIXER_INTERPOLATE +//#define JMIXER_DEBUG fprintf(stderr, "[%.8x:%.8x / %.8x]: %d\n", FP_GETH(tmpPos), FP_GETL(tmpPos), endPos, sp[FP_GETH(tmpPos)]); +#define JMIXER_DEBUG + +/* Mono / Linear Interpolation + */ +#define JMIXER_INIT const Sint32 vol = (chn->chVolume * mixer->globalVol) / 256; +#ifdef JMIXER_INTERPOLATE +#define JMIXER_FUNC_16 tr = (((Sint32) sp[FP_GETH(tmpPos)]) * vol + tr) / (256 * 2); *(ap++) += tr; +#define JMIXER_FUNC_8 tr = ((*volTab8)[vol][sp[FP_GETH(tmpPos)]] + tr) / 2; *(ap++) += tr; +#else +#define JMIXER_FUNC_16 *(ap++) += (((Sint32) sp[FP_GETH(tmpPos)]) * vol) / 256; +#define JMIXER_FUNC_8 *(ap++) += (*volTab8)[vol][sp[FP_GETH(tmpPos)]]; +#endif + +#define JMIXER_NAME jvmMix_Mono_C_FW +#define JMIXER_NEXT FP_ADD(tmpPos, tmpDelta); +#define JMIXER_ENDCOND (FP_GETH(tmpPos) < endPos) +#include "jmix_c.h" + +#define JMIXER_NAME jvmMix_Mono_C_BW +#define JMIXER_NEXT FP_SUB(tmpPos, tmpDelta); +#define JMIXER_ENDCOND (FP_GETH(tmpPos) > endPos) +#include "jmix_c.h" + +#undef JMIXER_INIT +#undef JMIXER_FUNC_16 +#undef JMIXER_FUNC_8 + + +/* Stereo / Linear Interpolation + */ +#define JMIXER_INIT const Sint32 vol_l = (chn->chVolume * mixer->globalVol) / 256, \ + vol_r = (chn->chVolume * mixer->globalVol) / 256; +#ifdef JMIXER_INTERPOLATE +#define JMIXER_FUNC_16 tl = (((Sint32) sp[FP_GETH(tmpPos)]) * vol_l + tl) / (256 * 2); *(ap++) += tl; \ + tr = (((Sint32) sp[FP_GETH(tmpPos)]) * vol_r + tr) / (256 * 2); *(ap++) += tr; +#define JMIXER_FUNC_8 tl = ((*volTab8)[vol_l][sp[FP_GETH(tmpPos)]] + tl) / 2; *(ap++) += tl; \ + tr = ((*volTab8)[vol_r][sp[FP_GETH(tmpPos)]] + tr) / 2; *(ap++) += tr; +#else +#define JMIXER_FUNC_16 *(ap++) += (((Sint32) sp[FP_GETH(tmpPos)]) * vol_l) / 256; \ + *(ap++) += (((Sint32) sp[FP_GETH(tmpPos)]) * vol_r) / 256; +#define JMIXER_FUNC_8 *(ap++) += (*volTab8)[vol_l][sp[FP_GETH(tmpPos)]]; \ + *(ap++) += (*volTab8)[vol_r][sp[FP_GETH(tmpPos)]]; +#endif + +#define JMIXER_NAME jvmMix_Stereo_C_FW +#define JMIXER_NEXT FP_ADD(tmpPos, tmpDelta); +#define JMIXER_ENDCOND (FP_GETH(tmpPos) < endPos) +#include "jmix_c.h" + +#define JMIXER_NAME jvmMix_Stereo_C_BW +#define JMIXER_NEXT FP_SUB(tmpPos, tmpDelta); +#define JMIXER_ENDCOND (FP_GETH(tmpPos) > endPos) +#include "jmix_c.h" + +#undef JMIXER_INIT +#undef JMIXER_FUNC_16 +#undef JMIXER_FUNC_8 + + +/* Post processing functions + */ +#define JMIXER_NAME jvmPostProcess_U8_C +#define JMIXER_TYPE Uint8 +#define JMIXER_FUNCTION \ + if (t < JVM_LIMIT_16_NEG) t = JVM_LIMIT_16_NEG; else \ + if (t > JVM_LIMIT_16_POS) t = JVM_LIMIT_16_POS; \ + *(sp++) = (t + JVM_ADD_16) >> 8; +#include "jmix_post.h" + + +#define JMIXER_NAME jvmPostProcess_S8_C +#define JMIXER_TYPE Sint8 +#define JMIXER_FUNCTION \ + if (t < JVM_LIMIT_16_NEG) t = JVM_LIMIT_16_NEG; else \ + if (t > JVM_LIMIT_16_POS) t = JVM_LIMIT_16_POS; \ + *(sp++) = t >> 8; +#include "jmix_post.h" + + +#define JMIXER_NAME jvmPostProcess_U16_C +#define JMIXER_TYPE Uint16 +#define JMIXER_FUNCTION \ + if (t < JVM_LIMIT_16_NEG) t = JVM_LIMIT_16_NEG; else \ + if (t > JVM_LIMIT_16_POS) t = JVM_LIMIT_16_POS; \ + *(sp++) = t + JVM_ADD_16; +#include "jmix_post.h" + + +#define JMIXER_NAME jvmPostProcess_S16_C +#define JMIXER_TYPE Sint16 +#define JMIXER_FUNCTION \ + if (t < JVM_LIMIT_16_NEG) t = JVM_LIMIT_16_NEG; else \ + if (t > JVM_LIMIT_16_POS) t = JVM_LIMIT_16_POS; \ + *(sp++) = t; +#include "jmix_post.h" diff -r 000000000000 -r 32250b436bca jmix_post.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jmix_post.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,26 @@ +/* + * miniJSS - Post-processing routine template + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2005-2007 Tecnic Software productions (TNSP) + */ + +#ifdef JMIXER_HEADER +void JMIXER_NAME (Sint32 *, void *, const int); +#else +void JMIXER_NAME (Sint32 *addBuffer, void *mixBuffer, const int mixLength) +{ + int strideLength = mixLength; + Sint32 *ap = addBuffer, t; + JMIXER_TYPE *sp = (JMIXER_TYPE *) mixBuffer; + + while (strideLength--) { + t = *(ap++); + JMIXER_FUNCTION + } +} + +#undef JMIXER_NAME +#undef JMIXER_TYPE +#undef JMIXER_FUNCTION + +#endif diff -r 000000000000 -r 32250b436bca jss.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jss.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,79 @@ +/* + * miniJSS - General functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2007 Tecnic Software productions (TNSP) + */ +#include "jss.h" +#include + + +/* Memory and error handling functions + */ + +BOOL jssWarningIsFatal, jssErrorIsFatal; + +#ifndef JSS_LIGHT +void (*jssError) (int code, char * filename, int linen, char * fmt, ...); +void (*jssWarning) (int code, char * filename, int linen, char * fmt, ...); +#endif + + +void jssDefaultPrint(int code, char * filename, int linen, char * fmt) +{ + fprintf(stderr, "JSS"); + if (filename) + fprintf(stderr, "[%s:%i]", filename, linen); + fprintf(stderr, fmt); + if (code > 0) + fprintf(stderr, "(%i)", code); + fprintf(stderr, ": "); +} + + +void jssDefaultError(int code, char * filename, int linen, char * fmt, ...) +{ + va_list ap; + jssDefaultPrint(code, filename, linen, "E"); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + + +void jssDefaultWarning(int code, char * filename, int linen, char * fmt, ...) +{ + va_list ap; + jssDefaultPrint(code, filename, linen, "W"); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + + +/* System initialization + */ +int jssInit(void) +{ + // Error handling + jssWarningIsFatal = FALSE; + jssErrorIsFatal = TRUE; + +#ifndef JSS_LIGHT + jssError = jssDefaultError; + jssWarning = jssDefaultWarning; +#endif + + // Allocate global tables + + return DMERR_OK; +} + + +/* System shutdown + */ +int jssClose(void) +{ + return DMERR_OK; +} diff -r 000000000000 -r 32250b436bca jss.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jss.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,96 @@ +/* + * miniJSS - Main header file + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2007 Tecnic Software productions (TNSP) + */ +#ifndef JSS_H +#define JSS_H + +#include "dmlib.h" + + +/* Locking + */ +#ifdef JSS_SUP_THREADS +# define JSS_LOCK(Q) dmMutexLock((Q)->mutex) +# define JSS_UNLOCK(Q) dmMutexUnlock((Q)->mutex) +#else +# warning DANGER! JSS threads support not included! +# define JSS_LOCK(Q) +# define JSS_UNLOCK(Q) +#endif + + +// Global settings +#define jsetNChannels (64) // Max number of channels +#define jsetNotSet (-1) // A global "not set" constant +#define jsetMinVol (0) +#define jsetMaxVol (255) + +// Sample instrument flags +#define jsfLooped (0x01) // Is sample looped +#define jsfBiDi (0x02) // Bi-directional loop? +#define jsf16bit (0x04) // 16-bit? +#define JSFSET(a, b) do { a |= b; } while (0) +#define JSFUNSET(a, b) do { a &= (0xff ^ b); } while (0) + +// Panning position +#define jchPanLeft (0x00) // Leftmost pan +#define jchPanMiddle (0x80) // Center pan +#define jchPanRight (0xff) // Rightmost pan + +// Audio formats +enum +{ + JSS_AUDIO_U8, // 8-bit formats + JSS_AUDIO_S8, + JSS_AUDIO_U16, // 16-bit formats + JSS_AUDIO_S16, + JSS_AUDIO_U32, // 32-bit (24+padding) + JSS_AUDIO_S32 +} JSS_AUDIO_FMT; + + +enum +{ + JSS_AUDIO_MONO = 1, + JSS_AUDIO_STEREO = 2 +} JSS_AUDIO_CHANNELS; + + +/* System initialization and shutdown + */ +int jssInit(void); // Initialization. Call before anything else! +int jssClose(void); // Shutdown. Do not call ANY JSS routines after this! + + +/* Error handling routines and related variables + */ +extern BOOL jssWarningIsFatal, // if TRUE, warnings are considered fatal -> function returns + jssErrorIsFatal; // if FALSE, error is considered non-fatal. this may cause strange problems. + +#ifndef JSS_LIGHT +extern void (*jssError)(int code, char *filename, int linen, char *fmt, ...); +extern void (*jssWarning)(int code, char *filename, int linen, char *fmt, ...); +#endif + + +/* If JSS_LIGHT is NOT defined, we add code for verbose error-, warning- + * and debug-messages. Otherwise use a macro stub. + */ +#ifndef JSS_LIGHT +# define JSSERROR(MEVAL, MRET, ...) do { jssError(MEVAL, __FILE__, (int) __LINE__, __VA_ARGS__); if (jssErrorIsFatal) return MRET; } while (0) +# define JSSWARNING(MEVAL, MRET, ...) do { jssWarning(MEVAL, __FILE__, (int) __LINE__, __VA_ARGS__); if (jssWarningIsFatal) return MRET; } while (0) +# ifdef JSS_DEBUG +# define JSSDEBUG(...) do { fprintf(stderr, "[%s:%d]: ", __FILE__, (int) __LINE__); fprintf(stderr, __VA_ARGS__); } while (0) +# else +# define JSSDEBUG(...) do { } while (0) +# endif // NDEBUG +#else +# define JSSERROR(MEVAL, MRET, ...) do { return MRET; } while (0) +# define JSSWARNING(MEVAL, MRET, ...) do { } while (0) +# define JSSDEBUG(...) do { } while (0) +#endif // JSS_LIGHT + + +#endif // JSS_H diff -r 000000000000 -r 32250b436bca jssmix.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jssmix.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,654 @@ +/* + * miniJSS - Mixing device and channel handling + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2012 Tecnic Software productions (TNSP) + */ +#include "jssmix.h" +#include + + +#ifdef DM_USE_C +#define JMIXER_HEADER +#include "jmix_c.c" +#undef JMIXER_HEADER +#endif + +#undef DM_USE_SIMD + +#ifdef DM_USE_SIMD +#define JMIXER_HEADER +#include "jmix_mmx.h" +#undef JMIXER_HEADER +#endif + + +typedef struct +{ + int mixerID; + int outFormat; + int outChannels; + int (*jvmMixChannel_FW)(JSSMixer *, JSSChannel *, Sint32 *, const int, const Uint32); + int (*jvmMixChannel_BW)(JSSMixer *, JSSChannel *, Sint32 *, const int, const Uint32); + void (*jvmPostProcess)(Sint32 *, void *, const int); +} JSSMixingRoutine; + + +/* This table should be sorted from fastest to slowest, e.g. MMX/x86 + * optimized routines first, pure C versions last. + */ +static JSSMixingRoutine jvmMixRoutines[] = +{ +#ifdef DM_USE_SIMD +{ JMIX_MMX, JSS_AUDIO_U8, JSS_AUDIO_MONO, jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_U8_MMX }, +{ JMIX_MMX, JSS_AUDIO_S8, JSS_AUDIO_MONO, jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_S8_MMX }, +{ JMIX_MMX, JSS_AUDIO_U8, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_U8_MMX }, +{ JMIX_MMX, JSS_AUDIO_S8, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_S8_MMX }, + +{ JMIX_MMX, JSS_AUDIO_U16, JSS_AUDIO_MONO, jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_U16_MMX }, +{ JMIX_MMX, JSS_AUDIO_S16, JSS_AUDIO_MONO, jvmMix_Mono_MMX_FW, jvmMix_Mono_MMX_BW, jvmPostProcess_S16_MMX }, +{ JMIX_MMX, JSS_AUDIO_U16, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_U16_MMX }, +{ JMIX_MMX, JSS_AUDIO_S16, JSS_AUDIO_STEREO, jvmMix_Stereo_MMX_FW, jvmMix_Stereo_MMX_BW, jvmPostProcess_S16_MMX }, +#endif + +#ifdef DM_USE_C +{ JMIX_C, JSS_AUDIO_U8, JSS_AUDIO_MONO, jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_U8_C }, +{ JMIX_C, JSS_AUDIO_S8, JSS_AUDIO_MONO, jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_S8_C }, +{ JMIX_C, JSS_AUDIO_U8, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_U8_C }, +{ JMIX_C, JSS_AUDIO_S8, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_S8_C }, + +{ JMIX_C, JSS_AUDIO_U16, JSS_AUDIO_MONO, jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_U16_C }, +{ JMIX_C, JSS_AUDIO_S16, JSS_AUDIO_MONO, jvmMix_Mono_C_FW, jvmMix_Mono_C_BW, jvmPostProcess_S16_C }, +{ JMIX_C, JSS_AUDIO_U16, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_U16_C }, +{ JMIX_C, JSS_AUDIO_S16, JSS_AUDIO_STEREO, jvmMix_Stereo_C_FW, jvmMix_Stereo_C_BW, jvmPostProcess_S16_C }, +#endif +}; + +static const int jvmNMixRoutines = sizeof(jvmMixRoutines) / sizeof(jvmMixRoutines[0]); + + +static int jvmFindMixRoutine(int outFormat, int outChannels, int mixerID) +{ + int i; + + for (i = 0; i < jvmNMixRoutines; i++) + { + if (jvmMixRoutines[i].outFormat == outFormat && + jvmMixRoutines[i].outChannels == outChannels && + (mixerID == JMIX_AUTO || jvmMixRoutines[i].mixerID == mixerID)) + return i; + } + + return -1; +} + + +JSSMixer *jvmInit(const int outFormat, const int outChannels, const int outFreq, const int mixerID) +{ + JSSMixer *mixer; + int mixerIdx, v, i; + + // Check settings + if (outChannels < 1) + { + JSSERROR(DMERR_INVALID_ARGS, NULL, + "Invalid number of channels %d\n", outChannels); + } + + if (outFreq < 4000) + { + JSSERROR(DMERR_INVALID_ARGS, NULL, + "Invalid mixing frequency %d\n", outFreq); + } + + /* Select mixing routines: + * Here we try to choose the most fitting mixing routines + * from the compiled in routines, unless caller is forcing + * us to select specific ones. + */ + if (mixerID == JMIX_AUTO) + { + mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_SSE); + if (mixerIdx < 0) + mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_MMX); + if (mixerIdx < 0) + mixerIdx = jvmFindMixRoutine(outFormat, outChannels, JMIX_AUTO); + } + else + { + mixerIdx = jvmFindMixRoutine(outFormat, outChannels, mixerID); + } + + if (mixerIdx < 0) + { + JSSERROR(DMERR_INVALID_ARGS, NULL, + "Could not find mixing routine for outFormat=%d, outChannels=%d, outFreq=%d.\n", + outFormat, outChannels, outFreq); + return NULL; + } + + // Allocate a mixer device structure + mixer = dmMalloc0(sizeof(JSSMixer)); + if (mixer == NULL) + { + JSSERROR(DMERR_MALLOC, NULL, + "Could not allocate mixing device structure.\n"); + } + + // Initialize variables +#ifdef JSS_SUP_THREADS + mixer->mutex = dmCreateMutex(); +#endif + mixer->outFormat = outFormat; + mixer->outFreq = outFreq; + mixer->outChannels = outChannels; + + mixer->jvmMixChannel_FW = jvmMixRoutines[mixerIdx].jvmMixChannel_FW; + mixer->jvmMixChannel_BW = jvmMixRoutines[mixerIdx].jvmMixChannel_BW; + mixer->jvmPostProcess = jvmMixRoutines[mixerIdx].jvmPostProcess; + + // Allocate addBuffer + mixer->addBufSize = outChannels * outFreq; + mixer->addBuffer = dmCalloc(mixer->addBufSize, sizeof(Sint32)); + if (mixer->addBuffer == NULL) + { + JSSERROR(DMERR_MALLOC, NULL, + "Could not allocate mixing addition buffer.\n"); + } + + // Initialize 8-bit volume table + for (v = jsetMinVol; v < jsetMaxVol; v++) + { + for (i = 0; i < 256; i++) + { + mixer->volTab8[v][i] = (v * (i - 128)); + } + } + + return mixer; +} + + +int jvmClose(JSSMixer * mixer) +{ + if (mixer == NULL) + return DMERR_NULLPTR; + + // Deallocate resources +#ifdef JSS_SUP_THREADS + dmDestroyMutex(mixer->mutex); +#endif + dmFree(mixer->addBuffer); + + memset(mixer, 0, sizeof(JSSMixer)); + dmFree(mixer); + + return DMERR_OK; +} + + +int jvmGetSampleSize(JSSMixer *mixer) +{ + int sampSize = 1; + assert(mixer); + + switch (mixer->outChannels) + { + case JSS_AUDIO_STEREO: + case JSS_AUDIO_MONO: + sampSize = mixer->outChannels; + break; + default: + JSSERROR(DMERR_INVALID_ARGS, -1, + "outChannels=%d not stereo or mono!\n", mixer->outChannels); + break; + } + + switch (mixer->outFormat) + { + case JSS_AUDIO_U16: sampSize *= sizeof(Uint16); break; + case JSS_AUDIO_S16: sampSize *= sizeof(Sint16); break; + case JSS_AUDIO_U8: sampSize *= sizeof(Uint8); break; + case JSS_AUDIO_S8: sampSize *= sizeof(Sint8); break; + default: + JSSERROR(DMERR_INVALID_ARGS, -1, + "outFormat=%d is not supported!\n", mixer->outFormat); + } + + return sampSize; +} + + +int jvmGetSampleRes(JSSMixer *mixer) +{ + int sampRes = 1; + + assert(mixer); + + switch (mixer->outFormat) + { + case JSS_AUDIO_U16: case JSS_AUDIO_S16: sampRes = 16; break; + case JSS_AUDIO_U8: case JSS_AUDIO_S8: sampRes = 8; break; + default: + JSSERROR(DMERR_INVALID_ARGS, -1, + "outFormat=%d is not supported!\n", mixer->outFormat); + } + + return sampRes; +} + + +static void jvmMixChannel(JSSMixer *mixer, JSSChannel *chn, Sint32 *addBuffer, const int mixLength) +{ + int mixDone = mixLength, mixResult; + Sint32 *ab = addBuffer; + + if (!chn->chPlaying || chn->chMute) + return; + + DBG("%s(%p, %d)\n", __FUNCTION__, chn, mixLength); + + while (mixDone > 0) + { + if (chn->chDirection) + { + // Channel is playing FORWARDS + if (chn->chFlags & jsfLooped) + { + // Sample is looped + if (chn->chFlags & jsfBiDi) + { + // Bi-directional loop + if (FP_GETH(chn->chPos) >= chn->chLoopE) + { + FP_SETH(chn->chPos, chn->chLoopE - 1); + FP_SETL(chn->chPos, 0); + chn->chDirection = FALSE; + } + } + else + { + // Normal forward loop + if (FP_GETH(chn->chPos) >= chn->chLoopE) + { + FP_SETH(chn->chPos, chn->chLoopS /* + (FP_GETH(chn->chPos) - chn->chLoopE) */); + FP_SETL(chn->chPos, 0); + } + } + } + else + { + // Normal (non-looped) sample + if (FP_GETH(chn->chPos) >= chn->chSize) + { + chn->chPlaying = FALSE; + return; + } + } + } + else + { + // Channel is playing BACKWARDS + if (chn->chFlags & jsfLooped) + { + // Sample is looped + if (chn->chFlags & jsfBiDi) + { + // Bi-directional loop + if (FP_GETH(chn->chPos) <= chn->chLoopS) + { + FP_SETH(chn->chPos, chn->chLoopS); + FP_SETL(chn->chPos, 0); + chn->chDirection = TRUE; + } + } + else + { + // Normal forward loop + if (FP_GETH(chn->chPos) <= chn->chLoopS) + { + FP_SETH(chn->chPos, chn->chLoopE - 1); + FP_SETL(chn->chPos, 0); + } + } + } + else + { + // Normal (non-looped) sample + if (FP_GETH(chn->chPos) <= 0) + { + chn->chPlaying = FALSE; + return; + } + } + } + + // Call the mixing innerloop functions + if (chn->chDirection) + { + DBG("MIX_FW[%p : %d : ", ab, mixDone); + if (chn->chFlags & jsfLooped) + { + DBG("%d (%x)] {loop}\n", chn->chLoopE, chn->chLoopE); + mixResult = mixer->jvmMixChannel_FW((void *) mixer, chn, + ab, mixDone, chn->chLoopE); + } + else + { + DBG("%d (%x)]\n", chn->chSize, chn->chSize); + mixResult = mixer->jvmMixChannel_FW((void *) mixer, chn, + ab, mixDone, chn->chSize); + } + } + else + { + DBG("MIX_BW[%p : %d : ", ab, mixDone); + if (chn->chFlags & jsfLooped) + { + DBG("%d (%x)] {loop}\n", chn->chLoopS, chn->chLoopS); + mixResult = mixer->jvmMixChannel_BW(mixer, chn, + ab, mixDone, chn->chLoopS); + } + else + { + DBG("%d (%x)]\n", 0, 0); + mixResult = mixer->jvmMixChannel_BW(mixer, chn, + ab, mixDone, 0); + } + } + + mixDone -= mixResult; + ab += mixResult * mixer->outChannels; + } + +#ifdef JSS_DEBUG + if (mixDone < 0) + JSSWARNING(DMERR_BOUNDS,, "mixDone < 0 in mixing logic loop.\n"); +#endif +} + + +void jvmRenderAudio(JSSMixer *mixer, void *mixBuffer, const int mixLength) +{ + int i, blockLength, mixLeft; + Sint32 *ab; + + JSS_LOCK(mixer); + + assert(mixer != NULL); + assert(mixBuffer != NULL); + assert(mixLength > 0); + assert(mixLength * mixer->outChannels < mixer->addBufSize); + + // Clear mixer->addBuffer + memset(mixer->addBuffer, 0, mixLength * mixer->outChannels * sizeof(Sint32)); + + ab = mixer->addBuffer; + mixLeft = mixLength; + while (mixLeft > 0) + { + // Check for callbacks + blockLength = mixLeft; + + if (mixer->cbFunction) + { + if (mixer->cbCounter <= 0) + { + mixer->cbFunction(mixer, mixer->cbData); + mixer->cbCounter = mixer->cbFreq; + } + + if (mixer->cbCounter < blockLength) + blockLength = mixer->cbCounter; + } + + // Do mixing + for (i = 0; i < jsetNChannels; i++) + { + JSSChannel *chn = &(mixer->channels[i]); + if (chn->chPlaying && !chn->chMute) + jvmMixChannel(mixer, chn, ab, blockLength); + } + +/* + if (chn->chPlaying) + { + if (!chn->chMute) + jvmMixChannel(mixer, chn, ab, blockLength); + else + jvmAdvanceChannel(mixer, chn, blockLength); + } +*/ + + ab += blockLength * mixer->outChannels; + mixLeft -= blockLength; + mixer->cbCounter -= blockLength; + } + + // Post-process + mixer->jvmPostProcess(mixer->addBuffer, mixBuffer, mixLength * mixer->outChannels); + + JSS_UNLOCK(mixer); +} + + +int jvmSetCallback(JSSMixer * mixer, void (*cbFunction) (void *, void *), void *cbData) +{ + assert(mixer); + + if (cbFunction == NULL) + JSSERROR(DMERR_NULLPTR, DMERR_NULLPTR, "NULL pointer given as cbFunction"); + + JSS_LOCK(mixer); + + mixer->cbFunction = cbFunction; + mixer->cbData = cbData; + + JSS_UNLOCK(mixer); + + return DMERR_OK; +} + + +void jvmRemoveCallback(JSSMixer * mixer) +{ + assert(mixer); + + JSS_LOCK(mixer); + + mixer->cbFunction = NULL; + mixer->cbData = NULL; + mixer->cbFreq = mixer->cbCounter = 0; + + JSS_UNLOCK(mixer); +} + + +int jvmSetCallbackFreq(JSSMixer * mixer, const int cbFreq) +{ + assert(mixer); + + if ((cbFreq < 1) || (cbFreq >= mixer->outFreq)) + JSSERROR(DMERR_INVALID_ARGS, DMERR_INVALID_ARGS, + "Invalid callback frequency given (%i / %i)\n", cbFreq, mixer->outFreq); + + JSS_LOCK(mixer); + + mixer->cbFreq = (mixer->outFreq / cbFreq); + mixer->cbCounter = 0; + +//fprintf(stderr, "set(outFreq = %d, cbFreq = %d) = %d\n", mixer->outFreq, cbFreq, mixer->cbFreq); + + JSS_UNLOCK(mixer); + return DMERR_OK; +} + + +/* Channel manipulation routines + */ +void jvmPlay(JSSMixer * mixer, const int channel) +{ + JSS_LOCK(mixer); + mixer->channels[channel].chPlaying = TRUE; + JSS_UNLOCK(mixer); +} + + +void jvmStop(JSSMixer * mixer, const int channel) +{ + JSS_LOCK(mixer); + mixer->channels[channel].chPlaying = FALSE; + JSS_UNLOCK(mixer); +} + + +void jvmSetSample(JSSMixer * mixer, const int channel, + void *data, const Uint32 size, const Uint32 loopS, + const Uint32 loopE, const int flags) +{ + JSSChannel *c; + + JSS_LOCK(mixer); + c = &mixer->channels[channel]; + + c->chData = data; + c->chSize = size; + c->chLoopS = loopS; + c->chLoopE = loopE; + c->chFlags = flags; + c->chDirection = TRUE; + c->chPrevL = c->chPrevR = 0; + c->chPos.dw = c->chDeltaO.dw = 0; + + JSS_UNLOCK(mixer); +} + + +void jvmSetFreq(JSSMixer * mixer, const int channel, const int freq) +{ + JSS_LOCK(mixer); + + mixer->channels[channel].chFreq = freq; + + if (mixer->outFreq > 0) + { + DMFixedPoint a, b; + FP_SETHL(a, freq, 0); + FP_CONV(b, mixer->outFreq); + FP_DIV_R(mixer->channels[channel].chDeltaO, a, b); + } + else + { + FP_SET(mixer->channels[channel].chDeltaO, 0); + } + + JSS_UNLOCK(mixer); +} + + +int jvmGetFreq(JSSMixer * mixer, const int channel) +{ + int tmp; + + JSS_LOCK(mixer); + tmp = mixer->channels[channel].chFreq; + JSS_UNLOCK(mixer); + + return tmp; +} + + +void jvmSetVolume(JSSMixer * mixer, const int channel, const int volume) +{ + JSS_LOCK(mixer); + mixer->channels[channel].chVolume = volume; + JSS_UNLOCK(mixer); +} + + +int jvmGetVolume(JSSMixer * mixer, const int channel) +{ + int tmp; + + JSS_LOCK(mixer); + tmp = mixer->channels[channel].chVolume; + JSS_UNLOCK(mixer); + + return tmp; +} + + +void jvmSetPos(JSSMixer * mixer, const int channel, const Uint32 pos) +{ + JSS_LOCK(mixer); + FP_SETH(mixer->channels[channel].chPos, pos); + FP_SETL(mixer->channels[channel].chPos, 0); + JSS_UNLOCK(mixer); +} + + +Uint32 jvmGetPos(JSSMixer * mixer, const int channel) +{ + Uint32 tmp; + + JSS_LOCK(mixer); + tmp = FP_GETH(mixer->channels[channel].chPos); + JSS_UNLOCK(mixer); + + return tmp; +} + + +void jvmSetPan(JSSMixer * mixer, const int channel, const int panning) +{ + JSS_LOCK(mixer); + mixer->channels[channel].chPanning = panning; + JSS_UNLOCK(mixer); +} + + +int jvmGetPan(JSSMixer * mixer, const int channel) +{ + int tmp; + + JSS_LOCK(mixer); + tmp = mixer->channels[channel].chPanning; + JSS_UNLOCK(mixer); + + return tmp; +} + + +void jvmMute(JSSMixer * mixer, const int channel, const BOOL mute) +{ + JSS_LOCK(mixer); + mixer->channels[channel].chMute = mute; + JSS_UNLOCK(mixer); +} + + +void jvmClear(JSSMixer * mixer, const int channel) +{ + JSS_LOCK(mixer); + memset(&mixer->channels[channel], 0, sizeof(JSSChannel)); + JSS_UNLOCK(mixer); +} + + +void jvmSetGlobalVol(JSSMixer * mixer, const int volume) +{ + JSS_LOCK(mixer); + mixer->globalVol = volume; + JSS_UNLOCK(mixer); +} + + +int jvmGetGlobalVol(JSSMixer * mixer) +{ + int tmp; + + JSS_LOCK(mixer); + tmp = mixer->globalVol; + JSS_UNLOCK(mixer); + + return tmp; +} diff -r 000000000000 -r 32250b436bca jssmix.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jssmix.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,151 @@ +/* + * miniJSS - Mixing device and channel handling + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2012 Tecnic Software productions (TNSP) + */ +#ifndef JSSMIX_H +#define JSSMIX_H +#include "jss.h" + +//#define DBG(...) do { fprintf(stderr, __VA_ARGS__); } while (0) +#define DBG(...) do { /* stub */ } while (0) + + +// Mixing limits +#define JVM_LIMIT_16_POS (32767) +#define JVM_LIMIT_16_NEG (-32767) +#define JVM_ADD_16 (32768) + + +// A channel data structure +typedef struct +{ + DMFixedPoint + chPos, // Current position in sample, 32:32 fixpoint + chDeltaO; // Delta in 32:32 UNSIGNED! (chDirection) + + int chFreq; // Frequency of sampel in Hz + Uint32 + chSize, // Length of sample in UNITS + chLoopS, // Loop start in UNITS + chLoopE; // Loop end in UNITS + + void *chData; // Pointer to data + + BOOL chPlaying, // TRUE = playing + chMute, // TRUE = muted + chDirection; // TRUE = playing forwards, FALSE = backwards + + int chFlags; // Flags + int chVolume, // Volume + chPanning; // Panning + + Sint32 chPrevR, chPrevL; +} JSSChannel; + + +// Channel table +typedef int JSSChannelTable[jsetNChannels]; + +typedef Sint32 JSSVolumeTable[256][256]; + + +// Virtual software mixer "device" structure +typedef struct _JSSMixer JSSMixer; + +struct _JSSMixer +{ + int outFormat, + outChannels, + outFreq, + globalVol; + JSSChannel channels[jsetNChannels]; + JSSVolumeTable volTab8; + + int addBufSize; + Sint32 *addBuffer; + + // Callback handling + int cbFreq, cbCounter; + void *cbData; + void (*cbFunction)(void *, void *); + + // Mixing routine pointers + int (*jvmMixChannel_FW)(JSSMixer *, JSSChannel *, Sint32 *, const int, const Uint32); + int (*jvmMixChannel_BW)(JSSMixer *, JSSChannel *, Sint32 *, const int, const Uint32); + void (*jvmPostProcess)(Sint32 *, void *mixBuffer, int); + + // Device locking +#ifdef JSS_SUP_THREADS + DMMutex *mutex; +#endif +}; + + +/* Enums + */ +enum { + JMIX_AUTO = 0, + JMIX_C, + JMIX_MMX, + JMIX_SSE +}; + + +/* Main routines + */ +JSSMixer * jvmInit(const int outFormat, const int nChannels, const int outFreq, const int mixerID); +int jvmClose(JSSMixer *mixer); + +int jvmSetCallback(JSSMixer *mixer, void (*cbFunction)(void *, void *), void *cbData); +void jvmRemoveCallback(JSSMixer *mixer); +int jvmSetCallbackFreq(JSSMixer *mixer, const int cbFreq); +Uint32 jvmGetLastCBBufPos(JSSMixer *mixer, const int cbHandle); +Uint32 jvmGetNextCBBufPos(JSSMixer *mixer, const int cbHandle); + +int jvmGetSampleSize(JSSMixer *mixer); +int jvmGetSampleRes(JSSMixer *mixer); + +void jvmRenderAudio(JSSMixer *mixer, void *mixBuffer, const int mixLength); + + +/* +int jvmAddCallback(JSSMixer *mixer, void (*cbFunction)(JSSMixer *, void *)); +int jvmRemoveCallback(JSSMixer *mixer, int cbHandle); +int jvmSetCallbackFreq(JSSMixer *mixer, int cbHandle, int cbFreq); + +int jvmAllocChannels(JSSMixer *mixer, int nChannels); +int jvmFreeChannels(JSSMixer *mixer, int iHandle); +int jvmGetChannelTable(JSSMixer *mixer, int iHandle, JSSChannelTable *pTable); +*/ + + +/* Channel manipulation routines + */ +void jvmPlay(JSSMixer *mixer, const int channel); +void jvmStop(JSSMixer *mixer, const int channel); + +void jvmSetSample(JSSMixer *mixer, const int channel, + void *data, const Uint32 size, const Uint32 loopS, + const Uint32 loopE, const int flags); + +void jvmSetFreq(JSSMixer *mixer, const int channel, const int freq); +int jvmGetFreq(JSSMixer *mixer, const int channel); +void jvmSetVolume(JSSMixer *mixer, const int channel, const int volume); +int jvmGetVolume(JSSMixer *mixer, const int channel); +void jvmSetPos(JSSMixer *mixer, const int channel, const Uint32 pos); +Uint32 jvmGetPos(JSSMixer *mixer, const int channel); + +void jvmSetPan(JSSMixer *mixer, const int channel, const int panning); +int jvmGetPan(JSSMixer *mixer, const int channel); + +void jvmMute(JSSMixer *mixer, const int channel, const BOOL mute); + +void jvmClear(JSSMixer *mixer, const int channel); +void jvmClearChannels(JSSMixer *mixer); + +void jvmSetGlobalVol(JSSMixer *mixer, const int volume); +int jvmGetGlobalVol(JSSMixer *mixer); + + +#endif // JSSMIX_H diff -r 000000000000 -r 32250b436bca jssmod.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jssmod.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,380 @@ +/* + * miniJSS - Module structure and handling routines + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2007 Tecnic Software productions (TNSP) + */ +#include "jssmod.h" +#include + + +#ifndef JSS_LIGHT +/* Take given data until maxlen reached, make a string + */ +char *jssASCIItoStr(char * sdata, const char endByte, const size_t maxLen) +{ + size_t i, k; + char *res; + + for (i = 0; sdata[i] && i < maxLen; i++); + + res = (char *) dmMalloc(i + 1); + if (res == NULL) + return NULL; + + for (k = 0; sdata[k] != endByte && k < i; k++) + res[k] = sdata[k]; + + res[k] = 0; + + return res; +} + + +/* Encodes a given 8-bit sample + */ +BOOL jssEncodeSample8(Uint8 * sdata, const size_t srcSize, const int ops) +{ + size_t ssize = srcSize; + Sint8 t, value = 0; + + while (ssize--) + { + t = *sdata; + + if (ops & jsampFlipSign) + t ^= 0x80; + + if (ops & jsampDelta) + { + int n = t - value; + value = t; + t = n; + } + + *(sdata++) = t; + } + + return TRUE; +} + + +/* Decodes a given 16-bit sample + */ +BOOL jssEncodeSample16(Uint16 * srcData, const size_t srcSize, const int ops) +{ + // "Split" the 16-bit samples into 8-bit halves + if (ops & jsampSplit) + { + // Allocate temporary processing buffer + size_t ssize, bufSize = srcSize * sizeof(Sint16); + Uint8 *bp1, *bp2; + Sint16 t, *sdata, *tmpBuf = dmMalloc(bufSize); + if (tmpBuf == NULL) return FALSE; + + sdata = tmpBuf; + bp1 = (Uint8 *) srcData; + bp2 = bp1 + srcSize; + ssize = srcSize; + + while (ssize--) + { + t = (*sdata++); + *bp1++ = t >> 8; + *bp2++ = t & 0xff; + } + + memcpy(srcData, tmpBuf, bufSize); + dmFree(tmpBuf); + + return jssEncodeSample8((Uint8 *) srcData, bufSize, ops); + } + else + { + Sint16 t, p, value = 0, *sdata = (Sint16 *) srcData; + size_t ssize = srcSize; + + while (ssize--) + { + if (ops & jsampSwapEndianess) + { + p = *sdata; + t = ((p >> 8) & 0xff) | ((p & 0xff) << 8); + } else + t = *sdata; + + if (ops & jsampDelta) + { + int n = t - value; + value = t; + t = n; + } + + if (ops & jsampFlipSign) + t ^= 0x8000; + + *(sdata++) = t; + } + } + return TRUE; +} +#endif + + +/* Decodes a given 8-bit sample + */ +BOOL jssDecodeSample8(Uint8 * sdata, const size_t srcSize, const int ops) +{ + size_t ssize = srcSize; + Sint8 t, value = 0; + + while (ssize--) + { + t = *sdata; + + if (ops & jsampDelta) + t = value = t + value; + + if (ops & jsampFlipSign) + t ^= 0x80; + + *(sdata++) = t; + } + return TRUE; +} + + +/* Decodes a given 16-bit sample + */ +BOOL jssDecodeSample16(Uint16 * srcData, const size_t srcSize, const int ops) +{ + if (ops & jsampSplit) + { + size_t ssize, bufSize = srcSize * sizeof(Uint16); + Uint8 *bp1, *bp2; + Sint16 *tmpBuf, *sdata; + + if (!jssDecodeSample8((Uint8 *) srcData, bufSize, ops)) + return FALSE; + + tmpBuf = dmMalloc(bufSize); + if (tmpBuf == NULL) return FALSE; + memcpy(tmpBuf, srcData, bufSize); + + sdata = (Sint16 *) srcData; + bp1 = (Uint8 *) tmpBuf; + bp2 = bp1 + srcSize; + ssize = srcSize; + while (ssize--) + { + *sdata++ = (*bp1++ << 8) | (*bp2++ & 0xff); + } + + dmFree(tmpBuf); + } + else + { + Sint16 t, p, value = 0, *sdata = (Sint16 *) srcData; + size_t ssize = srcSize; + while (ssize--) + { + if (ops & jsampSwapEndianess) + { + p = *sdata; + t = ((p >> 8) & 0xff) | ((p & 0xff) << 8); + } else + t = *sdata; + + if (ops & jsampDelta) + t = value = t + value; + + if (ops & jsampFlipSign) + t ^= 0x8000; + + *(sdata++) = t; + } + } + return TRUE; +} + + +/* Allocates a new module structure or returns errorvalue if failed. + * Memory is allocated only for the basic structure. Sample- and pattern + * areas must be allocated separately with appropriate routines. + */ +JSSModule *jssAllocateModule(void) +{ + int i; + JSSModule *module; + + // Allocate module structure + module = dmMalloc0(sizeof(JSSModule)); + if (module == NULL) + return NULL; + + // Initialize structure + for (i = 0; i < jsetNChannels; i++) + module->defPanning[i] = jchPanMiddle; + + for (i = 0; i < jsetMaxOrders; i++) + module->orderList[i] = jsetOrderEnd; + + // Allocate mutex +#ifdef JSS_SUP_THREADS + module->mutex = dmCreateMutex(); +#endif + + return module; +} + + +/* Frees a given module structure, freeing all memory areas + * that were allocated for it (including patterns, samples, etc.) + */ +int jssFreeModule(JSSModule * module) +{ + int i; + + if (module == NULL) + return DMERR_NULLPTR; + + // Free strings +#ifndef JSS_LIGHT + dmFree(module->moduleName); + dmFree(module->trackerName); +#endif + + // Free patterns + for (i = 0; i < module->npatterns; i++) + { + if (module->patterns[i]) + { + JSSPattern *pat = module->patterns[i]; + dmFree(pat->data); + dmFree(pat); + module->patterns[i] = NULL; + } + } + + // Free instruments + for (i = 0; i < module->ninstruments; i++) + { + if (module->instruments[i]) + { + JSSInstrument *inst = module->instruments[i]; +#ifndef JSS_LIGHT + dmFree(inst->desc); +#endif + dmFree(inst->data); + dmFree(inst); + module->instruments[i] = NULL; + } + } + + // Free extended instruments + for (i = 0; i < module->nextInstruments; i++) + { + if (module->extInstruments[i]) + { + JSSExtInstrument *ext = module->extInstruments[i]; +#ifndef JSS_LIGHT + dmFree(ext->desc); +#endif + dmFree(ext); + module->extInstruments[i] = NULL; + } + } + + // Free mutex +#ifdef JSS_SUP_THREADS + dmDestroyMutex(module->mutex); +#endif + + // Free the module structure + memset(module, 0, sizeof(JSSModule)); + dmFree(module); + + return DMERR_OK; +} + + +/* Allocates and initializes a internal pattern structure. + */ +JSSPattern *jssAllocatePattern(int nrows, int nchannels) +{ + int i, j; + JSSPattern *res; + JSSNote *pnote; + + // Check arguments + if (nrows <= 0 || nchannels <= 0) + JSSERROR(DMERR_INVALID_ARGS, NULL, "Invalid nrows=%i or nchannels=%i.\n", nrows, nchannels); + + // Allocate a pattern structure + res = dmMalloc0(sizeof(JSSPattern)); + if (res == NULL) + JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern structure.\n"); + + // Allocate notedata + res->data = dmCalloc(nrows * nchannels, sizeof(JSSNote)); + if (res->data == NULL) + { + dmFree(res); + JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern data (nrows=%i, nchannels=%i).\n", nrows, + nchannels); + } + + // Initialize + res->nrows = nrows; + res->nchannels = nchannels; + + pnote = res->data; + for (j = 0; j < nrows; j++) + { + for (i = 0; i < nchannels; i++) + { + pnote->note = pnote->instrument = pnote->volume = + pnote->effect = pnote->param = jsetNotSet; + + pnote++; + } + } + + // OK, return pointer to struct + return res; +} + + +/* Allocates and initializes internal "normal" instrument structure. + */ +JSSInstrument *jssAllocateInstrument(void) +{ + JSSInstrument *res; + + // Allocate a instrument structure + res = dmMalloc0(sizeof(JSSInstrument)); + if (res == NULL) + return NULL; + + return res; +} + + +/* Allocates and initializes "extended" instrument structure. + */ +JSSExtInstrument *jssAllocateExtInstrument(void) +{ + int i; + JSSExtInstrument *res; + + // Allocate a instrument structure + res = dmMalloc0(sizeof(JSSExtInstrument)); + if (res == NULL) + return NULL; + + for (i = 0; i < jsetNNotes; i++) + { + res->sNumForNotes[i] = jsetNotSet; + } + + return res; +} diff -r 000000000000 -r 32250b436bca jssmod.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jssmod.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,307 @@ +/* + * miniJSS - Module structure and handling routines + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2009 Tecnic Software productions (TNSP) + */ +#ifndef JSSMOD_H +#define JSSMOD_H + +#include "jss.h" +#include "dmres.h" + + +// Max data size definitions +#define jsetMaxRows (256) // Max number of rows +#define jsetMaxPatterns (256) // Max patterns +#define jsetMaxTracks (jsetMaxPatterns * jsetMaxPatterns) // Max tracks +#define jsetMaxInstruments (512) +#define jsetMaxOrders (260) +#define jsetNNotes (11 * 12) +#define jsetMinNote (0) // Smallest note number +#define jsetMaxNote (jsetNNotes - 1) +#define jsetMaxEnvPoints (32) // Max number of envelope points + + +// Instrument envelope flags +#define jenvfUsed (0x01) // Envelope is used +#define jenvfSustain (0x02) // Envelope has a sustain point (XM) or has sustain loop (IT) +#define jenvfLooped (0x04) // Envelope is looped + +// Instrument vibrato waveform types +#define jvibSine (0) +#define jvibRamp (1) +#define jvibSquare (2) +#define jvibRandom (3) + +// General stuff +#define jsetNoteOff (-2) +#define jsetOrderEnd (-1) +#define jsetOrderSkip (-2) + + +// General module flags +#define jmdfAmigaPeriods (0x0001) // Use non-linear periods (Amiga) +#define jmdfAmigaLimits (0x0002) // Use Amiga-styled valuelimits +#define jmdfStereo (0x0008) // Use stereo output, if possible + +#define jmdfFT2Replay (0x0010) // Use FT2 replaying bugs/features +#define jmdfST300Slides (0x0020) // Use Scream Tracker 3.00 slides +#define jmdfByteLStart (0x0040) // LStart is in BYTES instead of WORDS (MOD only) + + +// Module format types +enum JMDT +{ + jmdtMOD = 1, + jmdtS3M, + jmdtXM, + jmdtIT +}; + + +// Bits for sample conversion routines +#define jsampDelta (0x01) +#define jsampFlipSign (0x02) +#define jsampSwapEndianess (0x04) +#define jsampSplit (0x08) + + +// Internal instrument structure +typedef struct +{ + size_t size, // Length in units + loopS, // Loop start position in units + loopE; // Loop end position in units + int volume, // Volume [jsetMinVol...jsetMaxVol] + flags, // Flags - see jss.h jsfXXXX + C4BaseSpeed, // C4BaseSpeed + ERelNote, // Extended: Relative note value + EFineTune, // Extended: Fine-tune value + EPanning; // Extended: Panning +#ifndef JSS_LIGHT + char *desc; +#endif + void *data; // Sample data + + BOOL hasData; + int convFlags; +} JSSInstrument; + + +// Envelope point structure +typedef struct +{ + int frame, value; +} JSSEnvelopePoint; + + +// Envelope structure +typedef struct +{ + int flags, + npoints, + sustain, + loopS, + loopE; + JSSEnvelopePoint points[jsetMaxEnvPoints]; +} JSSEnvelope; + + +// Extended instrument +typedef struct +{ +#ifndef JSS_LIGHT + char *desc; +#endif + int nsamples, sNumForNotes[jsetNNotes]; + JSSEnvelope volumeEnv, panningEnv; + int vibratoType, + vibratoSweep, + vibratoDepth, + vibratoRate, + fadeOut; +} JSSExtInstrument; + + +// Internal pattern structures +typedef struct +{ + int note, + instrument, + volume, + effect, + param; +} JSSNote; + + +typedef struct +{ + int nrows, nchannels; + JSSNote *data; +} JSSPattern; + + +// Module structure +typedef struct +{ + int moduleType; // Type of the module + char *moduleName; // Title/name + char *trackerName; // Tracker software name + int defSpeed, // Initial values + defTempo, + defFlags, + defRestartPos, + intVersion, // Format's internal version + nchannels, + ninstruments, + nextInstruments, + npatterns, + norders; + + int defPanning[jsetNChannels]; + int orderList[jsetMaxOrders]; + JSSPattern *patterns[jsetMaxPatterns + 1]; + JSSInstrument *instruments[jsetMaxInstruments]; + JSSExtInstrument *extInstruments[jsetMaxInstruments]; + +#ifdef JSS_SUP_THREADS + DMMutex *mutex; +#endif + +} JSSModule; + + +#ifdef JSS_SUP_JSSMOD + +#define JSSMOD_VERSION (0x10) + +enum +{ + PATMODE_RAW_HORIZ = 1, + PATMODE_COMP_HORIZ, + PATMODE_RAW_VERT, + PATMODE_COMP_VERT, + PATMODE_RAW_ELEM, + PATMODE_LAST +}; + +/* JSSMOD typedefs + */ +typedef struct +{ + char idMagic[2]; // "JM" + Uint8 idVersion; // 0x10 for 1.0, etc. + Sint16 + norders, // Number of orders in orderlist + npatterns, // Number of patterns + nchannels, // Number of channels + nextInstruments, // Number of extended instruments + ninstruments, // Number of sample-instruments + defFlags, // Flags field: see jmdf* flags + intVersion, // Internal version, format dependant + defRestartPos, // Default restart position in orderlist + defSpeed, // Default speed (ticks/row) + defTempo, // Default tempo (BPM) + patMode; // Pattern data format mode + + /* + - After this, norders long orders table will follow, of type: + Sint16 orderList[norders]; + + - Pattern data, format depends on patMode. + + - Extended instruments (*) + + - Sample instrument headers (*) + + - Sample data (format depends) (*) + + (*) Items are optional and may have been omitted. Fields in the + module and other headers are used to indicate if these items exist. + */ +} JSSMODHeader; + + +typedef struct +{ + Uint16 frame, value; +} JSSMODEnvelopePoint; + + +typedef struct +{ + int flags, + npoints, + sustain, + loopS, + loopE; + JSSMODEnvelopePoint points[jsetMaxEnvPoints]; +} JSSMODEnvelope; + + +typedef struct +{ + Sint16 nsamples; + Uint8 sNumForNotes[jsetNNotes]; + JSSMODEnvelope volumeEnv, panningEnv; + Sint16 vibratoType, + vibratoSweep, + vibratoDepth, + vibratoRate; + int fadeOut; +} JSSMODExtInstrument; + + +typedef struct +{ + int size, // Length in units + loopS, // Loop start position in units + loopE; // Loop end position in units + Sint16 volume, // Volume [jsetMinVol...jsetMaxVol] + flags, // Flags - see jss.h jsfXXXX + C4BaseSpeed, // C4BaseSpeed + ERelNote, // Extended: Relative note value + EFineTune, // Extended: Fine-tune value + EPanning; // Extended: Panning + Uint8 convFlags, // Conversion flags .. jsampXXXX + hasData; // != 0 if there is sample data +} JSSMODInstrument; + + +#define COMP_NOTE (0x01) +#define COMP_INSTRUMENT (0x02) +#define COMP_VOLUME (0x04) +#define COMP_EFFECT (0x08) +#define COMP_PARAM (0x10) +#define COMP_ALL (COMP_NOTE | COMP_INSTRUMENT | COMP_VOLUME | COMP_EFFECT | COMP_PARAM) + + +typedef struct +{ + size_t nrows, size; +} JSSMODPattern; + +#endif + +#ifndef JSS_LIGHT +char* jssASCIItoStr(char *, const char, const size_t); +BOOL jssEncodeSample8(Uint8 *, const size_t, const int); +BOOL jssEncodeSample16(Uint16 *, const size_t, const int); +#endif +BOOL jssDecodeSample8(Uint8 *, const size_t, const int); +BOOL jssDecodeSample16(Uint16 *, const size_t, const int); +JSSModule * jssAllocateModule(void); +int jssFreeModule(JSSModule *); +JSSPattern * jssAllocatePattern(int, int); +JSSInstrument * jssAllocateInstrument(void); +JSSExtInstrument * jssAllocateExtInstrument(void); + +#ifdef JSS_SUP_XM +int jssLoadXM(DMResource *, JSSModule **); +#endif + +#ifdef JSS_SUP_JSSMOD +int jssLoadJSSMOD(Uint8 *, const size_t, JSSModule **); +#endif + +#endif // JSSMOD_H diff -r 000000000000 -r 32250b436bca jssplr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jssplr.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,1593 @@ +/* + * miniJSS - Module playing routines + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2009 Tecnic Software productions (TNSP) + */ +#include "jssplr.h" +#include +#include + +// FIXME!! FIX ME! +#include + +/* Miscellaneous tables + */ +#define jmpNSineTable (256) +static int *jmpSineTable = NULL; + + +static const Sint16 jmpXMAmigaPeriodTab[13 * 8] = { + 907, 900, 894, 887, 881, 875, 868, 862, 856, 850, 844, 838, + 832, 826, 820, 814, 808, 802, 796, 791, 785, 779, 774, 768, + 762, 757, 752, 746, 741, 736, 730, 725, 720, 715, 709, 704, + 699, 694, 689, 684, 678, 675, 670, 665, 660, 655, 651, 646, + 640, 636, 632, 628, 623, 619, 614, 610, 604, 601, 597, 592, + 588, 584, 580, 575, 570, 567, 563, 559, 555, 551, 547, 543, + 538, 535, 532, 528, 524, 520, 516, 513, 508, 505, 502, 498, + 494, 491, 487, 484, 480, 477, 474, 470, 467, 463, 460, 457, + + 453, 450, 447, 443, 440, 437, 434, 431 +}; + + +#define jmpNMODEffectTable (36) +static const char jmpMODEffectTable[jmpNMODEffectTable] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + +/* Helper functions + */ +int jmpNoteToAmigaPeriod(int note, int finetune) +{ + int tmp = (note + finetune + 8); + if (tmp < 0) tmp = 0; else + if (tmp > 103) tmp = 103; + return jmpXMAmigaPeriodTab[tmp]; +} + + +static int jmpGetPeriodFromNote(JSSPlayer *mp, int note, int finetune) +{ + int res; + + if (JMPGETMODFLAGS(mp, jmdfAmigaPeriods)) + { + int mfinetune = finetune / 16, + mnote = (note % 12) * 8, + moctave = note / 12, + period1, period2; + + period1 = jmpNoteToAmigaPeriod(mnote, mfinetune); + + if (finetune < 0) + { + mfinetune--; + finetune = -finetune; + } else + mfinetune++; + + period2 = jmpNoteToAmigaPeriod(mnote, mfinetune); + + mfinetune = finetune & 15; + period1 *= (16 - mfinetune); + period2 *= mfinetune; + + res = ((period1 + period2) * 2) >> moctave; + + //fprintf(stderr, "jmpGetAmigaPeriod(%d, %d) = %d\n", note, finetune, res); + } + else + { + //fprintf(stderr, "jmpGetLinearPeriod(%d, %d) = %d\n", note, finetune, res); + //res = ((120 - note) << 6) - (finetune / 2); + res = 7680 - (note * 64) - (finetune / 2); + if (res < 1) res = 1; + } + + return res; +} + + +static void jmpCSetPitch(JSSPlayer *mp, int channel, int value) +{ + assert(mp != NULL); + assert(mp->pDevice != NULL); + + if (value > 0) + { + if (JMPGETMODFLAGS(mp, jmdfAmigaPeriods)) + { + // Frequency = 8363*1712 / Period +//fprintf(stderr, "jmpCSetPitch::AMIGA(%d, %d)\n", channel, value); + jvmSetFreq(mp->pDevice, channel, 14317456.0f / (double) value); + } + else + { + // Frequency = Frequency = 8363*2^((6*12*16*4 - Period) / (12*16*4)) +//fprintf(stderr, "jmpCSetPitch::Linear(%d, %d)\n", channel, value); + jvmSetFreq(mp->pDevice, channel, + 8363.0f * pow(2.0f, (4608.0f - (double) value) / 768.0f)); + } + } +} + + +static void jmpCSetInstrument(JSSPlayer * mp, int channel) +{ + JSSInstrument *instr; + assert(mp != NULL); + assert(mp->pDevice != NULL); + + instr = mp->iCInstrument[channel]; + + if (instr) + { + jvmSetSample(mp->pDevice, channel, + instr->data, instr->size, + instr->loopS, instr->loopE, + instr->flags); + } +} + + +static void jmpCSetVolume(JSSPlayer * mp, int channel, int volume) +{ + assert(mp != NULL); + assert(mp->pDevice != NULL); + if (volume < 0) volume = 0; else + if (volume > 64) volume = 64; + +//fprintf(stderr, "chn %d: vol=%d, fad=%d, env=%d\n", channel, volume, mp->iCFadeOutVol[channel], mp->iCVolEnv[channel]); + + jvmSetVolume(mp->pDevice, channel, + (mp->iCFadeOutVol[channel] * mp->iCVolEnv[channel] * volume) / (16 * 65536)); +} + + +static void jmpCSetPanning(JSSPlayer * mp, int channel, int panning) +{ + assert(mp != NULL); + assert(mp->pDevice != NULL); + + jvmSetPan(mp->pDevice, channel, + panning + (((mp->iCPanEnv[channel] - 32) * (128 - abs(panning - 128))) / 32)); +} + + +static void jmpCSetPosition(JSSPlayer * mp, int channel, int value) +{ + assert(mp != NULL); + assert(mp->pDevice != NULL); + + jvmSetPos(mp->pDevice, channel, value); +} + + +static void jmpCPlay(JSSPlayer * mp, int channel) +{ + assert(mp != NULL); + assert(mp->pDevice != NULL); + + jvmPlay(mp->pDevice, channel); +} + + +static void jmpCStop(JSSPlayer * mp, int channel) +{ + assert(mp != NULL); + assert(mp->pDevice != NULL); + + jvmStop(mp->pDevice, channel); +} + + + +static int jmpFindEnvPoint(JSSEnvelope * env, int pos) +{ + int i; + + for (i = 0; i < env->npoints - 1; i++) + { + if (env->points[i].frame <= pos && + env->points[i + 1].frame >= pos) + return i; + } + + return -1; +} + + +static void jmpExecEnvelope(JSSEnvelope * env, BOOL keyOff, int * frames, BOOL * doExec, int * result) +{ + int currPoint, delta; + JSSEnvelopePoint *ipf1, *ipf2; + + // OK, find the current point based on frame + currPoint = jmpFindEnvPoint(env, *frames); + + // Check if the envelope has ended + if (currPoint < 0 && (env->flags & jenvfLooped) == 0) + { + *doExec = FALSE; + return; + } + + // Do the envelope looping here, if needed + if ((env->flags & jenvfLooped) && *frames >= env->points[env->loopE].frame) + { + currPoint = env->loopS; + *frames = env->points[currPoint].frame; + } + + // If the current point is OK, then process the envelope + if (currPoint >= 0) + { + // Linearly interpolate the value for given frame + ipf1 = &env->points[currPoint]; + ipf2 = &env->points[currPoint + 1]; + + delta = (ipf2->frame - ipf1->frame); + if (delta > 0) + *result = ipf1->value + ((ipf2->value - ipf1->value) * (*frames - ipf1->frame)) / delta; + else + *result = ipf1->value; + } + + // The frame counter IS processed even if the envelope is not! + if ((env->flags & jenvfSustain) && currPoint == env->sustain && + env->points[currPoint].frame == env->points[env->sustain].frame) { + if (keyOff) (*frames)++; + } else + (*frames)++; +} + + +static void jmpProcessExtInstrument(JSSPlayer * mp, int channel) +{ + JSSExtInstrument *inst = mp->iCExtInstrument[channel]; + + // Get the instrument for envelope data + if (!inst) return; + + // Process the autovibrato + /* + FIXME fix me FIX me!!! todo. + */ + + // Process the volume envelope + if (inst->volumeEnv.flags & jenvfUsed) + { + // Process the instrument volume fadeout + if (mp->iCKeyOff[channel] && (mp->iCFadeOutVol[channel] > 0) && (inst->fadeOut > 0)) + { + int tmp = (mp->iCFadeOutVol[channel] - inst->fadeOut); + if (tmp < 0) tmp = 0; + mp->iCFadeOutVol[channel] = tmp; + + JMPSETNDFLAGS(cdfNewVolume); + } + + if (mp->iCVolEnv_Exec[channel]) + { + // Execute the volume envelope + jmpExecEnvelope(&(inst->volumeEnv), mp->iCKeyOff[channel], + &(mp->iCVolEnv_Frames[channel]), &(mp->iCVolEnv_Exec[channel]), + &(mp->iCVolEnv[channel])); + + JMPSETNDFLAGS(cdfNewVolume); + } + } + else + { + // If the envelope is not used, set max volume + if (mp->iCVolEnv[channel] != mpMaxVol) + { + mp->iCVolEnv[channel] = mpMaxVol; + mp->iCFadeOutVol[channel] = mpMaxFadeoutVol; + JMPSETNDFLAGS(cdfNewVolume); + } + } + + // Process the panning envelope + if (inst->panningEnv.flags & jenvfUsed) + { + if (mp->iCPanEnv_Exec[channel]) + { + // Execute the panning envelope + jmpExecEnvelope(&(inst->panningEnv), mp->iCKeyOff[channel], + &(mp->iCPanEnv_Frames[channel]), &(mp->iCPanEnv_Exec[channel]), + &(mp->iCPanEnv[channel])); + + JMPSETNDFLAGS(cdfNewPanPos); + } + } + else + { + // If the envelope is not used, set center panning + if (mp->iCPanEnv[channel] != mpPanCenter) + { + mp->iCPanEnv[channel] = mpPanCenter; + JMPSETNDFLAGS(cdfNewPanPos); + } + } +} + + +/* + * The player + */ +JSSPlayer *jmpInit(JSSMixer *pDevice) +{ + JSSPlayer *mp; + + // Initialize global tables + if (jmpSineTable == NULL) + { + int i; + if ((jmpSineTable = dmMalloc(jmpNSineTable * sizeof(int))) == NULL) + JSSERROR(DMERR_MALLOC, NULL, "Could not allocate memory for sinus table.\n"); + + for (i = 0; i < 256; i++) + { + float f = ((float) i * M_PI * 2.0f) / 256.0f; + jmpSineTable[i] = (int) (sin(f) * 2048.0f); + } + } + + // Allocate a player structure + mp = dmMalloc0(sizeof(JSSPlayer)); + if (!mp) + JSSERROR(DMERR_MALLOC, NULL, "Could not allocate memory for player structure.\n"); + + // Set variables + mp->pDevice = pDevice; +#ifdef JSS_SUP_THREADS + mp->mutex = dmCreateMutex(); +#endif + + return mp; +} + + +int jmpClose(JSSPlayer * mp) +{ + if (mp == NULL) + return DMERR_NULLPTR; + + // Stop player + jmpStop(mp); + + // Deallocate resources +#ifdef JSS_SUP_THREADS + dmDestroyMutex(mp->mutex); +#endif + + // Clear structure + memset(mp, 0, sizeof(JSSPlayer)); + dmFree(mp); + + return DMERR_OK; +} + + +/* Reset the envelopes for given channel. + */ +static void jmpResetEnvelopes(JSSPlayer * mp, int channel) +{ + assert(mp != NULL); + + mp->iCPanEnv_Frames[channel] = mp->iCVolEnv_Frames[channel] = 0; + mp->iCPanEnv_Exec[channel] = mp->iCVolEnv_Exec[channel] = TRUE; +} + + +/* Clear module player structure + */ +void jmpClearPlayer(JSSPlayer * mp) +{ + int i; + assert(mp != NULL); + JSS_LOCK(mp); + + // Initialize general variables + mp->iPatternDelay = 0; + mp->newRowSet = FALSE; + mp->newOrderSet = FALSE; + mp->iTick = jsetNotSet; + mp->iRow = 0; + mp->iLastPatLoopRow = 0; + + // Initialize channel data + memset(&(mp->iPatLoopRow), 0, sizeof(mp->iPatLoopRow)); + memset(&(mp->iPatLoopCount), 0, sizeof(mp->iPatLoopCount)); + + memset(&mp->iLastPortaParam, 0, sizeof(mp->iLastPortaParam)); + memset(&mp->iLastPortaToNoteParam, 0, sizeof(mp->iLastPortaToNoteParam)); + memset(&mp->iLastPortaToNotePitch, 0, sizeof(mp->iLastPortaToNotePitch)); + + memset(&mp->iVibratoPos, 0, sizeof(mp->iVibratoPos)); + memset(&mp->iVibratoSpeed, 0, sizeof(mp->iVibratoSpeed)); + memset(&mp->iVibratoDepth, 0, sizeof(mp->iVibratoDepth)); + memset(&mp->iVibratoWC, 0, sizeof(mp->iVibratoWC)); + + memset(&mp->iTremoloPos, 0, sizeof(mp->iTremoloPos)); + memset(&mp->iTremoloSpeed, 0, sizeof(mp->iTremoloSpeed)); + memset(&mp->iTremoloDepth, 0, sizeof(mp->iTremoloDepth)); + memset(&mp->iTremoloWC, 0, sizeof(mp->iTremoloWC)); + + memset(&mp->iLastTremorParam, 0, sizeof(mp->iLastTremorParam)); + memset(&mp->iTremorCount, 0, sizeof(mp->iTremorCount)); + memset(&mp->iLastSampleOffset, 0, sizeof(mp->iLastSampleOffset)); + memset(&mp->iLastRetrigParam, 0, sizeof(mp->iLastRetrigParam)); + memset(&mp->iLastVolSlideParam, 0, sizeof(mp->iLastVolSlideParam)); + + memset(&mp->iRetrigNDFlags, 0, sizeof(mp->iRetrigNDFlags)); + memset(&mp->iSaveNDFlags, 0, sizeof(mp->iSaveNDFlags)); + + memset(&mp->iCNewDataFlags, 0, sizeof(mp->iCNewDataFlags)); + memset(&mp->iCPitch, 0, sizeof(mp->iCPitch)); + memset(&mp->iCPosition, 0, sizeof(mp->iCPosition)); + memset(&mp->iCVolume, 0, sizeof(mp->iCVolume)); + memset(&mp->iCFadeOutVol, 0, sizeof(mp->iCFadeOutVol)); + memset(&mp->iCVolEnv, 0, sizeof(mp->iCVolEnv)); + + memset(&mp->iCAutoVib_Frames, 0, sizeof(mp->iCAutoVib_Frames)); + memset(&mp->iCPanEnv_Frames, 0, sizeof(mp->iCPanEnv_Frames)); + memset(&mp->iCVolEnv_Frames, 0, sizeof(mp->iCVolEnv_Frames)); + + memset(&mp->iCLastFineVolumeslideUpParam, 0, sizeof(mp->iCLastFineVolumeslideUpParam)); + memset(&mp->iCLastFineVolumeslideDownParam, 0, sizeof(mp->iCLastFineVolumeslideDownParam)); + memset(&mp->iCLastExtraFinePortamentoUpParam, 0, sizeof(mp->iCLastExtraFinePortamentoUpParam)); + memset(&mp->iCLastExtraFinePortamentoDownParam, 0, sizeof(mp->iCLastExtraFinePortamentoDownParam)); + memset(&mp->iCLastFinePortamentoUpParam, 0, sizeof(mp->iCLastFinePortamentoUpParam)); + memset(&mp->iCLastFinePortamentoDownParam, 0, sizeof(mp->iCLastFinePortamentoDownParam)); + + memset(&mp->iCPanEnv_Exec, 0, sizeof(mp->iCPanEnv_Exec)); + memset(&mp->iCVolEnv_Exec, 0, sizeof(mp->iCVolEnv_Exec)); + memset(&mp->iCKeyOff, 0, sizeof(mp->iCKeyOff)); + + for (i = 0; i < jsetNChannels; i++) + { + mp->iCNote[i] = jsetNotSet; + mp->iCInstrument[i] = NULL; + mp->iCInstrumentN[i] = jsetNotSet; + mp->iCExtInstrument[i] = NULL; + mp->iCExtInstrumentN[i] = jsetNotSet; + mp->iCPanning[i] = mpPanCenter; + mp->iCPanEnv[i] = mpPanCenter; + } + + JSS_UNLOCK(mp); +} + + +/* Set module + */ +void jmpSetModule(JSSPlayer * mp, JSSModule * pModule) +{ + assert(mp != NULL); + JSS_LOCK(mp); + + jmpStop(mp); + jmpClearPlayer(mp); + + mp->pModule = pModule; + + JSS_UNLOCK(mp); +} + + +/* Stop playing + */ +void jmpStop(JSSPlayer * mp) +{ + assert(mp != NULL); + JSS_LOCK(mp); + + if (mp->isPlaying) + { + // Remove callback + jvmRemoveCallback(mp->pDevice); + mp->isPlaying = FALSE; + } + + JSS_UNLOCK(mp); +} + + +/* Resume playing + */ +void jmpResume(JSSPlayer * mp) +{ + assert(mp != NULL); + JSS_LOCK(mp); + + if (!mp->isPlaying) + { + int result = jvmSetCallback(mp->pDevice, jmpExec, (void *) mp); + if (result != DMERR_OK) + JSSERROR(result,, "Could not initialize callback for player.\n"); + + mp->isPlaying = TRUE; + } + + JSS_UNLOCK(mp); +} + + +/* Sets new order using given value as reference. + * Jumps over skip-points and invalid values, loops + * to first order if enabled. + */ +static void jmpSetNewOrder(JSSPlayer * mp, int order) +{ + BOOL orderOK; + int pattern; + + pattern = jsetOrderEnd; + mp->iOrder = jsetNotSet; + orderOK = FALSE; + + while (!orderOK) + { + if (order < 0 || order >= mp->pModule->norders) + { + jmpStop(mp); + orderOK = TRUE; + } + else + { + pattern = mp->pModule->orderList[order]; + if (pattern == jsetOrderSkip) + { + order++; + } + else + if (pattern >= mp->pModule->npatterns || pattern == jsetOrderEnd) + { + jmpStop(mp); + orderOK = TRUE; + } + else + { + // All OK + orderOK = TRUE; + mp->pPattern = mp->pModule->patterns[pattern]; + mp->iPattern = pattern; + mp->iOrder = order; + } + } + } +} + + +/* Set new tempo-value of the player. + */ +void jmpSetTempo(JSSPlayer * mp, int tempo) +{ + assert(mp != NULL); + JSS_LOCK(mp); + assert(mp->pDevice != NULL); + + mp->iTempo = tempo; + jvmSetCallbackFreq(mp->pDevice, (tempo * 2) / 5); + JSS_UNLOCK(mp); +} + + +void jmpClearChannels(JSSPlayer * mp) +{ + int i; + assert(mp != NULL); + JSS_LOCK(mp); + assert(mp->pDevice != NULL); + assert(mp->pModule != NULL); + + for (i = 0; i < mp->pModule->nchannels; i++) + { + jvmStop(mp->pDevice, i); + jvmClear(mp->pDevice, i); + } + + JSS_UNLOCK(mp); +} + + +/* Starts playing module from a given ORDER. + */ +int jmpPlayOrder(JSSPlayer * mp, int iStartOrder) +{ + int result; + assert(mp != NULL); + + JSS_LOCK(mp); + assert(mp->pModule != NULL); + + // Stop if already playing + jmpStop(mp); + + jmpClearChannels(mp); + + // Check starting order + if (iStartOrder < 0 || iStartOrder >= mp->pModule->norders) + { + JSS_UNLOCK(mp); + JSSERROR(DMERR_INVALID_ARGS, DMERR_INVALID_ARGS, "Invalid playing startorder given.\n"); + } + + // Initialize playing + jmpClearPlayer(mp); + + jmpSetNewOrder(mp, iStartOrder); + + if (mp->iOrder == jsetNotSet) + { + JSS_UNLOCK(mp); + JSSERROR(DMERR_NOT_SUPPORTED, DMERR_NOT_SUPPORTED, + "Could not start playing from given order #%i\n", iStartOrder); + } + + mp->iSpeed = mp->pModule->defSpeed; + jmpSetTempo(mp, mp->pModule->defTempo); + + // Set callback + result = jvmSetCallback(mp->pDevice, jmpExec, (void *) mp); + if (result != DMERR_OK) + { + JSS_UNLOCK(mp); + JSSERROR(result, result, "Could not initialize callback for player.\n"); + } + + mp->isPlaying = TRUE; + + JSS_UNLOCK(mp); + return 0; +} + + +/* Play given pattern + */ +int jmpPlayPattern(JSSPlayer * mp, int pattern) +{ + int result; + assert(mp != NULL); + JSS_LOCK(mp); + assert(mp->pModule != NULL); + + // Stop if already playing + jmpStop(mp); + jmpClearChannels(mp); + + // Initialize playing + jmpClearPlayer(mp); + + mp->iPattern = pattern; + mp->iSpeed = mp->pModule->defSpeed; + jmpSetTempo(mp, mp->pModule->defTempo); + + // Set callback + result = jvmSetCallback(mp->pDevice, jmpExec, (void *) mp); + if (result != DMERR_OK) + { + JSS_UNLOCK(mp); + JSSERROR(result, result, "Could not initialize callback for player.\n"); + } + + mp->isPlaying = TRUE; + + JSS_UNLOCK(mp); + return 0; +} + + +/* Set volume for given module channel. + */ +static void jmpSetVolume(JSSPlayer * mp, int channel, int value) +{ + // Check values + if (value < mpMinVol) + value = mpMinVol; + else if (value > mpMaxVol) + value = mpMaxVol; + + // Set the volume + mp->iCVolume[channel] = value; + JMPSETNDFLAGS(cdfNewVolume); +} + +#define jmpChangeVolume(Q, Z, X) jmpSetVolume(Q, Z, mp->iCVolume[channel] + (X)) + + +/* Change the pitch of given channel by ADelta. + */ +static void jmpChangePitch(JSSPlayer * mp, int channel, int delta) +{ + int value; + + // Calculate new pitch and check it + value = (mp->iCPitch[channel] + delta); + if (value < 0) + value = 0; + + // Set the new pitch + mp->iCPitch[channel] = value; + JMPSETNDFLAGS(cdfNewPitch); +} + + +/* Do a note portamento (pitch slide) effect for given module channel. + */ +static void jmpDoPortamento(JSSPlayer * mp, int channel) +{ + // Check for zero parameter + if (mp->iLastPortaToNoteParam[channel] == 0) + { + JMPSETNDFLAGS(cdfNewPitch); + return; + } + + /* Slide the pitch of channel to the destination value + * with speed of iLastPortaToNoteParam[] * 4 and stop when it equals. + */ + if (mp->iCPitch[channel] != mp->iLastPortaToNotePitch[channel]) + { + if (mp->iCPitch[channel] < mp->iLastPortaToNotePitch[channel]) + { + // Increase pitch UP + jmpChangePitch(mp, channel, mp->iLastPortaToNoteParam[channel] * 4); + if (mp->iCPitch[channel] > mp->iLastPortaToNotePitch[channel]) + mp->iCPitch[channel] = mp->iLastPortaToNotePitch[channel]; + } + else + { + // Decrease pitch DOWN + jmpChangePitch(mp, channel, -(mp->iLastPortaToNoteParam[channel] * 4)); + if (mp->iCPitch[channel] < mp->iLastPortaToNotePitch[channel]) + mp->iCPitch[channel] = mp->iLastPortaToNotePitch[channel]; + } + } +} + + +/* Do a tremolo effect for given module channel. + */ +static void jmpDoTremolo(JSSPlayer * mp, int channel) +{ + int delta, pos, depth; + + // Check settings + if (mp->iTremoloDepth[channel] == 0 || mp->iTremoloSpeed[channel] == 0) + return; + + // Get position of tremolo waveform + pos = mp->iTremoloPos[channel] & 255; + depth = mp->iTremoloDepth[channel]; + + switch (mp->iTremoloWC[channel] & 3) + { + case 0: // Sine-wave + delta = (jmpSineTable[pos] * depth) / 2048; + break; + + case 1: // Ramp down + delta = ((pos - 128) * depth) / 128; + break; + + case 2: // Square + delta = (((pos & 128) - 64) * depth) / 64; + break; + + default: + return; + } + + // Set the new volume + jmpCSetVolume(mp, channel, mp->iCVolume[channel] + delta); + + // Advance tremolo waveform position + mp->iTremoloPos[channel] += mp->iTremoloSpeed[channel]; + if (mp->iTremoloPos[channel] > 255) + mp->iTremoloPos[channel] = 0; +} + + +/* Do a vibrato effect for given module channel. + */ +static void jmpDoVibrato(JSSPlayer * mp, int channel) +{ + int delta, pos, depth; + + // Check settings + if (mp->iVibratoDepth[channel] == 0 || mp->iVibratoSpeed[channel] == 0) + return; + + // Get position of vibrato waveform + pos = mp->iVibratoPos[channel] & 255; + depth = mp->iVibratoDepth[channel]; + + switch (mp->iVibratoWC[channel] & 3) + { + case 0: // Sine-wave + delta = (jmpSineTable[pos] * depth) / 2048; + break; + + case 1: // Ramp down + delta = ((pos - 128) * depth) / 16; + break; + + case 2: // Square + delta = (((pos & 128) - 64) * depth) / 8; + break; + + default: + return; + } + + // Set the new frequency + jmpCSetPitch(mp, channel, mp->iCPitch[channel] + delta); + + // Advance vibrato waveform position + mp->iVibratoPos[channel] += mp->iVibratoSpeed[channel]; + if (mp->iVibratoPos[channel] > 255) + mp->iVibratoPos[channel] = 0; +} + + +/* Do a volume slide effect for given module channel. + */ +static void jmpDoVolumeSlide(JSSPlayer * mp, int channel, int param) +{ + int paramX, paramY; + + JMPMAKEPARAM(param, paramX, paramY) + + if (paramY == 0) + jmpChangeVolume(mp, channel, paramX); + if (paramX == 0) + jmpChangeVolume(mp, channel, -paramY); +} + + +/* Execute a pattern loop effect/command for given module channel. + * + * This routine works for most of the supported formats, as they + * use the 'standard' implementation ascending from MOD. However, + * here is included also a slightly kludgy implementation of the + * FT2 patloop bug. + */ +static void jmpDoPatternLoop(JSSPlayer * mp, int channel, int paramY) +{ + // Check what we need to do + if (paramY > 0) + { + // SBx/E6x loops 'x' times + if (mp->iPatLoopCount[channel] == 1) + mp->iPatLoopCount[channel] = 0; + else + { + // Check if we need to set the count + if (mp->iPatLoopCount[channel] == 0) + mp->iPatLoopCount[channel] = (paramY + 1); + + // Loop to specified row + mp->iPatLoopCount[channel]--; + mp->iNewRow = mp->iPatLoopRow[channel]; + mp->newRowSet = TRUE; + } + } + else + { + // SB0/E60 sets the loop start point + mp->iPatLoopRow[channel] = mp->iRow; + + // This is here because of the infamous FT2 patloop bug + mp->iLastPatLoopRow = mp->iRow; + } +} + + +/* Do arpeggio effect + */ +static void jmpDoArpeggio(JSSPlayer * mp, int channel, int paramY, int paramX) +{ + JSSInstrument *tempInst = mp->iCInstrument[channel]; + if (tempInst) + { + int tmp = mp->iCNote[channel]; + if (tmp == jsetNotSet || tmp == jsetNoteOff) return; + switch (mp->iTick & 3) + { + case 1: + tmp += paramX; + break; + case 2: + tmp += paramY; + break; + } + jmpCSetPitch(mp, channel, jmpGetPeriodFromNote(mp, tmp + tempInst->ERelNote, tempInst->EFineTune)); + } +} + + +/* + * Process pattern effects + */ +static void jmpProcessRowEffect(JSSPlayer * mp, int channel, JSSNote * currNote) +{ + int param, paramX, paramY; + char effect; + + param = currNote->param; + JMPMAKEPARAM(param, paramX, paramY); + JMPGETEFFECT(effect, currNote->effect); + + switch (effect) + { + case '0': // 0xy = Arpeggio + jmpDoArpeggio(mp, channel, paramX, paramY); + break; + + case 'W': // Used widely in demo-music as MIDAS Sound System sync-command + case 'Q': // SoundTracker/OpenCP: Qxx = Set LP filter resonance + case 'Z': // SoundTracker/OpenCP: Zxx = Set LP filter cutoff freq + break; + + case '1': + case '2': // 1xy = Portamento Up, 2xy = Portamento Down : IMPL.VERIFIED + if (param) + mp->iLastPortaParam[channel] = param; + break; + + case '3': // 3xy = Porta To Note + if (param) + mp->iLastPortaToNoteParam[channel] = param; + + if (currNote->note != jsetNotSet && currNote->note != jsetNoteOff) { + mp->iLastPortaToNotePitch[channel] = mp->iCPitch[channel]; + mp->iCPitch[channel] = mp->iCOldPitch[channel]; + JMPUNSETNDFLAGS(cdfNewPitch | cdfNewInstr | cdfNewPanPos); + } + break; + + case '4': // 4xy = Vibrato : IMPL.VERIFIED + if (paramX) + mp->iVibratoSpeed[channel] = paramX; + + if (paramY) + mp->iVibratoDepth[channel] = paramY; + + if ((mp->iVibratoWC[channel] & 4) == 0) + mp->iVibratoPos[channel] = 0; + break; + + case '5': // 5xy = Portamento + Volume Slide + case '6': // 6xy = Vibrato + Volume slide + if (param) + mp->iLastVolSlideParam[channel] = param; + break; + + case '7': // 7xy = Tremolo + if (paramX) + mp->iTremoloSpeed[channel] = paramX; + + if (paramY) + mp->iTremoloDepth[channel] = paramY; + + if ((mp->iTremoloWC[channel] & 4) == 0) + mp->iTremoloPos[channel] = 0; + break; + + case '8': // 8xx = Set Panning + JMPDEBUG("Set Panning used, UNIMPLEMENTED"); + break; + + case '9': // 9xx = Set Sample Offset : IMPL.VERIFIED + if (mp->iCNewDataFlags[channel] & cdfNewPitch) { + mp->iCPosition[channel] = param * 0x100; + JMPSETNDFLAGS(cdfNewPos); + } + break; + + case 'A': // Axy = Volume Slide : IMPL.VERIFIED + if (param) + mp->iLastVolSlideParam[channel] = param; + break; + + case 'B': // Bxx = Pattern Jump : IMPL.VERIFIED + mp->iNewOrder = param; + mp->newOrderSet = TRUE; + mp->jumpFlag = TRUE; + mp->iLastPatLoopRow = 0; + break; + + case 'C': // Cxx = Set Volume : IMPL.VERIFIED + jmpSetVolume(mp, channel, param); + break; + + case 'D': // Dxx = Pattern Break : IMPL.VERIFIED + // Compute the new row + mp->iNewRow = (paramX * 10) + paramY; + if (mp->iNewRow >= mp->pPattern->nrows) + mp->iNewRow = 0; + + mp->newRowSet = TRUE; + + // Now we do some tricky tests + if (!mp->breakFlag && !mp->jumpFlag) { + mp->iNewOrder = mp->iOrder + 1; + mp->newOrderSet = TRUE; + } + + mp->breakFlag = TRUE; + break; + + case 'E': // Exy = Special Effects + switch (paramX) { + case 0x00: // E0x - Set filter (NOT SUPPORTED) + JMPDEBUG("Set Filter used, UNSUPPORTED"); + break; + + case 0x01: // E1x - Fine Portamento Up + if (paramY) + mp->iCLastFinePortamentoUpParam[channel] = paramY; + + jmpChangePitch(mp, channel, -(mp->iCLastFinePortamentoUpParam[channel] * 4)); + break; + + case 0x02: // E2x - Fine Portamento Down + if (paramY) + mp->iCLastFinePortamentoDownParam[channel] = paramY; + + jmpChangePitch(mp, channel, (mp->iCLastFinePortamentoDownParam[channel] * 4)); + break; + + case 0x03: // E3x - Glissando Control (NOT SUPPORTED) + break; + + case 0x04: // E4x - Set Vibrato waveform + mp->iVibratoWC[channel] = paramY; + break; + + case 0x05: // E5x - Set Finetune + JMPDEBUG("Set Finetune used, UNIMPLEMENTED"); + break; + + case 0x06: // E6x - Set Pattern Loop + jmpDoPatternLoop(mp, channel, paramY); + break; + + case 0x07: // E7x - Set Tremolo waveform + mp->iTremoloWC[channel] = paramY; + break; + + case 0x08: // E8x - Set Pan Position + mp->iCPanning[channel] = (paramY * 16); + JMPSETNDFLAGS(cdfNewPanPos); + break; + + case 0x09: // E9x - Retrig note + JMPDEBUG("Retrig Note used, UNIMPLEMENTED"); + break; + + case 0x0a: // EAx - Fine Volumeslide Up + if (paramY) + mp->iCLastFineVolumeslideUpParam[channel] = paramY; + + jmpChangeVolume(mp, channel, mp->iCLastFineVolumeslideUpParam[channel]); + break; + + case 0x0b: // EBx - Fine Volumeslide Down + if (paramY) + mp->iCLastFineVolumeslideDownParam[channel] = paramY; + jmpChangeVolume(mp, channel, -(mp->iCLastFineVolumeslideDownParam[channel])); + break; + + case 0x0c: // ECx - Set Note Cut (NOT PROCESSED IN TICK0) + break; + + case 0x0d: // EDx - Set Note Delay : IMPL.VERIFIED + if (paramY > 0) + { + // Save the ND-flags, then clear + mp->iSaveNDFlags[channel] = mp->iCNewDataFlags[channel]; + mp->iCNewDataFlags[channel] = 0; + // TODO .. does this only affect NOTE or also instrument? + } + break; + + case 0x0e: // EEx - Set Pattern Delay : IMPL.VERIFIED + mp->iPatternDelay = paramY; + break; + + case 0x0f: // EFx - Invert Loop (NOT SUPPORTED) + JMPDEBUG("Invert Loop used, UNSUPPORTED"); + break; + + default: + JMPDEBUG("Unsupported special command used"); + } + break; + + case 'F': // Fxy = Set Speed / Tempo : IMPL.VERIFIED + if (param > 0) + { + if (param < 0x20) + mp->iSpeed = param; + else + jmpSetTempo(mp, param); + } + break; + + case 'G': // Gxx = Global Volume + mp->iGlobalVol = param; + JMPSETNDFLAGS(cdfNewGlobalVol); + break; + + + case 'H': // Hxx = Global Volume Slide + JMPDEBUG("Global Volume Slide used, UNIMPLEMENTED"); + break; + + case 'K': // Kxx = Key-off (Same as key-off note) + mp->iCKeyOff[channel] = TRUE; + break; + + case 'L': // Lxx = Set Envelope Position + JMPDEBUG("Set Envelope Position used, NOT verified with FT2"); + mp->iCPanEnv_Frames[channel] = param; + mp->iCVolEnv_Frames[channel] = param; + mp->iCPanEnv_Exec[channel] = TRUE; + mp->iCVolEnv_Exec[channel] = TRUE; + break; + + case 'R': // Rxy = Multi Retrig note + JMPDEBUG("Multi Retrig Note used, UNIMPLEMENTED"); + break; + + case 'T': // Txy = Tremor + if (param) + mp->iLastTremorParam[channel] = param; + break; + + case 'X': // Xxy = Extra Fine Portamento + switch (paramX) + { + case 0x01: // X1y - Extra Fine Portamento Up + if (paramY) + mp->iCLastExtraFinePortamentoUpParam[param] = paramY; + + jmpChangePitch(mp, channel, -(mp->iCLastExtraFinePortamentoUpParam[param])); + break; + + case 0x02: // X2y - Extra Fine Portamento Down + if (paramY) + mp->iCLastExtraFinePortamentoDownParam[param] = paramY; + + jmpChangePitch(mp, channel, mp->iCLastExtraFinePortamentoUpParam[param]); + break; + + default: + JMPDEBUG("Unsupported value in Extra Fine Portamento command!"); + break; + } + break; + + default: + JMPDEBUG("Unsupported effect"); + break; + } +} + + +static void jmpProcessNewRow(JSSPlayer * mp, int channel) +{ + JSSNote *currNote; + JSSExtInstrument *extInst = NULL; + JSSInstrument *inst = NULL; + BOOL newNote = FALSE; + int tmp, paramX, paramY; + + JMPGETNOTE(currNote, mp->iRow, channel); + + // Check for a new note/keyoff here + if (currNote->note == jsetNoteOff) + mp->iCKeyOff[channel] = TRUE; + else + if (currNote->note >= 0 && currNote->note <= 96) + { + // New note was set + newNote = TRUE; + mp->iCNote[channel] = currNote->note; + } + + // Check for new instrument + if (currNote->instrument != jsetNotSet) { + /* Envelopes and ext.instrument fadeout are initialized always if + * new instrument is set, even if the instrument does not exist. + */ + jmpResetEnvelopes(mp, channel); + mp->iCKeyOff[channel] = FALSE; + mp->iCFadeOutVol[channel] = mpMaxFadeoutVol; + + // We save the instrument number here for later use + if (currNote->instrument >= 0 && currNote->instrument < mp->pModule->nextInstruments) + mp->iCExtInstrumentN[channel] = currNote->instrument; + } + + /* ONLY if newNote was SET NOW and ExtInstrument HAS BEEN set, we can + * set new pitches, and other things... + */ + if (newNote) + { + if (mp->iCExtInstrumentN[channel] != jsetNotSet) + extInst = mp->pModule->extInstruments[mp->iCExtInstrumentN[channel]]; + else + extInst = NULL; + + if (extInst) + { + // Set instrument + int note = mp->iCNote[channel]; + mp->iCExtInstrument[channel] = extInst; + + // We set new Instrument ONLY if NEW NOTE has been set + if (note != jsetNotSet) + { + // Get instrument number + tmp = extInst->sNumForNotes[note]; + + if (tmp >= 0 && tmp < mp->pModule->ninstruments) { + // Set the new instrument + inst = mp->pModule->instruments[tmp]; + mp->iCInstrumentN[channel] = tmp; + mp->iCInstrument[channel] = inst; + mp->iCVolume[channel] = inst->volume; + mp->iCPanning[channel] = inst->EPanning; + mp->iCPosition[channel] = 0; + + // Set NDFlags + JMPSETNDFLAGS(cdfNewInstr | cdfNewPos | cdfNewPanPos | cdfNewVolume); + } + } + } + } + + if (inst) + { + // Save old pitch for later use + mp->iCOldPitch[channel] = mp->iCPitch[channel]; + + // Compute new pitch + tmp = (mp->iCNote[channel] + inst->ERelNote); +//fprintf(stderr, "HEH: %d + %d = %d\n", mp->iCNote[channel], inst->ERelNote, tmp); + if (tmp < 0) + tmp = 0; + else if (tmp > 119) + tmp = 119; + + mp->iCPitch[channel] = jmpGetPeriodFromNote(mp, tmp, inst->EFineTune); + JMPSETNDFLAGS(cdfNewPitch); + } + + // Process the volume column + JMPMAKEPARAM(currNote->volume, paramX, paramY); + + switch (paramX) + { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + jmpSetVolume(mp, channel, currNote->volume); + break; + + case 0x07: // Dx = Fine Volumeslide Down : IMPL.VERIFIED + jmpChangeVolume(mp, channel, -paramY); + break; + + case 0x08: // Ux = Fine Volumeslide Up : IMPL.VERIFIED + jmpChangeVolume(mp, channel, paramY); + break; + + case 0x09: // Sx = Set vibrato speed : IMPL.VERIFIED + mp->iVibratoSpeed[channel] = paramY; + break; + + case 0x0a: // Vx = Vibrato : IMPL.VERIFIED + if (paramY) + mp->iVibratoDepth[channel] = paramY; + break; + + case 0x0e: // Mx = Porta To Note : IMPL.VERIFIED + if (paramY) + mp->iLastPortaToNoteParam[channel] = paramY; + + if (currNote->note != jsetNotSet && currNote->note != jsetNoteOff) { + mp->iLastPortaToNotePitch[channel] = mp->iCPitch[channel]; + mp->iCPitch[channel] = mp->iCOldPitch[channel]; + JMPUNSETNDFLAGS(cdfNewPitch | cdfNewInstr | cdfNewPanPos); + } + break; + } + + // ...And finally process the Normal effects + if (currNote->effect != jsetNotSet) + jmpProcessRowEffect(mp, channel, currNote); +} + + +static void jmpProcessEffects(JSSPlayer * mp, int channel) +{ + JSSNote *currNote; + int param, paramX, paramY, tmp; + char effect; + + // Process the volume column effects + JMPGETNOTE(currNote, mp->iRow, channel); + JMPMAKEPARAM(currNote->volume, paramX, paramY); + + switch (paramX) + { + case 0x05: // -x = Volumeslide Down : IMPL.VERIFIED + jmpChangeVolume(mp, channel, -paramY); + break; + + case 0x06: // +x = Volumeslide Down : IMPL.VERIFIED + jmpChangeVolume(mp, channel, paramY); + break; + + case 0x0a: // Vx = Vibrato : IMPL.VERIFIED + jmpDoVibrato(mp, channel); + break; + + case 0x0e: // Mx = Porta To Note : IMPL.VERIFIED + jmpDoPortamento(mp, channel); + break; + } + + // ...And finally process the Normal effects + if (currNote->effect == jsetNotSet) + return; + + param = currNote->param; + JMPMAKEPARAM(param, paramX, paramY); + JMPGETEFFECT(effect, currNote->effect); + + switch (effect) + { + case '0': // 0xy = Arpeggio + jmpDoArpeggio(mp, channel, paramX, paramY); + break; + + case '1': // 1xy = Portamento Up + if (mp->iLastPortaParam[channel] > 0) + jmpChangePitch(mp, channel, -(mp->iLastPortaParam[channel] * 4)); + break; + + case '2': // 2xy = Portamento Down + if (mp->iLastPortaParam[channel] > 0) + jmpChangePitch(mp, channel, (mp->iLastPortaParam[channel] * 4)); + break; + + case '3': // 3xy = Porta To Note + jmpDoPortamento(mp, channel); + break; + + case '4': // 4xy = Vibrato + jmpDoVibrato(mp, channel); + break; + + case '5': // 5xy = Portamento + Volume Slide + jmpDoPortamento(mp, channel); + jmpDoVolumeSlide(mp, channel, mp->iLastVolSlideParam[channel]); + break; + + case '6': // 6xy = Vibrato + Volume Slide + jmpDoVibrato(mp, channel); + jmpDoVolumeSlide(mp, channel, mp->iLastVolSlideParam[channel]); + break; + + case '7': // 7xy = Tremolo + jmpDoTremolo(mp, channel); + break; + + case 'A': // Axy = Volume slide + jmpDoVolumeSlide(mp, channel, mp->iLastVolSlideParam[channel]); + break; + + case 'E': // Exy = Special Effects + switch (paramX) + { + case 0x0c: // ECx - Set Note Cut + if (mp->iTick == paramY) + jmpSetVolume(mp, channel, jsetMinVol); + break; + + case 0x0d: // EDx - Set Note Delay + if (mp->iTick == paramY) + mp->iCNewDataFlags[channel] = mp->iSaveNDFlags[channel]; + break; + } + break; + + case 'T': // Txy = Tremor + JMPMAKEPARAM(mp->iLastTremorParam[channel], paramX, paramY) + paramX++; + paramY++; + tmp = (mp->iTremorCount[channel] % (paramX + paramY)); + if (tmp < paramX) + jmpCSetVolume(mp, channel, mp->iCVolume[channel]); + else + jmpCSetVolume(mp, channel, jsetMinVol); + + mp->iTremorCount[channel] = (tmp + 1); + break; + } +} + + +/* This is the main processing callback-loop of a module player. + * It processes the ticks, calling the needed jmpProcessNewRow() + * and jmpProcessEffects() methods for processing the module playing. + */ +void jmpExec(void *pDEV, void *pMP) +{ + JSSPlayer *mp; + JSSMixer *dev; + int channel, flags; + + // Check some things via assert() + assert(pMP != NULL); + mp = (JSSPlayer *) pMP; + JSS_LOCK(mp); + + dev = (JSSMixer *) pDEV; + assert(mp->pDevice == dev); + assert(mp->pModule != NULL); + + // Check if we are playing + if (!mp->isPlaying) + goto out; + + // Clear channel new data flags + mp->jumpFlag = FALSE; + mp->breakFlag = FALSE; + memset(mp->iCNewDataFlags, 0, sizeof(mp->iCNewDataFlags)); + +//fprintf(stderr, "1: iTick=%d, iOrder=%d, iPattern=%d, iRow=%d\n", mp->iTick, mp->iOrder, mp->iPattern, mp->iRow); + + // Check for init-tick + if (mp->iTick < 0) + { + // Initialize pattern + if (mp->iOrder != jsetNotSet) + jmpSetNewOrder(mp, mp->iOrder); + + mp->iNewRow = 0; + mp->newRowSet = TRUE; + mp->iTick = mp->iSpeed; + } + +//fprintf(stderr, "2: iTick=%d, iOrder=%d, iPattern=%d, iRow=%d\n", mp->iTick, mp->iOrder, mp->iPattern, mp->iRow); + + // Check if we are playing + if (!mp->isPlaying) + goto out; + + assert(mp->pPattern); + + // Update the tick + mp->iTick++; + if (mp->iTick >= mp->iSpeed) + { + // Re-init tick counter + mp->iTick = 0; + + // Check pattern delay + if (mp->iPatternDelay > 0) + mp->iPatternDelay--; + else + { + // New pattern row + if (mp->newRowSet) + { + mp->iRow = mp->iNewRow; + mp->newRowSet = FALSE; + } else + mp->iRow++; + + // Check for end of pattern + if (mp->iRow >= mp->pPattern->nrows) + { + // Go to next order + if (mp->iOrder != jsetNotSet) + jmpSetNewOrder(mp, mp->iOrder + 1); + else + mp->isPlaying = FALSE; + + // Check for FT2 quirks + if (JMPGETMODFLAGS(mp, jmdfFT2Replay)) + mp->iRow = mp->iLastPatLoopRow; + else + mp->iRow = 0; + } + + if (!mp->isPlaying) + goto out; + + // Check current order + if (mp->newOrderSet) + { + jmpSetNewOrder(mp, mp->iNewOrder); + mp->newOrderSet = FALSE; + } + +//fprintf(stderr, "3: iTick=%d, iOrder=%d, iPattern=%d, iRow=%d\n", mp->iTick, mp->iOrder, mp->iPattern, mp->iRow); + + if (!mp->isPlaying) + goto out; + + // TICK #0: Process new row + for (channel = 0; channel < mp->pModule->nchannels; channel++) + jmpProcessNewRow(mp, channel); + } // iPatternDelay + } // iTick + else + { + // Implement FT2's pattern delay-effect: don't update effects while on patdelay + if (!JMPGETMODFLAGS(mp, jmdfFT2Replay) || + (JMPGETMODFLAGS(mp, jmdfFT2Replay) && mp->iPatternDelay <= 0)) + { + // TICK n: Process the effects + for (channel = 0; channel < mp->pModule->nchannels; channel++) + jmpProcessEffects(mp, channel); + } + } + + // Check if playing has stopped + if (!mp->isPlaying) + goto out; + + // Update player data to audio device/mixer + for (channel = 0; channel < mp->pModule->nchannels; channel++) + { + // Process extended instruments + jmpProcessExtInstrument(mp, channel); + + // Check NDFlags and update channel data + flags = mp->iCNewDataFlags[channel]; + if (flags) + { + // Check if we stop? + if (flags & cdfStop) + jmpCStop(mp, channel); + else + { + // No, handle other flags + if (flags & cdfNewInstr) + { + jmpCSetInstrument(mp, channel); + jmpCPlay(mp, channel); + } + + if (flags & cdfNewPitch) + jmpCSetPitch(mp, channel, mp->iCPitch[channel]); + + if (flags & cdfNewPos) + jmpCSetPosition(mp, channel, mp->iCPosition[channel]); + + if (flags & cdfNewVolume) + jmpCSetVolume(mp, channel, mp->iCVolume[channel]); + + if (flags & cdfNewPanPos) + jmpCSetPanning(mp, channel, mp->iCPanning[channel]); + + if (flags & cdfNewGlobalVol) + jvmSetGlobalVol(mp->pDevice, mp->iGlobalVol); + } + } + } + +out: + JSS_UNLOCK(mp); +} diff -r 000000000000 -r 32250b436bca jssplr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jssplr.h Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,166 @@ +/* + * miniJSS - Module playing routines + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2007 Tecnic Software productions (TNSP) + */ +#ifndef JSSPLR_H +#define JSSPLR_H + +#include "jss.h" +#include "jssmod.h" +#include "jssmix.h" + + +// Player general constants +#define mpMinVol (0) +#define mpMaxVol (64) +#define mpPanCenter (0) +#define mpMaxFadeoutVol (65535) + + +// Channel New-Data flags +#define cdfNONE (0x00) // Set nothing +#define cdfNewInstr (0x01) // Set a new instrument +#define cdfNewPitch (0x02) // Set a new pitch +#define cdfNewPos (0x04) // Set a new position +#define cdfNewVolume (0x08) // Set a new volume +#define cdfNewPanPos (0x10) // Set a new panning position +#define cdfNewGlobalVol (0x20) // Set a new global volume +#define cdfStop (0x80) // Stop channel playing + + +// Typedefs for channel structures +typedef int JDInt[jsetNChannels]; +typedef BOOL JDBool[jsetNChannels]; +typedef JSSInstrument * JDInst[jsetNChannels]; +typedef JSSExtInstrument * JDEInst[jsetNChannels]; + + +// Struct holding all player related information +typedef struct +{ + // General variables + int iTempo, // Current values + iSpeed, + iTick, + iOrder, + iPattern, + iRow, + iGlobalVol, + iOptions; // Playing option flags + BOOL isPlaying; // Are we playing? + + int iNewOrder, // NEW order number + iNewRow; // NEW row number + BOOL newOrderSet, // TRUE if new order has been set + newRowSet; // TRUE if new row has been set + + // Parameters for effects, etc + BOOL jumpFlag, // Pattern jump flag + breakFlag; // Pattern break flag + int iPatternDelay, // Pattern delay tick-counter + iLastPatLoopRow; // Latest set pattern loop row (any channel) + + JDInt iPatLoopRow, // Pattern loop start row for each channel + iPatLoopCount, // Pattern loop count for each channel + + iLastPortaParam, // Last portamento effect parameter + iLastPortaToNoteParam, // Last porta-to-note parameter + iLastPortaToNotePitch, // Last porta-to-note pitch + + iVibratoPos, // Vibrato waveform position + iVibratoSpeed, // Vibrato speed + iVibratoDepth, // Vibrato depth + iVibratoWC, // Vibrato wave control + + iTremoloPos, // Tremolo waveform position + iTremoloSpeed, // Tremolo speed + iTremoloDepth, // Tremolo depth + + iTremoloWC, // Tremolo wave control + + iLastTremorParam, + iTremorCount, + iLastSampleOffset, + iLastRetrigParam, + iLastVolSlideParam, + + iRetrigNDFlags, // For retrig-effect + iSaveNDFlags; // For notedelay-effect + + // Current channel data + JDInst iCInstrument; // Instruments + JDEInst iCExtInstrument; // ExtInstruments + JDInt iCInstrumentN, + iCExtInstrumentN, + iCNewDataFlags, // New data flags + iCNote, // Current notes + iCPitch, // Pitches (NOT actual frequencies!) + iCOldPitch, + iCPosition, // Sample positions + iCVolume, // Volumes + iCPanning, // Panning positions + + iCFadeOutVol, + + iCPanEnv, + iCVolEnv, + + iCAutoVib_Frames, + iCPanEnv_Frames, + iCVolEnv_Frames, + + iCLastFineVolumeslideUpParam, + iCLastFineVolumeslideDownParam, + iCLastExtraFinePortamentoUpParam, + iCLastExtraFinePortamentoDownParam, + iCLastFinePortamentoUpParam, + iCLastFinePortamentoDownParam; + + JDBool iCPanEnv_Exec, + iCVolEnv_Exec, + iCKeyOff; + + // Module and sounddevice specific + JSSModule *pModule; // Current module in this player + JSSPattern *pPattern; // Current pattern + JSSMixer *pDevice; // Pointer to mixing device structure + +#ifdef JSS_SUP_THREADS + DMMutex *mutex; +#endif + +} JSSPlayer; + + +/* External functions for end users + */ +int jmpPlayOrder(JSSPlayer *, int); +int jmpPlayPattern(JSSPlayer *, int); +void jmpStop(JSSPlayer *); +void jmpResume(JSSPlayer *); +void jmpSetModule(JSSPlayer *, JSSModule *); +JSSPlayer * jmpInit(JSSMixer *); +int jmpClose(JSSPlayer *); +void jmpExec(void *, void *); + + +/* Helper macros + */ +#define JMPMAKEPARAM(AIVAL, AVALX, AVALY) { AVALX = (((AIVAL) >> 4) & 0x0f); AVALY = ((AIVAL) & 0x0f); } +#define JMPSETNDFLAGS(IVAL) mp->iCNewDataFlags[channel] |= IVAL +#define JMPUNSETNDFLAGS(IVAL) mp->iCNewDataFlags[channel] &= (~(IVAL)) +#define JMPGETNOTE(MNOTE, MROW, MCHAN) assert(mp); assert(mp->pPattern); assert((MROW) >= 0); assert((MROW) < mp->pPattern->nrows); MNOTE = &mp->pPattern->data[(mp->pPattern->nchannels * MROW) + (MCHAN)] +#define JMPGETEFFECT(MEFF, MIEFF) if ((MIEFF >= 0) && (MIEFF < jmpNMODEffectTable)) MEFF = jmpMODEffectTable[MIEFF]; else MEFF = 0 +#define JMPGETMODFLAGS(Q, Z) ((Q->pModule->defFlags & (Z)) == (Z)) + + +/* Debugging macros + */ +#if !defined(JSS_LIGHT) && defined(JSS_DEBUG) +# define JMPDEBUG(QQ) { fprintf(stderr, "[o=%03d:p=%03d:r=%03d] (%c/%x:%x) %s\n", mp->iOrder, mp->iPattern, mp->iRow, effect, currNote->effect, currNote->param, QQ ); } +#else +# define JMPDEBUG(QQ) // stub +#endif + +#endif // JSSPLR_H diff -r 000000000000 -r 32250b436bca mod2wav.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod2wav.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,370 @@ +/* + * mod2wav - Render XM/JSSMOD module to WAV waveform file + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2007 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#include +#include +#include +#include +#include "jss.h" +#include "jssmod.h" +#include "jssmix.h" +#include "jssplr.h" +#include "dmlib.h" +#include "dmargs.h" +#include "dmfile.h" + + +#define JSS_WAVE_FORMAT_PCM (1) +#define JSS_WAVE_RIFF_ID "RIFF" +#define JSS_WAVE_WAVE_ID "WAVE" +#define JSS_WAVE_FMT_ID "fmt " +#define JSS_WAVE_DATA_ID "data" + + +typedef struct +{ + Uint8 chunkID[4]; + Uint32 chunkSize; +} JSSWaveChunk; + + +typedef struct +{ + Uint8 riffID[4]; + Uint32 fileSize; + Uint8 riffType[4]; + + JSSWaveChunk chFormat; + + Uint16 wFormatTag; + Uint16 nChannels; + Uint32 nSamplesPerSec; + Uint32 nAvgBytesPerSec; + Uint16 nBlockAlign; + Uint16 wBitsPerSample; + + JSSWaveChunk chData; + // Data follows here +} JSSWaveFile; + + +char *srcFilename = NULL, *destFilename = NULL; +int optOutFormat = JSS_AUDIO_S16, + optOutChannels = 2, + optOutFreq = 44100, + optMuteOChannels = -1, + optStartOrder = -1; +BOOL optUsePlayTime = FALSE; +size_t optPlayTime; + + +DMOptArg optList[] = { + { 0, '?', "help", "Show this help", OPT_NONE }, + { 2, 'v', "verbose", "Be more verbose", OPT_NONE }, + { 3, '1', "16bit", "16-bit output", OPT_NONE }, + { 4, '8', "8bit", "8-bit output", OPT_NONE }, + { 5, 'm', "mono", "Mono output", OPT_NONE }, + { 6, 's', "stereo", "Stereo output", OPT_NONE }, + { 7, 'f', "freq", "Output frequency", OPT_ARGREQ }, + { 8, 'M', "mute", "Mute other channels than #", OPT_ARGREQ }, + { 9, 'o', "order", "Start from order #", OPT_ARGREQ }, + {10, 't', "time", "Play for # seconds", OPT_ARGREQ }, +// {10, 'l', "loop", "Loop for # times", OPT_ARGREQ }, +}; + +const int optListN = sizeof(optList) / sizeof(optList[0]); + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + (void) optArg; + + switch (optN) { + case 0: + dmPrintBanner(stdout, dmProgName, + "[options] [sourcefile] [destfile]"); + + dmArgsPrintHelp(stdout, optList, optListN); + exit(0); + break; + + case 2: + dmVerbosity++; + break; + + case 3: + optOutFormat = JSS_AUDIO_S16; + break; + + case 4: + optOutFormat = JSS_AUDIO_U8; + break; + + case 5: + optOutChannels = JSS_AUDIO_MONO; + break; + + case 6: + optOutFormat = JSS_AUDIO_STEREO; + break; + + case 7: + optOutFreq = atoi(optArg); + break; + + case 8: + optMuteOChannels = atoi(optArg); + break; + + case 9: + optStartOrder = atoi(optArg); + break; + + case 10: + optPlayTime = atoi(optArg); + optUsePlayTime = TRUE; + break; + + default: + dmError("Unknown argument '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL argHandleFile(char *currArg) +{ + // Was not option argument + if (!srcFilename) + srcFilename = currArg; + else if (!destFilename) + destFilename = currArg; + else { + dmError("Too many filename arguments (only source and dest needed) '%s'\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL jssWriteChunk(FILE * f, JSSWaveChunk *ch) +{ + if (!dm_fwrite_str(f, ch->chunkID, 4)) return FALSE; + return dm_fwrite_le32(f, ch->chunkSize); +} + + +void jssMakeChunk(JSSWaveChunk *ch, const char *chunkID, const Uint32 chunkSize) +{ + memcpy(&(ch->chunkID), (const void *) chunkID, 4); + ch->chunkSize = chunkSize; +} + + +void jssWriteWAVHeader(FILE *outFile, int sampBits, int sampFreq, int sampChn, size_t sampLen) +{ + JSSWaveFile wav; + + // PCM WAVE chunk + jssMakeChunk(&wav.chFormat, JSS_WAVE_FMT_ID, (2 + 2 + 4 + 4 + 2 + 2)); + + wav.wFormatTag = JSS_WAVE_FORMAT_PCM; + wav.nChannels = sampChn; + wav.nSamplesPerSec = sampFreq; + wav.nAvgBytesPerSec = (sampBits * sampChn * sampFreq) / 8; + wav.nBlockAlign = (sampBits * sampChn) / 8; + wav.wBitsPerSample = sampBits; + + // Data chunk + jssMakeChunk(&wav.chData, JSS_WAVE_DATA_ID, (sampLen * wav.nBlockAlign)); + + // RIFF header + memcpy(&wav.riffID, (const void *) JSS_WAVE_RIFF_ID, 4); + memcpy(&wav.riffType, (const void *) JSS_WAVE_WAVE_ID, 4); + wav.fileSize = ((4 + 4 + 4) + wav.chFormat.chunkSize + wav.chData.chunkSize); + + // Write header + dm_fwrite_str(outFile, wav.riffID, sizeof(wav.riffID)); + dm_fwrite_le32(outFile, wav.fileSize); + + dm_fwrite_str(outFile, wav.riffType, sizeof(wav.riffType)); + jssWriteChunk(outFile, &wav.chFormat); + + dm_fwrite_le16(outFile, wav.wFormatTag); + dm_fwrite_le16(outFile, wav.nChannels); + dm_fwrite_le32(outFile, wav.nSamplesPerSec); + dm_fwrite_le32(outFile, wav.nAvgBytesPerSec); + dm_fwrite_le16(outFile, wav.nBlockAlign); + dm_fwrite_le16(outFile, wav.wBitsPerSample); + + jssWriteChunk(outFile, &wav.chData); +} + + +int main(int argc, char *argv[]) +{ + FILE *inFile = NULL, *outFile = NULL; + JSSModule *m = NULL; + JSSMixer *d = NULL; + JSSPlayer *p = NULL; + int result = -1; + size_t bufLen = 1024*4, dataTotal, dataWritten, sampSize; + Uint8 *mb = NULL; + + dmInitProg("mod2wav", "XM/JSSMOD to WAV renderer", "0.2", NULL, NULL); + dmVerbosity = 1; + + // Parse arguments + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, argHandleFile, TRUE)) + exit(1); + + // Check arguments + if (!srcFilename || !destFilename) { + dmError("Input or output file not specified!\n"); + return 1; + } + + // Initialize miniJSS + jssInit(); + + // Open the source file + if ((inFile = fopen(srcFilename, "rb")) == NULL) { + dmError("Error opening input file '%s'. (%s)\n", srcFilename, strerror(errno)); + return 2; + } + + // Read module file +#ifdef JSS_SUP_XM + result = jssLoadXM(inFile, &m); +#endif + if (result != 0) { +#ifdef JSS_SUP_JSSMOD + Uint8 *buf; + size_t bufsize; + fseek(inFile, 0L, SEEK_END); + bufsize = ftell(inFile); + fseek(inFile, 0L, SEEK_SET); + buf = dmMalloc(bufsize); + if (fread(buf, 1, bufsize, inFile) != bufsize) { + dmError("Error reading file!\n"); + return 2; + } + result = jssLoadJSSMOD(buf, bufsize, &m); +#endif + if (result != 0) { + dmError("Error loading module file: %d\n", result); + return 3; + } + } + + // Open mixer + d = jvmInit(optOutFormat, optOutChannels, optOutFreq, JMIX_AUTO); + if (!d) { + fprintf(stderr, "jvmInit() returned NULL\n"); + return 4; + } + + sampSize = jvmGetSampleSize(d); + mb = dmMalloc(bufLen * sampSize); + if (!mb) { + fprintf(stderr, "Could not allocate mixing buffer\n"); + return 5; + } + + dmMsg(1, "Using fmt=%d, bits=%d, channels=%d, freq=%d [%d / sample]\n", + optOutFormat, jvmGetSampleRes(d), optOutChannels, optOutFreq, + sampSize); + + // Initialize player + p = jmpInit(d); + if (!p) { + fprintf(stderr, "jmpInit() returned NULL\n"); + return 6; + } + + // Set callback + jvmSetCallback(d, jmpExec, p); + + // Initialize playing + jmpSetModule(p, m); + if (optStartOrder >= 0) { + dmMsg(1, "Starting from song order #%d\n", optStartOrder); + } else + optStartOrder = 0; + jmpPlayOrder(p, optStartOrder); + jvmSetGlobalVol(d, 50); + + if (optMuteOChannels > 0 && optMuteOChannels <= m->nchannels) { + int i; + for (i = 0; i < m->nchannels; i++) + jvmMute(d, i, TRUE); + jvmMute(d, optMuteOChannels - 1, FALSE); + } + + // Open output file + if ((outFile = fopen(destFilename, "wb")) == NULL) { + dmError("Error opening output file '%s'. (%s)\n", srcFilename, strerror(errno)); + return 7; + } + + // Write initial header + jssWriteWAVHeader(outFile, jvmGetSampleRes(d), optOutFreq, optOutChannels, 1024); + + // Render audio data and output to file + if (optUsePlayTime) + dmMsg(1, "Rendering module (%d seconds) ...\n", optPlayTime); + else + dmMsg(1, "Rendering module ...\n"); + + optPlayTime *= optOutFreq; + dataTotal = 0; + dataWritten = 1; + while (p->isPlaying && dataWritten > 0) + { + size_t writeLen = bufLen; + if (optUsePlayTime && (writeLen + dataTotal) > optPlayTime) + writeLen = optPlayTime - dataTotal; + + if (writeLen > 0) + { + jvmRenderAudio(d, mb, writeLen); +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + jssEncodeSample16((Uint16 *)mb, writeLen * optOutChannels, jsampSwapEndianess); +#endif + dataWritten = fwrite(mb, sampSize, writeLen, outFile); + if (dataWritten < writeLen) + { + dmError("Error writing data!\n"); + fclose(outFile); + return 8; + } + dataTotal += dataWritten; + } + + if (optUsePlayTime && dataTotal >= optPlayTime) + break; + } + + // Write the correct header + if (fseek(outFile, 0L, SEEK_SET) != 0) + { + dmError("Error rewinding to header position!\n"); + return 9; + } + + jssWriteWAVHeader(outFile, jvmGetSampleRes(d), optOutFreq, optOutChannels, dataTotal); + + // Done! + fclose(outFile); + jssFreeModule(m); + dmMsg(1, "OK.\n"); + return 0; +} diff -r 000000000000 -r 32250b436bca packed.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/packed.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,451 @@ +/* + * PACKed - PACKfile EDitor + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2011 Tecnic Software productions (TNSP) + */ +#include "dmlib.h" +#include "dmargs.h" +#include "dmpack.h" +#include "dmpackutil.h" +#include + +#define SET_MAX_FILES (4096) +#define SET_DEFAULT_PACK "data.pak" + +enum +{ + CMD_NONE = 0, + CMD_CREATE, + CMD_ADD, + CMD_LIST, + CMD_EXTRACT +} DCOMMAND; + +enum +{ + PACK_EXTRACTED = 0x0001, +}; + +int nsrcFilenames = 0; +char * srcFilenames[SET_MAX_FILES]; +char * optPackFilename = NULL; +BOOL optCompress = TRUE; +int optCommand = CMD_NONE; +int optDefResFlags = 0; + + +static DMOptArg optList[] = +{ + { 0, '?', "help", "Show this help", OPT_NONE }, + { 1, 'p', "pack", "Set pack filename (default: " SET_DEFAULT_PACK ")", OPT_ARGREQ }, + { 2, 'c', "create", "Create and add files to PACK", OPT_NONE }, + { 3, 'a', "add", "Add files to PACK", OPT_NONE }, + { 4, 'l', "list", "List files in PACK", OPT_NONE }, + { 5, 'e', "extract", "Extract files from PACK", OPT_NONE }, + { 6, 'n', "nocompress", "No compression", OPT_NONE }, + { 7, 'v', "verbose", "Increase verbosity", OPT_NONE }, + { 8, 'f', "resflags", "Set default resource flags (-f 0xff)", OPT_ARGREQ }, +}; + +static const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void argShowHelp() +{ + dmPrintBanner(stdout, dmProgName, "[options] [filename[s]]"); + dmArgsPrintHelp(stdout, optList, optListN); + fprintf(stdout, + "\n" + "Examples:\n" + "$ %s -p test.pak -l -- list files in test.pak\n" + "$ %s -a foobar.jpg -- add foobar.jpg in " SET_DEFAULT_PACK "\n" + "$ %s -x foobar.jpg -- extract foobar.jpg from " SET_DEFAULT_PACK "\n", + dmProgName, dmProgName, dmProgName); +} + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + (void) optArg; + switch (optN) + { + case 0: + argShowHelp(); + exit(0); + break; + + case 1: + optPackFilename = optArg; + break; + case 2: + optCommand = CMD_CREATE; + break; + case 3: + optCommand = CMD_ADD; + break; + case 4: + optCommand = CMD_LIST; + break; + case 5: + optCommand = CMD_EXTRACT; + break; + + case 6: + optCompress = FALSE; + break; + + case 7: + dmVerbosity++; + break; + + case 8: + { + int i; + if (!dmGetIntVal(optArg, &i)) + { + dmError("Invalid flags value '%s'.\n", optArg); + return FALSE; + } + optDefResFlags = i; + } + break; + + default: + dmError("Unknown argument '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL argHandleFile(char *currArg) +{ + if (nsrcFilenames < SET_MAX_FILES) + { + srcFilenames[nsrcFilenames] = currArg; + nsrcFilenames++; + } + else + { + dmError("Maximum number of input files (%i) exceeded!\n", + SET_MAX_FILES); + return FALSE; + } + return TRUE; +} + + +/* Compare a string to a pattern. Case-SENSITIVE version. + * The matching pattern can consist of any normal characters plus + * wildcards ? and *. "?" matches any character and "*" matches + * any number of characters. + */ +BOOL dm_strmatch(const char *str, const char *pattern) +{ + BOOL didMatch = TRUE, isAnyMode = FALSE, isEnd = FALSE; + const char *tmpPattern = NULL; + + // Check given pattern and string + if (str == NULL || pattern == NULL) + return FALSE; + + // Start comparision + do { + didMatch = FALSE; + switch (*pattern) + { + case '?': + // Any single character matches + if (*str) + { + didMatch = TRUE; + pattern++; + str++; + } + break; + + case '*': + didMatch = TRUE; + pattern++; + if (!*pattern) + isEnd = TRUE; + isAnyMode = TRUE; + tmpPattern = pattern; + break; + + case 0: + if (isAnyMode) + { + if (*str) + str++; + else + isEnd = TRUE; + } + else + { + if (*str) + { + if (tmpPattern) + { + isAnyMode = TRUE; + pattern = tmpPattern; + } + else + didMatch = FALSE; + } + else + isEnd = TRUE; + } + break; + default: + if (isAnyMode) + { + if (*pattern == *str) + { + isAnyMode = FALSE; + didMatch = TRUE; + } + else + { + if (*str) + { + didMatch = TRUE; + str++; + } + } + } + else + { + if (*pattern == *str) + { + didMatch = TRUE; + if (*pattern) + pattern++; + if (*str) + str++; + } + else + { + if (tmpPattern) + { + didMatch = TRUE; + isAnyMode = TRUE; + pattern = tmpPattern; + } + } + } + + if (!*str && !*pattern) + isEnd = TRUE; + break; + + } // switch + + } while (didMatch && !isEnd); + + return didMatch; +} + + +int dmAddFileToPack(DMPackFile *pack, const char *filename, int compression, int resFlags) +{ + DMPackEntry *node; + int res = dm_pack_add_file(pack, filename, compression, resFlags, &node); + + if (res != DMERR_OK) + { + dmPrint(1, "%-32s [ERROR:%i]\n", + filename, res); + } + else + { + dmPrint(1, "%-32s ['%s', s=%d, c=%d, o=%ld, f=0x%04x]\n", + filename, node->filename, + node->size, node->length, node->offset, + node->resFlags); + } + + return res; +} + + +int main(int argc, char *argv[]) +{ + int i, res = 0; + DMPackFile *pack = NULL; + + stderr = stdout; + + // Parse arguments + dmInitProg("packed", "Pack File Editor", "0.4", NULL, NULL); + dmVerbosity = 1; + + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, argHandleFile, TRUE)) + exit(1); + + // Check PACK filename + if (optPackFilename == NULL) + optPackFilename = SET_DEFAULT_PACK; + + if (optCommand == CMD_NONE) + { + argShowHelp(); + dmError("Nothing to do.\n"); + exit(0); + return 0; + } + + dmMsg(1, "Processing %s ...\n", optPackFilename); + + // Execute command + switch (optCommand) + { + case CMD_CREATE: + case CMD_ADD: + switch (optCommand) + { + case CMD_CREATE: + dmMsg(1, "Creating new PACK\n"); + res = dm_pack_create(optPackFilename, &pack); + break; + + case CMD_ADD: + dmMsg(1, "Opening existing PACK\n"); + res = dm_pack_open(optPackFilename, &pack, FALSE); + break; + } + + // Add files into PACK + if (res == DMERR_OK) + { + dmMsg(1, "Adding %d files...\n", nsrcFilenames); + + for (i = 0; i < nsrcFilenames; i++) + { + // Handle resource definition files + if (srcFilenames[i][0] == '@') + { + } + else + { + dmAddFileToPack(pack, srcFilenames[i], optCompress, optDefResFlags); + } + } + + dmMsg(1, "w=%i\n", dm_pack_write(pack)); + dmMsg(1, "c=%i\n", dm_pack_close(pack)); + } + else + { + dmError("Could not open packfile, error #%i: %s\n", res, + dmErrorStr(res)); + } + break; + + case CMD_LIST: + // List files in PACK + res = dm_pack_open(optPackFilename, &pack, TRUE); + if (res == DMERR_OK) + { + DMPackEntry *node; + for (i = 0, node = pack->entries; node; i++) + node = node->next; + dmMsg(1, "%d files total\n", i); + + for (node = pack->entries; node != NULL; node = node->next) + { + BOOL match; + + // Check for matches + if (nsrcFilenames > 0) + { + match = FALSE; + for (i = 0; i < nsrcFilenames && !match; i++) + { + match = dm_strmatch(node->filename, srcFilenames[i]); + } + } + else + match = TRUE; + + if (match) + { + // Print one entry + dmPrint(0, "Extracting: %-32s [siz=%d, cmp=%d, offs=0x%08x, flags=0x%04x]\n", + node->filename, node->size, node->length, + node->offset, node->resFlags); + } + } + + dmMsg(1, "c=%i\n", dm_pack_close(pack)); + } + else + dmError("Could not open packfile, error #%i: %s\n", res, + dmErrorStr(res)); + break; + + case CMD_EXTRACT: + // Extract files from PACK + res = dm_pack_open(optPackFilename, &pack, TRUE); + if (res == DMERR_OK) + { + DMPackEntry *node; + FILE *resFile = fopen(DMRES_RES_FILE, "w"); + if (resFile == NULL) + { + dmError("Could not create resource output file '%s' #%i: %s\n", + DMRES_RES_FILE, errno, strerror(errno)); + } + + for (node = pack->entries; node != NULL; node = node->next) + { + BOOL match; + + // Check for matches + if (nsrcFilenames > 0) + { + match = FALSE; + for (i = 0; (i < nsrcFilenames) && !match; i++) + { + match = dm_strmatch(node->filename, srcFilenames[i]); + } + } + else + match = TRUE; + + if (match && (node->privFlags & PACK_EXTRACTED) == 0) + { + // Mark as done + node->privFlags |= PACK_EXTRACTED; + + // Print one entry + dmPrint(0, "Extracting: %-32s [siz=%d, cmp=%d, offs=0x%08x, flags=0x%04x]\n", + node->filename, node->size, node->length, + node->offset, node->resFlags); + + dm_pack_extract_file(pack, node); + + if (resFile != NULL) + { + fprintf(resFile, + "%s|%04x", node->filename, node->resFlags); + } + } + } + + dmMsg(1, "c=%i\n", dm_pack_close(pack)); + + if (resFile != NULL) + fclose(resFile); + } + else + dmError("Could not open packfile, error #%i: %s\n", res, + dmErrorStr(res)); + break; + + } + + return 0; +} diff -r 000000000000 -r 32250b436bca stb_image.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stb_image.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,4691 @@ +/* stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + no warranty implied; use at your own risk + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive) + PNG 8-bit only + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + Latest revisions: + 1.33 (2011-07-14) minor fixes suggested by Dave Moore + 1.32 (2011-07-13) info support for all filetypes (SpartanJ) + 1.31 (2011-06-19) a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) added ability to load files via io callbacks (Ben Wenger) + 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) cast-to-uint8 to fix warnings (Laurent Gomila) + allow trailing 0s at end of image data (Laurent Gomila) + 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ + + See end of file for full revision history. + + TODO: + stbi_info support for BMP,PSD,HDR,PIC + + + ============================ Contributors ========================= + + Image formats Optimizations & bugfixes + Sean Barrett (jpeg, png, bmp) Fabian "ryg" Giesen + Nicolas Schulz (hdr, psd) + Jonathan Dummer (tga) Bug fixes & warning fixes + Jean-Marc Lienher (gif) Marc LeBlanc + Tom Seddon (pic) Christpher Lloyd + Thatcher Ulrich (psd) Dave Moore + Won Chun + the Horde3D community + Extensions, features Janez Zemva + Jetro Lauha (stbi_info) Jonathan Blow + James "moose2000" Brown (iPhone PNG) Laurent Gomila + Ben "Disch" Wenger (io callbacks) Aruelien Pocheville + Martin "SpartanJ" Golini Ryamond Barbiero + David Woo + + + If your name should be here but isn't, let Sean know. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// To get a header file for this, either cut and paste the header, +// or create stb_image.h, #define STBI_HEADER_FILE_ONLY, and +// then include stb_image.c from it. + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no jpeg progressive support +// - non-HDR formats support 8-bit samples only (jpeg, png) +// - no delayed line count (jpeg) -- IJG doesn't support either +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB; nominally they +// would silently load as BGR, except the existing code should have just +// failed on such iPhone PNGs. But you can disable this conversion by +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through. +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). + + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && _MSC_VER >= 0x1400 +#define _CRT_SECURE_NO_WARNINGS // suppress bogus warnings about fopen() +#endif + +#include +#endif + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,unsigned n); // skip the next 'n' bytes + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_HDR + extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif + + extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + extern void stbi_hdr_to_ldr_gamma(float gamma); + extern void stbi_hdr_to_ldr_scale(float scale); + + extern void stbi_ldr_to_hdr_gamma(float gamma); + extern void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +extern const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +// define faster low-level operations (typically SIMD support) +#ifdef STBI_SIMD +typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifndef STBI_HEADER_FILE_ONLY + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp, strtok +#endif + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4 ? 1 : -1]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +#define STBI_NOTUSED(v) (void)sizeof(v) + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +/////////////////////////////////////////////// +// +// stbi struct and start_xxx functions + +// stbi structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + uint8 buffer_start[128]; + + uint8 *img_buffer, *img_buffer_end; + uint8 *img_buffer_original; +} stbi; + + +static void refill_buffer(stbi *s); + +// initialize a memory-decode context +static void start_mem(stbi *s, uint8 const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (uint8 *) buffer; + s->img_buffer_end = (uint8 *) buffer+len; +} + +// initialize a callback-based context +static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + refill_buffer(s); +} + +#ifndef STBI_NO_STDIO + +static int stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stdio_skip(void *user, unsigned n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi_stdio_callbacks = +{ + stdio_read, + stdio_skip, + stdio_eof, +}; + +static void start_file(stbi *s, FILE *f) +{ + start_callbacks(s, &stbi_stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi_rewind(stbi *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; +} + +static int stbi_jpeg_test(stbi *s); +static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp); +static int stbi_png_test(stbi *s); +static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_png_info(stbi *s, int *x, int *y, int *comp); +#ifdef STBI_CRAP_FORMATS +static int stbi_bmp_test(stbi *s); +static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_test(stbi *s); +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); +static int stbi_psd_test(stbi *s); +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_pic_test(stbi *s); +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_test(stbi *s); +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi_hdr_test(stbi *s); +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); +#endif + +// this is not threadsafe +static const char *failure_reason; + +const char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(const char *str) +{ + failure_reason = str; + return 0; +} + +// e - error +// epf - error returning pointer to float +// epuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp); + if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp); +#ifdef STBI_CRAP_FORMATS + if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp); + if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp); + if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp); + if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp); +#endif + + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) { + float *hdr = stbi_hdr_load(s, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + +#ifdef STBI_CRAP_FORMATS + // test tga last because it's a crappy test! + if (stbi_tga_test(s)) + return stbi_tga_load(s,x,y,comp,req_comp); +#endif + + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_load_main(&s,x,y,comp,req_comp); +} +#endif //!STBI_NO_STDIO + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_HDR + +float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) + return stbi_hdr_load(s,x,y,comp,req_comp); + #endif + data = stbi_load_main(s, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_HDR + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi s; + start_mem(&s,buffer,len); + return stbi_hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi s; + start_file(&s,f); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header +}; + +static void refill_buffer(stbi *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_end-1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static int get8(stbi *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int at_eof(stbi *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +stbi_inline static uint8 get8u(stbi *s) +{ + return (uint8) get8(s); +} + +static void skip(stbi *s, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int getn(stbi *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int get16(stbi *s) +{ + int z = get8(s); + return (z << 8) + get8(s); +} + +static uint32 get32(stbi *s) +{ + uint32 z = get16(s); + return (z << 16) + get16(s); +} + +static int get16le(stbi *s) +{ + int z = get8(s); + return z + (get8(s) << 8); +} + +static uint32 get32le(stbi *s) +{ + uint32 z = get16le(s); + return z + (get16le(s) << 16); +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +typedef struct +{ + #ifdef STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi *s; + huffman huff_dc[4]; + huffman huff_ac[4]; + uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; + } img_comp[4]; + + uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +static void grow_buffer_unsafe(jpeg *j) +{ + do { + int b = j->nomore ? 0 : get8(j->s); + if (b == 0xff) { + int c = get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int decode(jpeg *j, huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int extend_receive(jpeg *j, int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (j->code_bits < n) grow_buffer_unsafe(j); + + #if 1 + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~bmask[n]; + k &= bmask[n]; + j->code_bits -= n; + #else + k = (j->code_buffer >> (32 - n)) & bmask[n]; + j->code_bits -= n; + j->code_buffer <<= n; + #endif + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(j, hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(j, hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(j,s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +stbi_inline static uint8 clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#ifdef STBI_SIMD +typedef unsigned short stbi_dequantize_t; +#else +typedef uint8 stbi_dequantize_t; +#endif + +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) +{ + int i,val[64],*v=val; + stbi_dequantize_t *dq = dequantize; + uint8 *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SIMD +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(jpeg *j) +{ + uint8 x; + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(j->s); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(jpeg *z) +{ + reset(z); + if (z->scan_n == 1) { + int i,j; + #ifdef STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } + return 1; +} + +static int process_marker(jpeg *z, int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16(z->s)-2; + while (L > 0) { + int q = get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][dezigzag[i]] = get8u(z->s); + #ifdef STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = z->dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16(z->s)-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int q = get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(z->s); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(z->s); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(z->s, get16(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(jpeg *z) +{ + int i; + int Ls = get16(z->s); + z->scan_n = get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(z->s), which; + int q = get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(z->s); // should be 63, but might be 0 + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(jpeg *z, int scan) +{ + stbi *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(jpeg *z, int scan) +{ + int m; + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(z); + while (!SOF(m)) { + if (!process_marker(z,m)) return 0; + m = get_marker(z); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); + } + } + if (!process_frame_header(z, scan)) return 0; + return 1; +} + +static int decode_jpeg_image(jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; + if (j->marker == MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!at_eof(j->s)) { + int x = get8(j->s); + if (x == 255) { + j->marker = get8u(j->s); + break; + } else if (x != 0) { + return 0; + } + } + // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!process_marker(j, m)) return 0; + } + m = get_marker(j); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + in_far = in_far; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#ifdef STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s->img_n = 0; + + // load a jpeg image from whichever source + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (uint8 *) malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + uint8 *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (z->s->img_n == 3) { + #ifdef STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); + #endif + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + j.s = s; + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi_jpeg_test(stbi *s) +{ + int r; + jpeg j; + j.s = s; + r = decode_jpeg_header(&j, SCAN_type); + stbi_rewind(s); + return r; +} + +static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) +{ + if (!decode_jpeg_header(j, SCAN_header)) { + stbi_rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp) +{ + jpeg j; + j.s = s; + return stbi_jpeg_info_raw(&j, x, y, comp); +} + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +stbi_inline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + uint8 *zbuffer, *zbuffer_end; + int num_bits; + uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +stbi_inline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void fill_bits(zbuf *z) +{ + do { + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int zreceive(zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z) +{ + int b,s,k; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(a->code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int expand(zbuf *z, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(z->zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(zbuf *a) +{ + for(;;) { + int z = zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (uint8 *) (a->zout - dist); + while (len--) + *a->zout++ = *p++; + } + } +} + +static int compute_huffman_codes(zbuf *a) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + zhuffman z_codelength; + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(a, &z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(zbuf *a) +{ + uint8 header[4]; + int len,nlen,k; + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; + } + assert(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int parse_zlib_header(zbuf *a) +{ + int cmf = zget8(a); + int cm = cmf & 15; + // int cinfo = cmf >> 4; + int flg = zget8(a); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead +static int parse_zlib(zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = zreceive(a,1); + type = zreceive(a,2); + if (type == 0) { + if (!parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes(a)) return 0; + } + if (!parse_huffman_block(a)) return 0; + } + if (stbi_png_partial && a->zout - a->zout_start > 65536) + break; + } while (!final); + return 1; +} + +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(stbi *s) +{ + chunk c; + c.length = get32(s); + c.type = get32(s); + return c; +} + +static int check_png_header(stbi *s) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi *s; + uint8 *idata, *expanded, *out; +} png; + + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y) +{ + stbi *s = a->s; + uint32 i,j,stride = x*out_n; + int k; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + if (stbi_png_partial) y = 1; + a->out = (uint8 *) malloc(x * y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (!stbi_png_partial) { + if (s->img_x == x && s->img_y == y) { + if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } + } + for (j=0; j < y; ++j) { + uint8 *cur = a->out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch (filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int interlaced) +{ + uint8 *final; + int p; + int save; + if (!interlaced) + return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y); + save = stbi_png_partial; + stbi_png_partial = 0; + + // de-interlacing + final = (uint8 *) malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { + free(final); + return 0; + } + for (j=0; j < y; ++j) + for (i=0; i < x; ++i) + memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, + a->out + (j*x+i)*out_n, out_n); + free(a->out); + raw += (x*out_n+1)*y; + raw_len -= (x*out_n+1)*y; + } + } + a->out = final; + + stbi_png_partial = save; + return 1; +} + +static int compute_transparency(png *z, uint8 tc[3], int out_n) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = a->s->img_x * a->s->img_y; + uint8 *p, *temp_out, *orig = a->out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi_unpremultiply_on_load = 0; +static int stbi_de_iphone_flag = 0; + +void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; +} +void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi_de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi_de_iphone(png *z) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + assert(s->img_out_n == 4); + if (stbi_unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + uint8 a = p[3]; + uint8 t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +static int parse_png_file(png *z, int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, iphone=0; + stbi *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!check_png_header(s)) return 0; + + if (scan == SCAN_type) return 1; + + for (;;) { + chunk c = get_chunk_header(s); + switch (c.type) { + case PNG_TYPE('C','g','B','I'): + iphone = stbi_de_iphone_flag; + skip(s, c.length); + break; + case PNG_TYPE('I','H','D','R'): { + int depth,color,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(s); + } else { + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (uint8) get16(s); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; + } + if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (first) return e("first not IHDR", "Corrupt PNG"); + if (scan != SCAN_load) return 1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; + if (has_trans) + if (!compute_transparency(z, tc, s->img_out_n)) return 0; + if (iphone && s->img_out_n > 2) + stbi_de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + free(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return e("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(s, c.length); + break; + } + // end of chunk, read and skip CRC + get32(s); + } +} + +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + png p; + p.s = s; + return do_png(&p, x,y,comp,req_comp); +} + +static int stbi_png_test(stbi *s) +{ + int r; + r = check_png_header(s); + stbi_rewind(s); + return r; +} + +static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) +{ + if (!parse_png_file(p, SCAN_header, 0)) { + stbi_rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi_png_info(stbi *s, int *x, int *y, int *comp) +{ + png p; + p.s = s; + return stbi_png_info_raw(&p, x, y, comp); +} + +#ifdef STBI_CRAP_FORMATS + +// Microsoft/Windows BMP image + +static int bmp_test(stbi *s) +{ + int sz; + if (get8(s) != 'B') return 0; + if (get8(s) != 'M') return 0; + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + get32le(s); // discard data offset + sz = get32le(s); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +static int stbi_bmp_test(stbi *s) +{ + int r = bmp_test(s); + stbi_rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + offset = get32le(s); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = get16le(s); + s->img_y = get16le(s); + } else { + s->img_x = get32le(s); + s->img_y = get32le(s); + } + if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); + bpp = get16le(s); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(s); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(s); // discard sizeof + get32le(s); // discard hres + get32le(s); // discard vres + get32le(s); // discard colorsused + get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(s); + get32le(s); + get32le(s); + get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return epuc("bad BMP", "bad BMP"); + } + } else + return epuc("bad BMP", "bad BMP"); + } + } else { + assert(hsz == 108); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space + for (i=0; i < 12; ++i) + get32le(s); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * s->img_x * s->img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + if (hsz != 12) get8(s); + pal[i][3] = 255; + } + skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { free(out); return epuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + int a; + out[z+2] = get8u(s); + out[z+1] = get8u(s); + out[z+0] = get8u(s); + z += 3; + a = (easy == 2 ? get8(s) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); + int a; + out[z++] = (uint8) shiftsigned(v & mr, rshift, rcount); + out[z++] = (uint8) shiftsigned(v & mg, gshift, gcount); + out[z++] = (uint8) shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } + skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} + +static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp) +{ + return bmp_load(s, x,y,comp,req_comp); +} + + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_info(stbi *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if( sz > 1 ) { + stbi_rewind(s); + return 0; // only RGB or indexed allowed + } + sz = get8u(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + skip(s,9); + tga_w = get16le(s); + if( tga_w < 1 ) { + stbi_rewind(s); + return 0; // test width + } + tga_h = get16le(s); + if( tga_h < 1 ) { + stbi_rewind(s); + return 0; // test height + } + sz = get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi_rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +int stbi_tga_info(stbi *s, int *x, int *y, int *comp) +{ + return tga_info(s, x, y, comp); +} + +static int tga_test(stbi *s) +{ + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(s); // discard palette start + get16(s); // discard palette length + get8(s); // discard bits per palette color entry + get16(s); // discard x origin + get16(s); // discard y origin + if ( get16(s) < 1 ) return 0; // test width + if ( get16(s) < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +static int stbi_tga_test(stbi *s) +{ + int res = tga_test(s); + stbi_rewind(s); + return res; +} + +static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(s); + int tga_indexed = get8u(s); + int tga_image_type = get8u(s); + int tga_is_RLE = 0; + int tga_palette_start = get16le(s); + int tga_palette_len = get16le(s); + int tga_palette_bits = get8u(s); + int tga_x_origin = get16le(s); + int tga_y_origin = get16le(s); + int tga_width = get16le(s); + int tga_height = get16le(s); + int tga_bits_per_pixel = get8u(s); + int tga_inverted = get8u(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + // int tga_alpha_bits = tga_inverted & 15; + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if ( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + if (!tga_data) return epuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + skip(s, tga_offset ); + // do I need to load a palette? + if ( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) return epuc("outofmem", "Out of memory"); + if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + free(tga_data); + free(tga_palette); + return epuc("bad palette", "Corrupt TGA"); + } + } + // load the data + trans_data[0] = trans_data[1] = trans_data[2] = trans_data[3] = 0; + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = get8u(s); + } + } + // convert raw to the intermediate format + switch (tga_bits_per_pixel) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch (req_comp) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for (i = tga_width * req_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return tga_load(s,x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +static int stbi_psd_test(stbi *s) +{ + int r = psd_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8u(s); + p += 4; + len--; + } + } else if (len > 128) { + uint8 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8u(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = get8u(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return psd_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +static int pic_is4(stbi *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int pic_test(stbi *s) +{ + int i; + + if (!pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + get8(s); + + if (!pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} pic_packet_t; + +static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (at_eof(s)) return epuc("bad file","PIC file too short"); + dest[i]=get8u(s); + } + } + + return dest; +} + +static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + pic_packet_t packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return epuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + + act_comp |= packet->channel; + + if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return epuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=get8u(s); + if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (uint8) left; + + if (!pic_readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = get8(s), i; + if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = get16(s); + else + count -= 127; + if (count > left) + return epuc("bad file","scanline overrun"); + + if (!pic_readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return epuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + get8(s); + + x = get16(s); + y = get16(s); + if (at_eof(s)) return epuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); + + get32(s); //skip `ratio' + get16(s); //skip `fields' + get16(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!pic_load2(s,x,y,comp, result)) { + free(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi_pic_test(stbi *s) +{ + int r = pic_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return pic_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb +typedef struct stbi_gif_lzw_struct { + int16 prefix; + uint8 first; + uint8 suffix; +} stbi_gif_lzw; + +typedef struct stbi_gif_struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + uint8 pal[256][4]; + uint8 lpal[256][4]; + stbi_gif_lzw codes[4096]; + uint8 *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi_gif; + +static int gif_test(stbi *s) +{ + int sz; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; + sz = get8(s); + if (sz != '9' && sz != '7') return 0; + if (get8(s) != 'a') return 0; + return 1; +} + +static int stbi_gif_test(stbi *s) +{ + int r = gif_test(s); + stbi_rewind(s); + return r; +} + +static void stbi_gif_parse_colortable(stbi *s, uint8 pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + pal[i][3] = transp ? 0 : 255; + } +} + +static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) +{ + uint8 version; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') + return e("not GIF", "Corrupt GIF"); + + version = get8u(s); + if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); + if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); + + failure_reason = ""; + g->w = get16le(s); + g->h = get16le(s); + g->flags = get8(s); + g->bgindex = get8(s); + g->ratio = get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) +{ + stbi_gif g; + if (!stbi_gif_header(s, &g, comp, 1)) { + stbi_rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi_out_gif_code(stbi_gif *g, uint16 code) +{ + uint8 *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi_out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) +{ + uint8 lzw_cs; + int32 len, code; + uint32 first; + int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi_gif_lzw *p; + + lzw_cs = get8u(s); + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (uint8) code; + g->codes[code].suffix = (uint8) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (int32) get8(s) << valid_bits; + valid_bits += 8; + } else { + int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + skip(s, len); + while ((len = get8(s)) > 0) + skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return epuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); + p->prefix = (int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return epuc("illegal code in raster", "Corrupt GIF"); + + stbi_out_gif_code(g, (uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return epuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi_fill_gif_background(stbi_gif *g) +{ + int i; + uint8 *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + uint8 *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) +{ + int i; + uint8 *old_out = 0; + + if (g->out == 0) { + if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + stbi_fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (get8(s)) { + case 0x2C: // Image Descriptor + { + int32 x, y, w, h; + uint8 *o; + + x = get16le(s); + y = get16le(s); + w = get16le(s); + h = get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return epuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (uint8 *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (uint8 *) g->pal; + } else + return epuc("missing color table", "Corrupt GIF"); + + o = stbi_process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (get8(s) == 0xF9) { // Graphic Control Extension. + len = get8(s); + if (len == 4) { + g->eflags = get8(s); + get16le(s); // delay + g->transparent = get8(s); + } else { + skip(s, len); + break; + } + } + while ((len = get8(s)) != 0) + skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (uint8 *) 1; + + default: + return epuc("unknown code", "Corrupt GIF"); + } + } +} + +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *u = 0; + stbi_gif g={0}; + + u = stbi_gif_load_next(s, &g, comp, req_comp); + if (u == (void *) 1) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} + +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp) +{ + return stbi_gif_info_raw(s,x,y,comp); +} + +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(stbi *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi_hdr_test(stbi* s) +{ + int r = hdr_test(s); + stbi_rewind(s); + return r; +} + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(stbi *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) get8(z); + + while (!at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof(z) && get8(z) != '\n') + ; + break; + } + c = (char) get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; // fallthrough + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; // fallthrough + case 1: output[0] = 0; + break; + } + } +} + +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(s, rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + uint8 rgbe[4]; + rgbe[0] = (uint8) c1; + rgbe[1] = (uint8) c2; + rgbe[2] = (uint8) len; + rgbe[3] = (uint8) get8u(s); + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= get8(s); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8u(s); + if (count > 128) { + // Run + value = get8u(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8u(s); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return hdr_load(s,x,y,comp,req_comp); +} + +static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi_rewind( s ); + return 0; + } + + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi_rewind( s ); + return 0; + } + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *y = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *x = strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifdef STBI_CRAP_FORMATS + +static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp) +{ + int hsz; + if (get8(s) != 'B' || get8(s) != 'M') { + stbi_rewind( s ); + return 0; + } + skip(s,12); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) { + stbi_rewind( s ); + return 0; + } + if (hsz == 12) { + *x = get16le(s); + *y = get16le(s); + } else { + *x = get32le(s); + *y = get32le(s); + } + if (get16le(s) != 1) { + stbi_rewind( s ); + return 0; + } + *comp = get16le(s) / 8; + return 1; +} + +static int stbi_psd_info(stbi *s, int *x, int *y, int *comp) +{ + int channelCount; + if (get32(s) != 0x38425053) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 1) { + stbi_rewind( s ); + return 0; + } + skip(s, 6); + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) { + stbi_rewind( s ); + return 0; + } + *y = get32(s); + *x = get32(s); + if (get16(s) != 8) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 3) { + stbi_rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi_pic_info(stbi *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + pic_packet_t packets[10]; + + skip(s, 92); + + *x = get16(s); + *y = get16(s); + if (at_eof(s)) return 0; + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi_rewind( s ); + return 0; + } + + skip(s, 8); + + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + act_comp |= packet->channel; + + if (at_eof(s)) { + stbi_rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi_rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + + +static int stbi_info_main(stbi *s, int *x, int *y, int *comp) +{ + if (stbi_jpeg_info(s, x, y, comp)) + return 1; + if (stbi_png_info(s, x, y, comp)) + return 1; +#ifdef STBI_CRAP_FORMATS + if (stbi_gif_info(s, x, y, comp)) + return 1; + if (stbi_bmp_info(s, x, y, comp)) + return 1; + if (stbi_psd_info(s, x, y, comp)) + return 1; + if (stbi_pic_info(s, x, y, comp)) + return 1; + #ifndef STBI_NO_HDR + if (stbi_hdr_info(s, x, y, comp)) + return 1; + #endif + // test tga last because it's a crappy test! + if (stbi_tga_info(s, x, y, comp)) + return 1; +#endif + return e("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = fopen(filename, "rb"); + int result; + if (!f) return e("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi s; + long pos = ftell(f); + start_file(&s, f); + r = stbi_info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_info_main(&s,x,y,comp); +} + +int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi_info_main(&s,x,y,comp); +} + +#endif // STBI_HEADER_FILE_ONLY + +/* + revision history: + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-uint8 to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) + 1.21 fix use of 'uint8' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 2008-08-02 + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 first released version +*/ diff -r 000000000000 -r 32250b436bca testfs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testfs.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,62 @@ +#include "d_fs.h" +#include +#include + +void dream_error(const char *pFmt, ...) +{ + va_list myAP; + fprintf(stderr, "testfs: "); + va_start(myAP, pFmt); + vfprintf(stderr, pFmt, myAP); + va_end(myAP); + fprintf(stderr, "\n"); +} + +int main(void) +{ + DINT iResult; + t_d_file *f; + + /* Initialize dreamFS + */ + iResult = dream_fs_init(DTRUE, DFALSE); + fprintf(stderr, "dream_fs_init(): %i\n", iResult); + if (iResult != DREAM_ERR_OK) return 1; + + /* File reading test, output "README" to stdout + */ + f = dfopen("README"); + if (!f) + fprintf(stderr, "dfopen() failed\n"); + else + { + fprintf(stderr, "dfopen(%s) ok, fops = %p\n", f->pcFilename, f->fops); + dfclose(f); + } + + + + /* Try opening a file which is not in the PACK + */ + f = dfopen("jeejee.jpg"); + if (!f) + fprintf(stderr, "dfopen() failed\n"); + else + { + fprintf(stderr, "dfopen(%s) ok, fops = %p\n", f->pcFilename, f->fops); + dfclose(f); + } + + f = dfopen("jeejee.jpgz"); + if (!f) + fprintf(stderr, "dfopen() failed (as expected)\n"); + else + { + fprintf(stderr, "dfopen(%s) ok, fops = %p\n", f->pcFilename, f->fops); + dfclose(f); + } + + fprintf(stderr, "dream_fs_close()\n"); dream_fs_close(); + + return 0; +} diff -r 000000000000 -r 32250b436bca testpl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testpl.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,257 @@ +#include "jss.h" +#include "jssmod.h" +#include "jssmix.h" +#include "jssplr.h" +#include +#include +#include +#include + + +static const char patNoteTable[12][3] = +{ + "C-", "C#", "D-", + "D#", "E-", "F-", + "F#", "G-", "G#", + "A-", "A#", "B-" +}; + + +#define jmpNMODEffectTable (36) +static const char jmpMODEffectTable[jmpNMODEffectTable] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + +void printRow(FILE * f, JSSPattern * p, int row) +{ + int j, k; + char c; + JSSNote *n; + + if (!p) + return; + + n = &(p->data[p->nchannels * row]); + + fprintf(f, "%.2x: ", row); + + k = p->nchannels < 5 ? p->nchannels : 5; + + for (j = 0; j < k; j++) + { + switch (n->note) + { + case jsetNotSet: + fprintf(f, "... "); + break; + case jsetNoteOff: + fprintf(f, "=== "); + break; + default: + fprintf(f, "%s%i ", patNoteTable[n->note % 12], n->note / 12); + break; + } + + if (n->instrument != jsetNotSet) + fprintf(f, "%.2x ", n->instrument + 1); + else + fprintf(f, ".. "); + + if (n->volume == jsetNotSet) + fprintf(f, ".. "); + else if (n->volume >= 0x00 && n->volume <= 0x40) + fprintf(f, "%.2x ", n->volume); + else + { + switch (n->volume & 0xf0) + { + case 0x50: c = '-'; break; + case 0x60: c = '+'; break; + case 0x70: c = '/'; break; + case 0x80: c = '\\'; break; + case 0x90: c = 'S'; break; + case 0xa0: c = 'V'; break; + case 0xb0: c = 'P'; break; + case 0xc0: c = '<'; break; + case 0xd0: c = '>'; break; + case 0xe0: c = 'M'; break; + default: c = '?'; break; + } + fprintf(f, "%c%x ", c, (n->volume & 0x0f)); + } + + if (n->effect >= 0 && n->effect < jmpNMODEffectTable) + fprintf(f, "%c", jmpMODEffectTable[n->effect]); + else if (n->effect == jsetNotSet) + fprintf(f, "."); + else + fprintf(f, "?"); + + if (n->param != jsetNotSet) + fprintf(f, "%.2x|", n->param); + else + fprintf(f, "..|"); + + n++; + } +} + + +void audioCallback(void *userdata, Uint8 *stream, int len) +{ + JSSMixer *d = (JSSMixer *) userdata; + + if (d != NULL) + { + jvmRenderAudio(d, stream, len / jvmGetSampleSize(d)); + } +} + + +int main(int argc, char *argv[]) +{ + SDL_AudioSpec *a_desired, *a_obtained; + DMResource *inFile; + char *sname = NULL; + int result = -1; + JSSModule *m; + JSSMixer *d; + JSSPlayer *p; + int buflen = 4096; + + if (argc > 1) + sname = argv[1]; + + // Open the files + if (sname == NULL) + inFile = dmf_create_stdio_stream(stdin); + else if ((inFile = dmf_create_stdio(sname)) == NULL) + { + fprintf(stderr, "Error opening input file '%s'. (%s)\n", sname, strerror(errno)); + return 1; + } + + // Initialize miniJSS + fprintf(stderr, "Initializing miniJSS\n"); + jssInit(); + + + // Read module file + fprintf(stderr, "Reading file: %s\n", sname); +#ifdef JSS_SUP_XM + fprintf(stderr, "* Trying XM...\n"); + result = jssLoadXM(inFile, &m); +#endif +#ifdef JSS_SUP_JSSMOD + if (result != 0) + { + size_t bufsize = dmfsize(inFile); + Uint8 *buf = dmMalloc(bufsize); + fprintf(stderr, "* Trying JSSMOD...\n"); + if (dmfread(buf, 1, bufsize, inFile) != bufsize) + { + fprintf(stderr, "Error reading file!\n"); + return 2; + } + result = jssLoadJSSMOD(buf, bufsize, &m); + dmFree(buf); + } +#endif + dmf_close(inFile); + + if (result != 0) + { + fprintf(stderr, "Error loading module file: %d\n", result); + return 3; + } + + + // Initialize SDL audio + fprintf(stderr, "Pre-initializing params\n"); + a_desired = dmMalloc(sizeof(SDL_AudioSpec)); + a_obtained = dmMalloc(sizeof(SDL_AudioSpec)); + if (!a_desired || !a_obtained) + { + fprintf(stderr, "Could not allocate SDL shit\n"); + return 3; + } + + a_desired->freq = 48000; + a_desired->format = AUDIO_S16SYS; + a_desired->channels = 2; + + // Initialize mixing device + fprintf(stderr, "Initializing miniJSS mixer with: %d, %d, %d\n", + JSS_AUDIO_S16, a_desired->channels, a_desired->freq); + + d = jvmInit(JSS_AUDIO_S16, a_desired->channels, a_desired->freq, JMIX_AUTO); + if (!d) { + fprintf(stderr, "jvmInit() returned NULL\n"); + return 3; + } + + a_desired->samples = buflen; + a_desired->callback = audioCallback; + a_desired->userdata = (void *) d; + + // Open the audio device + fprintf(stderr, "Trying to init SDL with: %d, %d, %d\n", + a_desired->format, a_desired->channels, a_desired->freq); + + if (SDL_OpenAudio(a_desired, a_obtained) < 0) + { + fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError()); + return 4; + } + + fprintf(stderr, "Got: %d, %d, %d\n", + a_obtained->format, a_obtained->channels, a_obtained->freq); + + if ((a_obtained->format != a_desired->format) || + (a_obtained->channels != a_desired->channels) || + (a_obtained->freq != a_desired->freq)) + { + fprintf(stderr, "Could not get wanted audio parameters from SDL!\n"); + return 8; + } + + free(a_desired); + + // Initialize player + p = jmpInit(d); + if (!p) + { + fprintf(stderr, "jmpInit() returned NULL\n"); + return 4; + } + + // Set callback + jvmSetCallback(d, jmpExec, p); + + // Initialize playing + jmpSetModule(p, m); + jmpPlayOrder(p, 0); + jvmSetGlobalVol(d, 60); + + // okay, main loop here ... "play" module and print out info + printf("----------------------------------------------------\n"); + SDL_PauseAudio(0); + while (p->isPlaying) + { + int r = p->iRow; + while (r == p->iRow && p->isPlaying) + SDL_Delay(50); + + printRow(stdout, p->pPattern, p->iRow); + printf("\n"); + } + + printf("----------------------------------------------------\n"); + + // Free module data + jmpClose(p); + jvmClose(d); + jssFreeModule(m); + m = NULL; + + return 0; +} diff -r 000000000000 -r 32250b436bca vecmattest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vecmattest.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,84 @@ +#include "dmlib.h" +#include "dmvecmat.h" + +void printTest(const char *test, int expected, int result) +{ + fprintf(stderr, "Test '%s': %s\n", test, + expected == result ? "OK" : "FAILED!"); +} + +#define tst(X, R) printTest(# X, (X), (R)) + + +void dm_vector_fprintf(FILE *f, const char *name, DMVector *v) +{ + if (name != NULL) + fprintf(f, "%s=", name); + + fprintf(f, "[<%1.3f, %1.3f, %1.3f>=%1.3f]", v->x, v->y, v->z, dm_vector_length(v)); + + if (name != NULL) + fprintf(f, "\n"); +} + + +void dm_vector_printf(const char *name, DMVector *v) +{ + dm_vector_fprintf(stdout, name, v); +} + + +void dm_matrix_fprintf(FILE *f, const char *name, DMMatrix *mat) +{ + int i, j, k, pad = 0; + char *tmp = NULL; + + if (name != NULL) + { + tmp = dm_strdup_printf("%s=", name); + pad = strlen(tmp); + } + + for (i = 0; i < DM_MATRIX_SIZE; i++) + { + if (i == 1) + fputs(tmp, f); + else + for (k = 0; k < pad; k++) + fputc(' ', f); + + fprintf(f, "["); + for (j = 0; j < DM_MATRIX_SIZE; j++) + fprintf(f, "% 8.3f%s", mat->m[i][j], j < DM_MATRIX_SIZE - 1 ? " " : ""); + + fprintf(f, "]\n"); + } +} + + +void dm_matrix_printf(const char *name, DMMatrix *mat) +{ + dm_matrix_fprintf(stdout, name, mat); +} + + +int main(int argc, char *argv[]) +{ + DMVector a = { -5, 1, 17 }, b = { 1, 2, 0.5 }; + DMMatrix m; + + (void) argc; + (void) argv; + + dm_vector_printf("a", &a); + dm_vector_printf("b", &a); + + dm_matrix_rot_a(&m, 0.5, 0.9, 0.1); + dm_matrix_printf("m", &m); + + dm_vector_mul_by_mat(&b, &a, &m); + + dm_vector_printf("nb", &b); + + return 0; +} diff -r 000000000000 -r 32250b436bca viewmod.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/viewmod.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,469 @@ +/* + * viewmod - View information about given module file + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2007 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#include "jss.h" +#include "jssmod.h" +#include +#include +#include "dmargs.h" + + +char *optFilename = NULL; +BOOL optViewPatterns = FALSE, + optViewInstruments = FALSE, + optViewExtInstruments = FALSE, + optViewGeneralInfo = FALSE; + + +DMOptArg optList[] = +{ + { 0, '?', "help", "Show this help and exit", OPT_NONE }, + { 1, 'p', "patterns", "View patterns", OPT_NONE }, + { 2, 'i', "instruments", "View instruments", OPT_NONE }, + { 5, 'e', "extinstruments", "View extended instruments", OPT_NONE }, + { 3, 'g', "general", "General information", OPT_NONE }, + { 4, 'v', "verbose", "Be more verbose", OPT_NONE }, +}; + +const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void argShowHelp() +{ + dmPrintBanner(stdout, dmProgName, "[options] [modfile]"); + dmArgsPrintHelp(stdout, optList, optListN); +} + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + (void) optArg; + + switch (optN) + { + case 0: + argShowHelp(); + exit(0); + break; + + case 1: + optViewPatterns = TRUE; + break; + + case 2: + optViewInstruments = TRUE; + break; + + case 3: + optViewGeneralInfo = TRUE; + break; + + case 4: + dmVerbosity++; + break; + + case 5: + optViewExtInstruments = TRUE; + break; + + default: + dmError("Unknown argument '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL argHandleFile(char *currArg) +{ + // Was not option argument + if (!optFilename) + optFilename = currArg; + else { + dmError("Gay error '%s'!\n", currArg); + return FALSE; + } + + return TRUE; +} + + +const char patNoteTable[12][3] = +{ + "C-", "C#", "D-", + "D#", "E-", "F-", + "F#", "G-", "G#", + "A-", "A#", "B-" +}; + +#define jmpNMODEffectTable (36) +static const char jmpMODEffectTable[jmpNMODEffectTable] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + +/* Print a given pattern + */ +void printPattern(FILE *f, JSSPattern *p) +{ + int i, j; + char c; + JSSNote *n; + + if (!p) + return; + + n = p->data; + + for (i = 0; i < p->nrows; i++) + { + fprintf(f, "%.2x: ", i); + + for (j = 0; j < p->nchannels; j++) + { + switch (n->note) + { + case jsetNotSet: + fprintf(f, "... "); + break; + case jsetNoteOff: + fprintf(f, "=== "); + break; + default: + fprintf(f, "%s%i ", patNoteTable[n->note % 12], n->note / 12); + break; + } + + if (n->instrument != jsetNotSet) + fprintf(f, "%.2x ", n->instrument + 1); // Because FT2 is 1-based and we use 0 internally + else + fprintf(f, ".. "); + + if (n->volume == jsetNotSet) + fprintf(f, ".. "); + else if (n->volume >= 0x00 && n->volume <= 0x40) + fprintf(f, "%.2x ", n->volume); + else + { + switch (n->volume & 0xf0) + { + case 0x50: c = '-'; break; + case 0x60: c = '+'; break; + case 0x70: c = '/'; break; + case 0x80: c = '\\'; break; + case 0x90: c = 'S'; break; + case 0xa0: c = 'V'; break; + case 0xb0: c = 'P'; break; + case 0xc0: c = '<'; break; + case 0xd0: c = '>'; break; + case 0xe0: c = 'M'; break; + default: c = '?'; break; + } + fprintf(f, "%c%x ", c, (n->volume & 0x0f)); + } + + if (n->effect >= 0 && n->effect < jmpNMODEffectTable) + fprintf(f, "%c", jmpMODEffectTable[n->effect]); + else if (n->effect == jsetNotSet) + fprintf(f, "."); + else + fprintf(f, "?"); + + if (n->param != jsetNotSet) + fprintf(f, "%.2x|", n->param); + else + fprintf(f, "..|"); + + n++; + } + + fprintf(f, "\n"); + } +} + + +/* + * Print given extended instrument + */ +void printEnvelope(FILE *f, JSSEnvelope *e, char *s) +{ + int i; + + fprintf(f, + "\t%s-envelope:\n" + "\t - flags.....: %.4x", s, e->flags); + + if (e->flags & jenvfUsed) + fprintf(f, " [used]"); + if (e->flags & jenvfSustain) + fprintf(f, " [sust]"); + if (e->flags & jenvfLooped) + fprintf(f, " [loop]"); + + fprintf(f, "\n" + "\t - npoints...: %i\n" + "\t - sustain...: %i\n" + "\t - loopS.....: %i\n" + "\t - loopE.....: %i\n", + e->npoints, e->sustain, e->loopS, e->loopE); + + if (dmVerbosity >= 2) + { + fprintf(f, "\t - Points....:"); + for (i = 0; i < e->npoints; i++) + { + fprintf(f, " [%i:%i]", + e->points[i].frame, e->points[i].value); + } + + fprintf(f, "\n"); + } +} + + +void printExtInstrument(FILE *f, JSSExtInstrument *i) +{ + if (!i) + { + fprintf(f, "\n"); + return; + } + +#ifndef JSS_LIGHT + if (i->desc) + fprintf(f, + "Description: '%s'\n", i->desc); +#endif + fprintf(f, + "nsamples.......: %i\n" + "vibratoType....: %i\n" + "vibratoSweep...: %i\n" + "vibratoDepth...: %i\n" + "vibratoRate....: %i\n" + "fadeOut........: %i\n", + i->nsamples, i->vibratoType, i->vibratoSweep, + i->vibratoDepth, i->vibratoRate, i->fadeOut); + + if (dmVerbosity >= 1) + { + printEnvelope(f, &i->volumeEnv, "Volume"); + printEnvelope(f, &i->panningEnv, "Panning"); + } + fprintf(f, "\n"); +} + + +void printInstrument(FILE *f, JSSInstrument *i) +{ + if (!i) + { + fprintf(f, "\n"); + return; + } + + if (dmVerbosity >= 1) + { +#ifndef JSS_LIGHT + if (i->desc) + fprintf(f, "Description: '%s'\n", i->desc); +#endif + fprintf(f, + "size...........: %ld (0x%lx)\n" + "loopStart......: %ld (0x%lx)\n" + "loopEnd........: %ld (0x%lx)\n" + "volume.........: %d (0x%x)\n" + "flags..........: 0x%x ", + (unsigned long) i->size, (unsigned long) i->size, + (unsigned long) i->loopS, (unsigned long) i->loopE, + (unsigned long) i->loopS, (unsigned long) i->loopE, + i->volume, i->volume, + i->flags); + + if (i->flags & jsfLooped) fprintf(f, "[loop] "); + if (i->flags & jsfBiDi) fprintf(f, "[bi-di] "); + if (i->flags & jsf16bit) fprintf(f, "[16bit] "); + + fprintf(f, + "\nC4BaseSpeed....: %d (0x%x)\n" + "ERelNote.......: %d (%s%d)\n" + "EFineTune......: %d\n" + "EPanning,,,....: %d (0x%x)\n\n", + i->C4BaseSpeed, i->C4BaseSpeed, + i->ERelNote, patNoteTable[(48 + i->ERelNote) % 12], (48 + i->ERelNote) / 12, + i->EFineTune, i->EPanning, i->EPanning); + } + else + { +#ifndef JSS_LIGHT + if (i->desc) + fprintf(f, "'%s', ", i->desc); +#endif + fprintf(f, + "s=%ld (%lx), l=%ld-%ld (%lx-%lx), v=%i (%x), f=0x%x, c4=%i (%x), rn=%i (%s%i), ft=%i, pn=%i (%x)\n", + (unsigned long) i->size, (unsigned long) i->size, + (unsigned long) i->loopS, (unsigned long) i->loopE, + (unsigned long) i->loopS, (unsigned long) i->loopE, + i->volume, i->volume, i->flags, i->C4BaseSpeed, + i->C4BaseSpeed, i->ERelNote, + patNoteTable[(48 + i->ERelNote) % 12], + (48 + i->ERelNote) / 12, i->EFineTune, + i->EPanning, i->EPanning); + } +} + + +void printGeneralInfo(FILE *f, JSSModule *m) +{ + int i; + + if (!m) + return; + + fprintf(f, "Module type.....: %i\n", m->moduleType); +#ifndef JSS_LIGHT + if (m->moduleName) + fprintf(f, "Module name.....: '%s'\n", m->moduleName); + if (m->trackerName) + fprintf(f, "Tracker name....: '%s'\n", m->trackerName); +#endif + fprintf(f, + "Speed...........: %d ticks\n" + "Tempo...........: %d bpm\n" + "Flags...........: %x ", + m->defSpeed, m->defTempo, m->defFlags); + + if (m->defFlags & jmdfAmigaPeriods) fprintf(f, "[Amiga periods] "); + if (m->defFlags & jmdfAmigaLimits) fprintf(f, "[Amiga limits] "); + if (m->defFlags & jmdfStereo) fprintf(f, "[stereo] "); + if (m->defFlags & jmdfFT2Replay) fprintf(f, "[FT2 replay] "); + if (m->defFlags & jmdfST300Slides) fprintf(f, "[ST300 slides] "); + if (m->defFlags & jmdfByteLStart) fprintf(f, "[ByteStart] "); + + fprintf(f, "\n" + "Restart pos.....: %d (order)\n" + "IntVersion......: %x\n" + "Channels........: %d\n" + "Instruments.....: %d\n" + "Ext.instruments.: %d\n" + "Patterns........: %d\n" + "Orders..........: %d\n", + m->defRestartPos, m->intVersion, m->nchannels, + m->ninstruments, m->nextInstruments, m->npatterns, + m->norders); + + if (dmVerbosity >= 1) + { + fprintf(f, "Orderlist: "); + for (i = 0; i < m->norders - 1; i++) + fprintf(f, "%d, ", m->orderList[i]); + if (i < m->norders) + fprintf(f, "%d", m->orderList[i]); + fprintf(f, "\n"); + } +} + + + +int main(int argc, char *argv[]) +{ + int result = -1, i; + DMResource *inFile; + JSSModule *m; + + dmInitProg("viewmod", "miniJSS Module Viewer", "0.4", NULL, NULL); + dmVerbosity = 0; + + // Parse arguments + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, argHandleFile, TRUE)) + exit(1); + + // Initialize miniJSS + jssInit(); + + // Open the file + dmMsg(1, "Reading module file '%s'\n", optFilename); + if (optFilename == NULL) + inFile = dmf_create_stdio_stream(stdin); + else if ((inFile = dmf_create_stdio(optFilename)) == NULL) + { + fprintf(stderr, "Error opening input file '%s'. (%s)\n", optFilename, strerror(errno)); + return 1; + } + + // Read module file + fprintf(stderr, "Reading file: %s\n", optFilename); +#ifdef JSS_SUP_XM + fprintf(stderr, "* Trying XM...\n"); + result = jssLoadXM(inFile, &m); +#endif +#ifdef JSS_SUP_JSSMOD + if (result != 0) + { + size_t bufsize = dmfsize(inFile); + Uint8 *buf = dmMalloc(bufsize); + fprintf(stderr, "* Trying JSSMOD...\n"); + if (dmfread(buf, 1, bufsize, inFile) != bufsize) + { + fprintf(stderr, "Error reading file!\n"); + return 2; + } + result = jssLoadJSSMOD(buf, bufsize, &m); + dmFree(buf); + } +#endif + dmf_close(inFile); + if (result != 0) + { + fprintf(stderr, "Error loading module file: %d\n", result); + return 3; + } + + // Print out information + if (optViewGeneralInfo) + printGeneralInfo(stdout, m); + + if (optViewPatterns) + { + for (i = 0; i < m->npatterns; i++) + { + printf("\nPattern #%03i:\n", i); + printPattern(stdout, m->patterns[i]); + } + } + + if (optViewExtInstruments) + { + printf("\n" + "ExtInstruments:\n" + "---------------\n" + ); + for (i = 0; i < m->nextInstruments; i++) + { + printf("#%03i: ", i); + printExtInstrument(stdout, m->extInstruments[i]); + } + } + + if (optViewInstruments) + { + printf("\n" + "Instruments:\n" + "------------\n" + ); + for (i = 0; i < m->ninstruments; i++) + { + printf("#%03i: ", i); + printInstrument(stdout, m->instruments[i]); + } + } + + // Free module data + jssFreeModule(m); + + exit(0); + return 0; +} diff -r 000000000000 -r 32250b436bca vptest.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vptest.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,328 @@ +#include "dmlib.h" +#include "dmargs.h" +#include "dmvecmat.h" +#include "dmres.h" +#include "dmimage.h" +#include + +#define DM_COLORS (256) + +char *optFontFile = "font.ttf", + *optBitmapFilename = "map.png"; +BOOL optBenchmark = FALSE; +int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE; +int optScrWidth = 640, optScrHeight = 480, optFontSize = 20, optScrDepth = 32; +int optBenchmarkLen = 20; + +DMOptArg optList[] = { + { 0, '?', "help", "Show this help", OPT_NONE }, + { 2, 'v', "verbose", "Be more verbose", OPT_NONE }, + { 3, 'f', "full", "Fullscreen", OPT_NONE }, + { 4, 'h', "hw", "Use SDL hardware surface", OPT_NONE }, + { 5, 's', "size", "Initial window size/resolution -s 640x480", OPT_ARGREQ }, + { 6, 'd', "depth", "Color depth of mode/window in bits (8/15/16/32)", OPT_ARGREQ }, + { 7, 'b', "bench", "Run in benchmark mode", OPT_NONE }, +}; + +const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void argShowHelp() +{ + dmArgsPrintHelp(stdout, optList, optListN); +} + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + switch (optN) { + case 0: + argShowHelp(); + exit(0); + break; + + case 2: + dmVerbosity++; + break; + + case 3: + optVFlags |= SDL_FULLSCREEN; + break; + + case 6: + if (optArg) + optScrDepth = atoi(optArg); + break; + + case 5: + { + int w, h; + if (sscanf(optArg, "%dx%d", &w, &h) == 2) + { + if (w < 320 || h < 200 || w > 3200 || h > 3200) + { + dmError("Invalid width or height: %d x %d\n", w, h); + return FALSE; + } + optScrWidth = w; + optScrHeight = h; + } + else + { + dmError("Invalid size argument '%s'.\n", optArg); + return FALSE; + } + } + break; + + case 7: + optBenchmark = TRUE; + break; + + default: + dmError("Unknown option '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +void DM_MakePalette(SDL_Surface *scr) +{ + SDL_Color pal[DM_COLORS]; + int n; + + for (n = 0; n < 256; n++) + { + pal[n].r = n; + pal[n].g = n; + pal[n].b = n; + } + + SDL_SetColors(scr, pal, 0, DM_COLORS); +} + + +void DM_PrintRect(FILE *f, SDL_Rect *r) +{ + fprintf(f, "SDL_Rect <%d, %d : %d, %d>\n", + r->x, r->y, r->w, r->h); +} + +BOOL DM_InitializeVideo(SDL_Surface **screen) +{ + *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE); + if (*screen == NULL) + { + dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError()); + return FALSE; + } + +#if 0 + SDL_Rect r; + r.x = 50; + r.y = 50; + r.w = 320; + r.h = 200; + DM_PrintRect(stderr, &r); + SDL_SetClipRect(*screen, &r); + DM_PrintRect(stderr, &r); + DM_PrintRect(stderr, &((*screen)->clip_rect)); +#endif + + return TRUE; +} + + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen = NULL, *bmap = NULL; + TTF_Font *font = NULL; + SDL_Color fontcol={255,155,155,0}; + SDL_Event event; + int mouseX, mouseY, bx, by; + BOOL initSDL = FALSE, initTTF = FALSE, exitFlag; + + dmVerbosity = 5; + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, NULL, FALSE)) + exit(1); + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) + { + dmError("Could not initialize SDL: %s\n", SDL_GetError()); + goto error_exit; + } + initSDL = TRUE; + + + if (TTF_Init() < 0) + { + dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError()); + goto error_exit; + } + initTTF = TRUE; + + font = TTF_OpenFont(optFontFile, optFontSize); + if (font == NULL) + { + dmError("Could not load TTF font '%s' (%d): %s\n", + optFontFile, optFontSize, SDL_GetError()); + goto error_exit; + } + TTF_SetFontStyle(font, TTF_STYLE_NORMAL); + + DMResource *res = dmf_create_stdio(optBitmapFilename); + if (res == NULL) + { + dmError("Could not open resource file '%s'.\n", optBitmapFilename); + goto error_exit; + } + bmap = dmLoadImage(res); + dmf_close(res); + if (bmap == NULL) + { + dmError("Could not load image file '%s'.\n", optBitmapFilename); + goto error_exit; + } + + if (optBenchmark) + { + screen = SDL_CreateRGBSurface(SDL_SWSURFACE, optScrWidth, optScrHeight, optScrDepth, 0, 0, 0, 0); + if (screen == NULL) + { + dmError("Could not create screen surface.\n"); + goto error_exit; + } + + dmMsg(0, "Benchmark mode, not opening window.\n"); + } + else + { + if (!DM_InitializeVideo(&screen)) + goto error_exit; + + SDL_WM_SetCaption("Halleluja", "DMT"); + } + + int numFrames = 0, startTime = SDL_GetTicks(), endTime = 0; + exitFlag = FALSE; + + if (optBenchmark) + dmMsg(0, "Starting benchmark, running for %d seconds.\n", optBenchmarkLen); + + while (!exitFlag) + { + if (!optBenchmark) + { + while (SDL_PollEvent(&event)) + switch (event.type) + { + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: exitFlag = TRUE; break; + + default: + break; + } + + break; + + case SDL_VIDEORESIZE: + optScrWidth = event.resize.w; + optScrHeight = event.resize.h; + + if (!DM_InitializeVideo(&screen)) + goto error_exit; + + break; + + case SDL_VIDEOEXPOSE: + break; + + case SDL_QUIT: + exit(0); + } + + SDL_GetMouseState(&mouseX, &mouseY); + bx = 300 - ((DMFloat) mouseX * 500.0f ) / (DMFloat) optScrWidth; + by = 300 - ((DMFloat) mouseY * 500.0f ) / (DMFloat) optScrHeight; + } + + if (!optBenchmark && SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0) + { + dmError("Can't lock surface.\n"); + goto error_exit; + } + + + dmClearSurface(screen, 0); + + float f = SDL_GetTicks() / 50.0f, + qw = (float) 32.0 * (1.0 + sin(f) * 0.1), + qh = (float) 32.0 * (1.0 + sin(f) * 0.1), + qw2 = (float) 132.0 * (1.0 + sin(f) * 0.1), + qh2 = (float) 132.0 * (1.0 + sin(f) * 0.1); + + dmScaledBlitSurfaceAny(bmap, bx-qw2, by-qh2, bmap->w+qw2, bmap->h+qh2, screen, +// DMD_NONE + DMD_SATURATE + ); + + dmScaledBlitSurfaceAny(bmap, bx-qw, by-qh, bmap->w+qw, bmap->h+qh, screen, +// DMD_NONE + DMD_TRANSPARENT +// DMD_SATURATE + ); +// fprintf(stderr, "%d -> %d : %d\n", bmap->format->BitsPerPixel, screen->format->BitsPerPixel, rt); +// exitFlag=TRUE; + + if (!optBenchmark) + { + dmDrawTTFText(screen, font, fontcol, 0, 0, "%3.1f FPS", + (float) (numFrames * 1000.0f) / (float) (endTime - startTime)); + + if (SDL_MUSTLOCK(screen) != 0) + SDL_UnlockSurface(screen); + + SDL_Flip(screen); + SDL_Delay(25); + } + + endTime = SDL_GetTicks(); + numFrames++; + + if (optBenchmark) + { + if (endTime - startTime > optBenchmarkLen * 1000) + exitFlag = TRUE; + } + } + + // Print benchmark results + dmMsg(0, "%d frames in %d ms, fps = %1.3f\n", + numFrames, endTime - startTime, + (float) (numFrames * 1000.0f) / (float) (endTime - startTime)); + + +error_exit: + dmMsg(0, "Shutting down dmlib.\n"); + if (screen) + SDL_FreeSurface(screen); + + if (bmap) + SDL_FreeSurface(bmap); + + if (font) + TTF_CloseFont(font); + + if (initSDL) + SDL_Quit(); + + if (initTTF) + TTF_Quit(); + + return 0; +} diff -r 000000000000 -r 32250b436bca vview.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vview.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,557 @@ +#include "dmlib.h" +#include "dmargs.h" + +#define DM_COLORS (256) + +char *optFilename = NULL, *optFontFile = "font.ttf"; +int optVFlags = SDL_SWSURFACE | SDL_HWPALETTE; +int optScrWidth = 640, optScrHeight = 480, optFontSize = 20, optScrDepth = 32; +BOOL optShowSurfaceInfo = FALSE; + +DMOptArg optList[] = { + { 0, '?', "help", "Show this help", OPT_NONE }, + { 2, 'v', "verbose", "Be more verbose", OPT_NONE }, + { 3, 'f', "full", "Fullscreen", OPT_NONE }, + { 4, 'h', "hw", "Use SDL hardware surface", OPT_NONE }, + { 5, 's', "size", "Initial window size/resolution -s 640x480", OPT_ARGREQ }, + { 6, 'd', "depth", "Color depth of mode/window in bits (8/15/16/32)", OPT_ARGREQ }, + { 7, 'i', "info", "Print out extra information on used SDL surfaces", OPT_NONE } +}; + +const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void argShowHelp() +{ + dmPrintBanner(stdout, dmProgName, "[options] "); + dmArgsPrintHelp(stdout, optList, optListN); +} + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + switch (optN) { + case 0: + argShowHelp(); + exit(0); + break; + + case 2: + dmVerbosity++; + break; + + case 3: + optVFlags |= SDL_FULLSCREEN; + break; + + case 6: + if (optArg) + optScrDepth = atoi(optArg); + break; + + case 7: + optShowSurfaceInfo = TRUE; + break; + + case 5: + { + int w, h; + if (sscanf(optArg, "%dx%d", &w, &h) == 2) + { + if (w < 320 || h < 200 || w > 3200 || h > 3200) + { + dmError("Invalid width or height: %d x %d\n", w, h); + return FALSE; + } + optScrWidth = w; + optScrHeight = h; + } + else + { + dmError("Invalid size argument '%s'.\n", optArg); + return FALSE; + } + } + break; + + default: + dmError("Unknown option '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL argHandleFile(char *filename) +{ + if (optFilename == NULL) + { + optFilename = dm_strdup(filename); + return TRUE; + } + else + { + dmError("Too many filenames specified ('%s')\n", filename); + return FALSE; + } +} + +void DM_MakePalette(SDL_Color *pal) +{ + int n; + + for (n = 0; n < 64; n++) + { + pal[n].r = n * 2; + pal[n].g = 0; + pal[n].b = 0; + } + + for (n = 64; n < 128; n++) + { + pal[n].r = n * 2; + pal[n].g = (n - 64) * 4; + pal[n].b = (n - 64) * 4; + } + + for (n = 128; n < 192; n++) + { + pal[n].r = (255 - n) * 4; + pal[n].g = (255 - n) * 2; + pal[n].b = (255 - n) * 2; + } + + for (n = 192; n < 256; n++) + { + pal[n].r = 0; + pal[n].g = (255 - n) * 2; + pal[n].b = (255 - n) * 2; + } +} + + +void DM_CreatePaletteFrom(SDL_Color *pal, Uint8 *buf) +{ + int n, offs; + for (offs = n = 0; n < DM_COLORS; n++) + { + pal[n].r = buf[offs++]; + pal[n].g = buf[offs++]; + pal[n].b = buf[offs++]; + } +} + +void printSurface(FILE *f, SDL_Surface *s, const char *name) +{ + if (!optShowSurfaceInfo || s == NULL) return; + + fprintf(f, "\nSDL_Surface[%p]: %s:\n" + " flags : 0x%08x\n" + " format : %p\n", s, name, s->flags, s->format); + if (s->format != NULL) + { + SDL_PixelFormat *pf = s->format; + fprintf(f, + " palette : %p\n" + " BitsPerPixel : %d\n" + " BytesPerPixel : %d\n" + " colorkey : %d (%08x)\n" + " alpha : %d\n", + pf->palette, + pf->BitsPerPixel, + pf->BytesPerPixel, + pf->colorkey, pf->colorkey, + pf->alpha); + + fprintf(f, + " R/G/B/A loss : %d / %d / %d / %d\n" + " R/G/B/A shift : %d / %d / %d / %d\n" + " R/G/B/A mask : %08x / %08x / %08x / %08x\n", + pf->Rloss , pf->Gloss , pf->Bloss , pf->Aloss, + pf->Rshift , pf->Gshift , pf->Bshift , pf->Ashift, + pf->Rmask , pf->Gmask , pf->Bmask , pf->Amask + ); + } + fprintf(f, + " W x H : %d x %d\n" + " pitch : %d bytes\n" + " pixels : %p\n\n", + s->w, s->h, + s->pitch, + s->pixels); +} + +BOOL DM_InitializeVideo(SDL_Surface **screen) +{ + *screen = SDL_SetVideoMode(optScrWidth, optScrHeight, optScrDepth, optVFlags | SDL_RESIZABLE); + if (*screen == NULL) + { + dmError("Can't SDL_SetVideoMode(): %s\n", SDL_GetError()); + return FALSE; + } + printSurface(stdout, *screen, "screen"); + return TRUE; +} + +void DM_UnRLE(Uint8 *dst, Uint8 **udata, const Uint8 *dp, const int rleMarker, const Uint8 *de) +{ + Uint8 *data = *udata; + + while (data < dp && dst < de) + { + int c = *data++; + if ((c & rleMarker) == rleMarker) + { + int cnt = c & (0xff ^ rleMarker); + c = *data++; + while (cnt-- && dst < de) + *dst++ = c; + } + else + *dst++ = c; + } + + *udata = data; +} + +void DM_Redraw(SDL_Surface *screen, Uint8 *data, int skip, int useRLE, int rleMarker) +{ + Uint8 *pix = screen->pixels; + + if (useRLE) + { + const int tmpmax = screen->w * 64; + Uint8 *tmp = dmMalloc(tmpmax); + const Uint8 *dp = data + (screen->w * screen->h); + + if (skip > tmpmax) + skip = tmpmax; + + if (useRLE == 1) + { + int yc; + for (yc = 0; yc < screen->h; yc++) + { + memset(tmp, 0xff, tmpmax); + DM_UnRLE(tmp, &data, dp, rleMarker, tmp + tmpmax); + memcpy(pix, tmp, screen->w); + pix += screen->pitch; + } + } + else + if (useRLE == 2) + { + int yc; + for (yc = 0; yc < screen->h; yc++) + { + memset(tmp, 0xff, tmpmax); + DM_UnRLE(tmp, &data, dp, rleMarker, tmp + skip); + memcpy(pix, tmp, screen->w); + pix += screen->pitch; + } + } + else + { + printf("useRLE=%d\n", useRLE); + } + + dmFree(tmp); + } + else + { + int yc; + for (yc = 0; yc < screen->h; yc++) + { + int xc; + for (xc = 0; xc < screen->w; xc++) + pix[xc] = *data++; + pix += screen->pitch; + } + } +} + +void DM_Random(SDL_Surface *screen, int q) +{ + Uint8 *pix = screen->pixels; + int xc, yc; + + for (yc = 0; yc < screen->h; yc++) + { + Uint8 *dp = pix; + + for (xc = 0; xc < screen->w; xc++) + *dp++ = (xc * q + yc * q); + + pix += screen->pitch; + } +} + +void adjustValue(int *val, int min, int max, int delta) +{ + *val += delta; + if (*val < min) *val = min; + else if (*val > max) *val = max; +} + +int main(int argc, char *argv[]) +{ + SDL_Color pal[256]; + SDL_Surface *screen = NULL, *bmap = NULL; + TTF_Font *font = NULL; + SDL_Color fontcol={255,255,255,0}; + SDL_Event event; + Uint8 *scrData = NULL; + size_t scrSize = 0; + int scrWidth = 128, scrHeight = 128, scrPalOffs = 0, scrOffs = 0, scrSkip = 0; + BOOL initSDL = FALSE, initTTF = FALSE, + exitFlag, needRedraw, palChanged = FALSE, showTest = FALSE, useRLE = FALSE, + showHex = FALSE, scrPalette = FALSE, scrChanged; + + // Initialize + dmInitProg("vtest", "vtester", "0.1", NULL, NULL); + + // Parse arguments + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, argHandleFile, FALSE)) + exit(1); + + + if (optFilename == NULL) + { + dmError("No input file specified.\n"); + goto error_exit; + } + else + { + FILE *f = fopen(optFilename, "rb"); + if (f == NULL) + { + dmError("Could not open input file '%s'\n"); + goto error_exit; + } + fseek(f, 0L, SEEK_END); + scrSize = ftell(f); + fseek(f, 0L, SEEK_SET); + + if ((scrData = malloc(scrSize)) == NULL) + { + fclose(f); + dmError("Error allocating memory for data, %d bytes.\n", scrSize); + goto error_exit; + } + + fread(scrData, 1, scrSize, f); + fclose(f); + } + + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) + { + dmError("Could not initialize SDL: %s\n", SDL_GetError()); + goto error_exit; + } + initSDL = TRUE; + + if (TTF_Init() < 0) + { + dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError()); + goto error_exit; + } + initTTF = TRUE; + + font = TTF_OpenFont(optFontFile, optFontSize); + if (font == NULL) + { + dmError("Could not load TTF font '%s' (%d): %s\n", + optFontFile, optFontSize, SDL_GetError()); + goto error_exit; + } + TTF_SetFontStyle(font, TTF_STYLE_NORMAL); + + if (!DM_InitializeVideo(&screen)) + goto error_exit; + + SDL_WM_SetCaption("Halleluja", "DMT"); + + exitFlag = FALSE; + scrChanged = palChanged = TRUE; + + while (!exitFlag) + { + while (SDL_PollEvent(&event)) + switch (event.type) + { + case SDL_KEYDOWN: + { + int amount = 10; + + if ((event.key.keysym.mod & KMOD_CTRL)) + amount = 50; + + if ((event.key.keysym.mod & KMOD_SHIFT)) + amount = 1; + + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: exitFlag = TRUE; break; + case SDLK_LEFT: adjustValue(&scrWidth, 0, 1024, -amount); scrChanged = TRUE; break; + case SDLK_RIGHT: adjustValue(&scrWidth, 0, 1024, amount); scrChanged = TRUE; break; + case SDLK_UP: adjustValue(&scrHeight, 0, 1024, -amount); scrChanged = TRUE; break; + case SDLK_DOWN: adjustValue(&scrHeight, 0, 1024, amount); scrChanged = TRUE; break; + + case SDLK_F1: + scrPalette = !scrPalette; + palChanged = TRUE; + break; + + case SDLK_F2: + showTest = !showTest; + break; + + case SDLK_F3: adjustValue(&scrPalOffs, 0, scrSize - 256 * 3, -amount); palChanged = TRUE; break; + case SDLK_F4: adjustValue(&scrPalOffs, 0, scrSize - 256 * 3, amount); palChanged = TRUE; break; + + case SDLK_F5: adjustValue(&scrOffs, 0, scrSize, -amount); break; + case SDLK_F6: adjustValue(&scrOffs, 0, scrSize, amount); break; + + case SDLK_F7: adjustValue(&scrSkip, 0, scrSize, -amount); break; + case SDLK_F8: adjustValue(&scrSkip, 0, scrSize, amount); break; + + case SDLK_F9: + showHex = !showHex; + break; + + case SDLK_r: + useRLE = (useRLE + 1) % 3; + break; + + default: + break; + } + } + + needRedraw = TRUE; + break; + + case SDL_VIDEORESIZE: + optScrWidth = event.resize.w; + optScrHeight = event.resize.h; + + if (!DM_InitializeVideo(&screen)) + goto error_exit; + + needRedraw = TRUE; + break; + + case SDL_VIDEOEXPOSE: + needRedraw = TRUE; + break; + + case SDL_QUIT: + exit(0); + } + + if (scrChanged) + { + SDL_FreeSurface(bmap); + bmap = SDL_CreateRGBSurface(SDL_SWSURFACE, scrWidth, scrHeight, 8, 0, 0, 0, 0); + printSurface(stdout, bmap, "bmap"); + needRedraw = TRUE; + scrChanged = FALSE; + palChanged = TRUE; + } + + if (palChanged) + { + if (scrPalette) + DM_CreatePaletteFrom(pal, scrData + scrPalOffs); + else + DM_MakePalette(pal); + + SDL_SetColors(bmap, pal, 0, DM_COLORS); + palChanged = FALSE; + } + + if (needRedraw) + { + if (SDL_MUSTLOCK(screen) != 0 && SDL_LockSurface(screen) != 0) + { + dmError("Can't lock surface.\n"); + goto error_exit; + } + + dmClearSurface(screen, dmMapRGB(screen, 255, 255, 150)); + dmFillRect(screen, 0, 0, screen->w, TTF_FontHeight(font) + 10, dmMapRGB(screen, 50, 50, 150)); + dmDrawTTFText(screen, font, fontcol, 5, 5, "%3d x %3d @ 0x%08x, skip=%d/0x%x", + scrWidth, scrHeight, scrOffs, scrSkip, scrSkip); + + dmFillRect(screen, 0, screen->h - TTF_FontHeight(font) - 10, screen->w, screen->h, dmMapRGB(screen, 150, 50, 50)); + if (scrPalette) + { + dmDrawTTFText(screen, font, fontcol, 5, screen->h - TTF_FontHeight(font) - 5, + "Palette offset 0x%08x/%08x", scrPalOffs, scrSize); + } + else + { + dmDrawTTFText(screen, font, fontcol, 5, screen->h - TTF_FontHeight(font) - 5, + "Generated palette."); + } + dmDrawTTFText(screen, font, fontcol, 450, screen->h - TTF_FontHeight(font) - 5, + "useRLE=%d", useRLE); + + + if (showTest) + DM_Random(bmap, SDL_GetTicks() / 100); + else + DM_Redraw(bmap, scrData + scrOffs, scrSkip, useRLE, 0xc0); + +// float f = SDL_GetTicks() / 200.0f; +// dmScaledBlitSurfaceAny(bmap, sin(f) * 50, 40, screen->w + 100 * cos(f), screen->h - 80, screen, DMD_NONE); + dmScaledBlitSurfaceAny(bmap, 5, 40, screen->w - 10, screen->h - 80, screen, DMD_NONE); + + if (showHex) + { + int w = 350, h = screen->h - 50; + int i, x = screen->w - w, y = 25; + int fh = TTF_FontHeight(font); + + dmFillRect(screen, screen->w - 350, - 5, w, h, dmMapRGB(screen, 50, 50, 50)); + + dmDrawTTFText(screen, font, fontcol, x, y, "Palette hexdump"); + y += fh + 5; + for (i = 0; i < 16; i++) + { + int offs = scrPalOffs + i * 3; + dmDrawTTFText(screen, font, fontcol, x, y + fh * i, "%04x: %02x %02x %02x | '%c' '%c' '%c'", + offs, scrData[offs], scrData[offs+1], scrData[offs+2], + scrData[offs], scrData[offs+1], scrData[offs+2]); + } + } + + if (SDL_MUSTLOCK(screen) != 0) + SDL_UnlockSurface(screen); + + SDL_Flip(screen); + needRedraw = FALSE; + } + + SDL_Delay(10); + } + + +error_exit: + if (screen) + SDL_FreeSurface(screen); + + if (font) + TTF_CloseFont(font); + + if (initSDL) + SDL_Quit(); + + if (initTTF) + TTF_Quit(); + + return 0; +} diff -r 000000000000 -r 32250b436bca xm2jss.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xm2jss.c Fri Sep 28 01:54:23 2012 +0300 @@ -0,0 +1,997 @@ +/* + * xm2jss - Convert XM module to JSSMOD + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2006-2009 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#include +#include +#include "jss.h" +#include "jssmod.h" +#include "dmlib.h" +#include "dmargs.h" +#include "dmres.h" + + +char *srcFilename = NULL, *destFilename = NULL; +BOOL optIgnoreErrors = FALSE, + optStripExtInstr = FALSE, + optStripInstr = FALSE, + optStripSamples = FALSE, + optOptimize = FALSE; + +int optPatternMode = PATMODE_COMP_HORIZ, + optSampMode16 = jsampDelta, + optSampMode8 = jsampFlipSign | jsampDelta; + +#define SAMPMODE_MASK (jsampFlipSign | jsampSwapEndianess | jsampSplit | jsampDelta) + + +static const char* patModeTable[PATMODE_LAST] = +{ + "Raw horizontal", + "Compressed horizontal (similar to XM modules)", + "Raw vertical", + "Compressed vertical", + "Raw vertical for each element", +}; + + +DMOptArg optList[] = { + { 0, '?', "help", "Show this help", OPT_NONE }, + { 5, 'v', "verbose", "Be more verbose", OPT_NONE }, + { 6, 'o', "output", "Output file", OPT_ARGREQ }, + { 1, 'i', "ignore", "Ignore errors", OPT_NONE }, + { 2, 'p', "patterns", "Pattern storage mode", OPT_ARGREQ }, + { 3, 'E', "strip-ext-instr","Strip ext. instruments (implies -I -S)", OPT_NONE }, + { 9, 'I', "strip-instr", "Strip instruments (implies -S)", OPT_NONE }, + { 4, 'S', "strip-samples", "Strip instr. sampledata", OPT_NONE }, + { 7, '8', "smode8", "8-bit sample conversion flags", OPT_ARGREQ }, + { 8, '1', "smode16", "16-bit sample conversion flags", OPT_ARGREQ }, + {10, 'O', "optimize", "Optimize module", OPT_NONE }, +}; + +const int optListN = sizeof(optList) / sizeof(optList[0]); + + +void argShowHelp() +{ + int i; + + dmPrintBanner(stdout, dmProgName, "[options] "); + dmArgsPrintHelp(stdout, optList, optListN); + + printf("\n" + "Pattern storage modes:\n"); + + for (i = 1; i < PATMODE_LAST; i++) + printf(" %d = %s\n", i, patModeTable[i-1]); + + printf( + "\n" + "Sample data conversion flags (summative):\n" + " 1 = Delta encoding (DEF 8 & 16)\n" + " 2 = Flip signedness (DEF 8)\n" + " 4 = Swap endianess (affects 16-bit only)\n" + " 8 = Split and de-interleave hi/lo bytes (affects 16-bit only)\n" + "\n" + ); +} + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + (void) optArg; + + switch (optN) + { + case 0: + argShowHelp(); + exit(0); + break; + + case 1: + optIgnoreErrors = TRUE; + break; + + case 2: + optPatternMode = atoi(optArg); + if (optPatternMode <= 0 || optPatternMode >= PATMODE_LAST) + { + dmError("Unknown pattern conversion mode %d\n", optPatternMode); + return FALSE; + } + break; + + case 7: optSampMode8 = atoi(optArg) & SAMPMODE_MASK; break; + case 8: optSampMode16 = atoi(optArg) & SAMPMODE_MASK; break; + + case 4: optStripSamples = TRUE; break; + case 3: optStripExtInstr = TRUE; break; + case 9: optStripInstr = TRUE; break; + case 10: optOptimize = TRUE; break; + + case 5: + dmVerbosity++; + break; + + case 6: + destFilename = optArg; + break; + + default: + dmError("Unknown argument '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL argHandleFile(char *currArg) +{ + // Was not option argument + if (!srcFilename) + srcFilename = currArg; + else + { + dmError("Gay error '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +/* These functions and the macro mess are meant to make the + * conversion routines themselves clearer and simpler. + */ +BOOL jsPutByte(Uint8 *patBuf, size_t patBufSize, size_t *npatBuf, Uint8 val) +{ + if (*npatBuf >= patBufSize) + return FALSE; + else + { + patBuf[*npatBuf] = val; + (*npatBuf)++; + return TRUE; + } +} + +#define JSPUTBYTE(x) do { if (!jsPutByte(patBuf, patBufSize, patSize, x)) return DMERR_BOUNDS; } while (0) + +#define JSCOMP(x,z) do { if ((x) != jsetNotSet) { qflags |= (z); qcomp++; } } while (0) + +#define JSCOMPPUT(xf,xv,qv) do { \ + if (qflags & (xf)) { \ + if ((xv) < 0 || (xv) > 255) \ + JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, \ + "%s value out of bounds %d.\n", qv, (xv)); \ + JSPUTBYTE(xv); \ + } \ +} while (0) + +#define JSCONVPUT(xv,qv) do { \ + if ((xv) != jsetNotSet) { \ + if ((xv) < 0 || (xv) > 254) \ + JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, \ + "%s value out of bounds %d.\n", qv, (xv)); \ + JSPUTBYTE((xv) + 1); \ + } else { \ + JSPUTBYTE(0); \ + } \ +} while (0) + + +/* Convert a note + */ +static int jssConvertNote(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *note) +{ + Uint8 tmp; + if (note->note == jsetNotSet) + tmp = 0; + else if (note->note == jsetNoteOff) + tmp = 127; + else + tmp = note->note + 1; + + if (tmp > 0x7f) + JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp); + + JSPUTBYTE(tmp & 0x7f); + + JSCONVPUT(note->instrument, "Instrument"); + JSCONVPUT(note->volume, "Volume"); + JSCONVPUT(note->effect, "Effect"); + + tmp = (note->param != jsetNotSet) ? note->param : 0; + JSPUTBYTE(tmp); + + return DMERR_OK; +} + + +/* Compress a note + */ +static int jssCompressNote(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *note) +{ + Uint8 qflags = 0; + int qcomp = 0; + + JSCOMP(note->note, COMP_NOTE); + JSCOMP(note->instrument, COMP_INSTRUMENT); + JSCOMP(note->volume, COMP_VOLUME); + JSCOMP(note->effect, COMP_EFFECT); + if (note->param != jsetNotSet && note->param != 0) + { + qflags |= COMP_PARAM; + qcomp++; + } + + if (qcomp < 4) + { + JSPUTBYTE(qflags | 0x80); + + if (note->note != jsetNotSet) + { + Uint8 tmp = (note->note != jsetNoteOff) ? note->note : 127; + if (tmp > 0x7f) + JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp); + JSPUTBYTE(tmp); + } + + JSCOMPPUT(COMP_INSTRUMENT, note->instrument, "Instrument"); + JSCOMPPUT(COMP_VOLUME, note->volume, "Volume"); + JSCOMPPUT(COMP_EFFECT, note->effect, "Effect"); + JSCOMPPUT(COMP_PARAM, note->param, "Param"); + } else + return jssConvertNote(patBuf, patBufSize, patSize, note); + + return DMERR_OK; +} + + +/* Compress pattern + */ +static int jssConvertPatternCompHoriz(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) +{ + int row, channel; + *patSize = 0; + + for (row = 0; row < pattern->nrows; row++) + for (channel = 0; channel < pattern->nchannels; channel++) + { + const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; + const int res = jssCompressNote(patBuf, patBufSize, patSize, note); + 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; +} + + +static int jssConvertPatternCompVert(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) +{ + int row, channel; + *patSize = 0; + + for (channel = 0; channel < pattern->nchannels; channel++) + for (row = 0; row < pattern->nrows; row++) + { + const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; + const int res = jssCompressNote(patBuf, patBufSize, patSize, note); + 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; +} + + +/* Convert a pattern + */ +static int jssConvertPatternRawHoriz(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) +{ + int row, channel; + *patSize = 0; + + for (row = 0; row < pattern->nrows; row++) + for (channel = 0; channel < pattern->nchannels; channel++) + { + const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; + const int res = jssConvertNote(patBuf, patBufSize, patSize, note); + 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 jssConvertPatternRawVert(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) +{ + int row, channel; + *patSize = 0; + + for (channel = 0; channel < pattern->nchannels; channel++) + for (row = 0; row < pattern->nrows; row++) + { + const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; + const int res = jssConvertNote(patBuf, patBufSize, patSize, note); + 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; +} + + +#define JSFOREACHNOTE1 \ + for (channel = 0; channel < pattern->nchannels; channel++) \ + for (row = 0; row < pattern->nrows; row++) { \ + const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; + +#define JSFOREACHNOTE2 } + +static int jssConvertPatternRawElem(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) +{ + Uint8 tmp; + int row, channel; + *patSize = 0; + + JSFOREACHNOTE1; + if (note->note == jsetNotSet) + tmp = 0; + else if (note->note == jsetNoteOff) + tmp = 127; + else + tmp = note->note + 1; + if (tmp > 0x7f) + JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp); + JSPUTBYTE(tmp); + JSFOREACHNOTE2; + + JSFOREACHNOTE1; + JSCONVPUT(note->instrument, "Instrument"); + JSFOREACHNOTE2; + + JSFOREACHNOTE1; + JSCONVPUT(note->volume, "Volume"); + JSFOREACHNOTE2; + + JSFOREACHNOTE1; + JSCONVPUT(note->effect, "Effect"); + JSFOREACHNOTE2; + + JSFOREACHNOTE1; + JSCONVPUT(note->param, "Param"); + JSFOREACHNOTE2; + + return DMERR_OK; +} + +#undef JSFOREACHNOTE1 +#undef JSFOREACHNOTE2 + + +static void jssCopyEnvelope(JSSMODEnvelope *je, JSSEnvelope *e) +{ + int i; + + je->flags = e->flags; + je->npoints = e->npoints; + je->sustain = e->sustain; + je->loopS = e->loopS; + je->loopE = e->loopE; + + for (i = 0; i < e->npoints; i++) + { + je->points[i].frame = e->points[i].frame; + je->points[i].value = e->points[i].value; + } +} + + +/* Save a JSSMOD file + */ +int jssSaveJSSMOD(FILE *outFile, JSSModule *m, int patMode, int flags8, int flags16) +{ + JSSMODHeader jssH; + int i, pattern, order, instr; + const size_t patBufSize = 64*1024; // 64kB pattern buffer + Uint8 *patBuf; + + // Check the module + if (m == NULL) + JSSERROR(DMERR_NULLPTR, DMERR_NULLPTR, "Module pointer was NULL\n"); + + if ((m->nchannels < 1) || (m->npatterns < 1) || (m->norders < 1)) + JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, + "Module had invalid values (nchannels=%i, npatterns=%i, norders=%i)\n", + m->nchannels, m->npatterns, m->norders); + + // Create the JSSMOD header + dmMsg(2," * Writing JSSMOD-header 0x%04x.\n", JSSMOD_VERSION); + jssH.idMagic[0] = 'J'; + jssH.idMagic[1] = 'M'; + jssH.idVersion = JSSMOD_VERSION; + jssH.norders = m->norders; + jssH.npatterns = m->npatterns; + jssH.nchannels = m->nchannels; + jssH.nextInstruments = m->nextInstruments; + jssH.ninstruments = m->ninstruments; + jssH.defFlags = m->defFlags; + jssH.intVersion = m->intVersion; + jssH.defRestartPos = m->defRestartPos; + jssH.defSpeed = m->defSpeed; + jssH.defTempo = m->defTempo; + jssH.patMode = patMode; + + // Write header + if (fwrite(&jssH, sizeof(jssH), 1, outFile) != 1) + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, "Could not write JSSMOD header!\n"); + + // Write orders list + dmMsg(2," * Writing %d item orders list.\n", m->norders); + for (order = 0; order < m->norders; order++) + { + Uint16 tmp = m->orderList[order]; + if (fwrite(&tmp, sizeof(tmp), 1, outFile) != 1) + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, "Could not write JSSMOD orders list.\n"); + } + + // Allocate pattern compression buffer + if ((patBuf = dmMalloc(patBufSize)) == NULL) + JSSERROR(DMERR_MALLOC, DMERR_MALLOC, + "Error allocating memory for pattern compression buffer.\n"); + + // Write patterns + dmMsg(2," * Writing %d patterns.\n", m->npatterns); + for (pattern = 0; pattern < m->npatterns; pattern++) + { + JSSMODPattern patHead; + size_t finalSize = 0; + i = -1; + + switch (patMode) + { + case PATMODE_RAW_HORIZ: + i = jssConvertPatternRawHoriz(patBuf, patBufSize, &finalSize, m->patterns[pattern]); + break; + case PATMODE_COMP_HORIZ: + i = jssConvertPatternCompHoriz(patBuf, patBufSize, &finalSize, m->patterns[pattern]); + break; + case PATMODE_RAW_VERT: + i = jssConvertPatternRawVert(patBuf, patBufSize, &finalSize, m->patterns[pattern]); + break; + case PATMODE_COMP_VERT: + i = jssConvertPatternCompVert(patBuf, patBufSize, &finalSize, m->patterns[pattern]); + break; + case PATMODE_RAW_ELEM: + i = jssConvertPatternRawElem(patBuf, patBufSize, &finalSize, m->patterns[pattern]); + break; + default: + dmFree(patBuf); + JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, + "Unsupported pattern conversion mode %d.\n", patMode); + break; + } + + if (i != DMERR_OK) + { + dmFree(patBuf); + JSSERROR(i, i, "Error converting pattern data #%i\n", pattern); + } + else + { + dmMsg(3, " - Pattern %d size %d bytes\n", pattern, finalSize); + patHead.nrows = m->patterns[pattern]->nrows; + patHead.size = finalSize; + + if (fwrite(&patHead, sizeof(patHead), 1, outFile) != 1) + { + dmFree(patBuf); + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, + "Error writing pattern #%d header\n", pattern); + } + + if (fwrite(patBuf, sizeof(Uint8), finalSize, outFile) != finalSize) + { + dmFree(patBuf); + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, + "Error writing pattern #%d data\n", pattern); + } + } + } + + dmFree(patBuf); + + // Write extended instruments + dmMsg(2," * Writing %d Extended Instruments.\n", m->nextInstruments); + for (instr = 0; instr < m->nextInstruments; instr++) + { + JSSMODExtInstrument jssE; + JSSExtInstrument *einst = m->extInstruments[instr]; + + memset(&jssE, 0, sizeof(jssE)); + + if (einst) + { + // Create header + jssE.nsamples = einst->nsamples; + for (i = 0; i < jsetNNotes; i++) + { + int snum = einst->sNumForNotes[i]; + jssE.sNumForNotes[i] = (snum != jsetNotSet) ? snum : 0; + } + + jssCopyEnvelope(&jssE.volumeEnv, &(einst->volumeEnv)); + jssCopyEnvelope(&jssE.panningEnv, &(einst->panningEnv)); + jssE.vibratoType = einst->vibratoType; + jssE.vibratoSweep = einst->vibratoSweep; + jssE.vibratoDepth = einst->vibratoDepth; + jssE.fadeOut = einst->fadeOut; + } else + JSSWARNING(DMERR_NULLPTR, DMERR_NULLPTR, "Extended instrument #%i NULL!\n", instr); + + // Write to file + if (fwrite(&jssE, sizeof(jssE), 1, outFile) != 1) + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, + "Could not write JSSMOD extended instrument #%i to file!\n", instr); + } + + + // Write sample instrument headers + dmMsg(2," * Writing %d Instrument headers.\n", m->ninstruments); + for (instr = 0; instr < m->ninstruments; instr++) + { + JSSMODInstrument jssI; + JSSInstrument *pInst = m->instruments[instr]; + + memset(&jssI, 0, sizeof(jssI)); + + // Create header + if (pInst) + { + jssI.size = pInst->size; + jssI.loopS = pInst->loopS; + jssI.loopE = pInst->loopE; + jssI.volume = pInst->volume; + jssI.flags = pInst->flags; + jssI.C4BaseSpeed = pInst->C4BaseSpeed; + jssI.ERelNote = pInst->ERelNote; + jssI.EFineTune = pInst->EFineTune; + jssI.EPanning = pInst->EPanning; + jssI.hasData = (pInst->data != NULL) ? TRUE : FALSE; + jssI.convFlags = (pInst->flags & jsf16bit) ? flags16 : flags8; + } + else + JSSWARNING(DMERR_NULLPTR, DMERR_NULLPTR, "Instrument #%i NULL!\n", instr); + + // Write to file + if (fwrite(&jssI, sizeof(jssI), 1, outFile) != 1) + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, + "Could not write JSSMOD instrument #%i to file!\n", instr); + } + + // Write sample data + dmMsg(2," * Writing %d samples.\n", m->ninstruments); + for (instr = 0; instr < m->ninstruments; instr++) + if (m->instruments[instr]) + { + JSSInstrument *inst = m->instruments[instr]; + if (inst->data != NULL) + { + size_t res; + if (inst->flags & jsf16bit) + { + jssEncodeSample16(inst->data, inst->size, flags16); + res = fwrite(inst->data, sizeof(Uint16), inst->size, outFile); + } + else + { + jssEncodeSample8(inst->data, inst->size, flags8); + res = fwrite(inst->data, sizeof(Uint8), inst->size, outFile); + } + + if (res != inst->size) + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, + "Could not write JSSMOD sample #%i to file!\n", instr); + } + } + + return DMERR_OK; +} + + +/* Optimize a given module + */ +JSSModule *optimizeModule(JSSModule *m) +{ + BOOL usedPatterns[jsetMaxPatterns + 1], + usedInstruments[jsetMaxInstruments + 1], + usedExtInstruments[jsetMaxInstruments + 1]; + int mapExtInstruments[jsetMaxInstruments + 1], + mapInstruments[jsetMaxInstruments + 1], + mapPatterns[jsetMaxPatterns + 1]; + JSSModule *r = NULL; + int i, n8, n16; + + // Allocate a new module + if ((r = jssAllocateModule()) == NULL) + return NULL; + + // Allocate tables + + // Copy things + r->moduleType = m->moduleType; + r->moduleName = dm_strdup(m->moduleName); + r->trackerName = dm_strdup(m->trackerName); + r->defSpeed = m->defSpeed; + r->defTempo = m->defTempo; + r->defFlags = m->defFlags; + r->defRestartPos = m->defRestartPos; + r->intVersion = m->intVersion; + r->nchannels = m->nchannels; + r->norders = m->norders; + for (i = 0; i < jsetNChannels; i++) + r->defPanning[i] = m->defPanning[i]; + + // Initialize values + for (i = 0; i <= jsetMaxInstruments; i++) + { + usedExtInstruments[i] = FALSE; + usedInstruments[i] = FALSE; + mapExtInstruments[i] = jsetNotSet; + mapInstruments[i] = jsetNotSet; + } + + for (i = 0; i <= jsetMaxPatterns; i++) + { + usedPatterns[i] = FALSE; + mapPatterns[i] = jsetNotSet; + } + + // Find out all used patterns and ext.instruments + for (i = 0; i < m->norders; i++) + { + int pattern = m->orderList[i]; + if (pattern >= 0 && pattern < m->npatterns) + { + JSSPattern *p = m->patterns[pattern]; + if (p != NULL) + { + int row, channel; + JSSNote *n = p->data; + + // Mark pattern as used + usedPatterns[pattern] = TRUE; + + // Check all notes + for (row = 0; row < p->nrows; row++) + for (channel = 0; channel < p->nchannels; channel++, n++) + { + if (n->instrument != jsetNotSet) + { + if (n->instrument >= 0 && n->instrument < m->nextInstruments) + usedExtInstruments[n->instrument] = TRUE; + else + dmMsg(3, "Pattern 0x%x, row=0x%x, chn=%d has invalid instrument 0x%x\n", + pattern, row, channel, n->instrument); + } + } + } + else + { + dmError("Pattern 0x%x is used on order 0x%x, but has no data!\n", + pattern, i); + } + } + else + if (pattern != jsetMaxPatterns) + { + dmError("Order 0x%x has invalid pattern number 0x%x!\n", + i, pattern); + } + } + + // Find out used instruments + for (i = 0; i <= jsetMaxInstruments; i++) + if (usedExtInstruments[i] && m->extInstruments[i] != NULL) + { + int note; + JSSExtInstrument *e = m->extInstruments[i]; + + for (note = 0; note < jsetNNotes; note++) + if (e->sNumForNotes[note] != jsetNotSet) + { + int q = e->sNumForNotes[note]; + if (q >= 0 && q < m->ninstruments) + { + usedInstruments[q] = TRUE; + } + else + { + dmError("Ext.instrument #%d sNumForNotes[%d] value out range (%d < %d).\n", + i, m->ninstruments, q); + } + } + } + + // Create pattern mappings + r->npatterns = 0; + dmMsg(2, "Unused patterns: "); + + for (i = 0; i <= jsetMaxPatterns; i++) + if (m->patterns[i] != NULL) + { + if (!usedPatterns[i]) + { + dmPrint(2, "0x%x, ", i); + } + else + { + if (i >= m->npatterns) + dmError("Pattern 0x%x >= 0x%x, but used!\n", i, m->npatterns); + + mapPatterns[i] = r->npatterns; + r->patterns[r->npatterns] = m->patterns[i]; + (r->npatterns)++; + } + } + dmPrint(2, "\n"); + + dmMsg(1, "%d used patterns, %d unused.\n", + r->npatterns, m->npatterns - r->npatterns); + + + // Re-map instruments + dmMsg(2, "Unused instruments: "); + for (n8 = n16 = i = 0; i <= jsetMaxInstruments; i++) + if (m->instruments[i] != NULL) + { + if (!usedInstruments[i]) + { + dmPrint(2, "0x%x, ", i); + } + else + { + JSSInstrument *ip = m->instruments[i]; + if (i >= m->ninstruments) + dmError("Instrument 0x%x >= 0x%x, but used!\n", i, m->ninstruments); + + mapInstruments[i] = r->ninstruments; + r->instruments[r->ninstruments] = ip; + (r->ninstruments)++; + + if (ip->flags & jsf16bit) + n16++; + else + n8++; + } + } + dmPrint(2, "\n"); + dmMsg(1, "Total of (%d) 16-bit, (%d) 8-bit samples, (%d) instruments.\n", + n16, n8, r->ninstruments); + + // Re-map ext.instruments + dmMsg(2, "Unused ext.instruments: "); + for (i = 0; i < jsetMaxInstruments; i++) + if (usedExtInstruments[i]) + { + if (i >= m->nextInstruments) + { + dmError("Ext.instrument 0x%x >= 0x%x, but used!\n", + i, m->nextInstruments); + } + else + if (m->extInstruments[i] != NULL) + { + JSSExtInstrument *e = m->extInstruments[i]; + int note; + + mapExtInstruments[i] = r->nextInstruments; + r->extInstruments[r->nextInstruments] = e; + (r->nextInstruments)++; + + // Re-map sNumForNotes + for (note = 0; note < jsetNNotes; note++) + { + int q = e->sNumForNotes[note]; + if (q != jsetNotSet) + { + int map; + if (q >= 0 && q <= jsetMaxInstruments) + { + map = mapInstruments[q]; + } + else + { + map = jsetNotSet; + dmError("e=%d, note=%d, q=%d/%d\n", i, note, q, r->ninstruments); + } + e->sNumForNotes[note] = map; + } + } + } + else + { + dmPrint(2, "[0x%x==NULL], ", i); + mapExtInstruments[i] = jsetNotSet; + } + } + else + { + if (i < m->nextInstruments && m->extInstruments[i] != NULL) + { + dmPrint(2, "0x%x, ", i); + } + } + dmPrint(2, "\n"); + dmMsg(1, "%d extended instruments.\n", r->nextInstruments); + + + // Remap pattern instrument data + for (i = 0; i < r->npatterns; i++) + { + int row, channel; + JSSPattern *p = r->patterns[i]; + JSSNote *n = p->data; + + for (row = 0; row < p->nrows; row++) + for (channel = 0; channel < p->nchannels; channel++, n++) + if (n->instrument >= 0 && n->instrument <= jsetMaxInstruments) + { + n->instrument = mapExtInstruments[n->instrument]; + } + } + + // Remap orders list + for (i = 0; i < m->norders; i++) + { + r->orderList[i] = mapPatterns[m->orderList[i]]; + } + + return r; +} + + +int main(int argc, char *argv[]) +{ + DMResource *sfile = NULL; + FILE *dfile = NULL; + JSSModule *sm, *dm; + int result; + + dmInitProg("xm2jss", "XM to JSSMOD converter", "0.6", NULL, NULL); + dmVerbosity = 0; + + // Parse arguments + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, argHandleFile, TRUE)) + exit(1); + + + // Read the source file + if (srcFilename == NULL) + sfile = dmf_create_stdio_stream(stdin); + else + if ((sfile = dmf_create_stdio(srcFilename)) == NULL) + { + dmError("Error opening input file '%s'. (%s)\n", + srcFilename, strerror(errno)); + return 1; + } + + // Initialize miniJSS + jssInit(); + + // Read file + dmMsg(1, "Reading XM-format file ...\n"); + result = jssLoadXM(sfile, &sm); + dmf_close(sfile); + if (result != 0) + { + dmError("Error while loading XM file (%i), ", result); + if (optIgnoreErrors) + fprintf(stderr, "ignoring. This may cause problems.\n"); + else + { + fprintf(stderr, "giving up. Use --ignore if you want to try to convert anyway.\n"); + return 2; + } + } + + // Check stripping settings + if (optStripExtInstr) optStripInstr = TRUE; + if (optStripInstr) optStripSamples = TRUE; + + // Remove samples + if (optStripSamples) + { + int i; + + dmMsg(1, "Stripping samples...\n"); + for (i = 0; i < sm->ninstruments; i++) + { + dmFree(sm->instruments[i]->data); + sm->instruments[i]->data = NULL; + } + } + + // Remove instruments + if (optStripInstr) + { + int i; + + dmMsg(1, "Stripping instruments...\n"); + for (i = 0; i < sm->ninstruments; i++) + { + dmFree(sm->instruments[i]); + sm->instruments[i] = NULL; + } + sm->ninstruments = 0; + } + + // Remove ext.instruments + if (optStripExtInstr) + { + int i; + + dmMsg(1, "Stripping ext.instruments...\n"); + for (i = 0; i < sm->nextInstruments; i++) + { + dmFree(sm->extInstruments[i]); + sm->extInstruments[i] = NULL; + } + sm->nextInstruments = 0; + } + // Run the optimization procedure + if (optOptimize) + { + dmMsg(1, "Optimizing module data...\n"); + dm = optimizeModule(sm); + } else + dm = sm; + + // Write output file + if (destFilename == NULL) + dfile = stdout; + else if ((dfile = fopen(destFilename, "wb")) == NULL) + { + dmError("Error creating output file '%s'. (%s)\n", destFilename, strerror(errno)); + return 1; + } + + dmMsg(1, "Writing JSSMOD-format file [patMode=0x%x, samp8=0x%2x, samp16=0x%2x]\n", + optPatternMode, optSampMode8, optSampMode16); + + result = jssSaveJSSMOD(dfile, dm, optPatternMode, optSampMode8, optSampMode16); + + fclose(dfile); + + if (result != 0) + { + dmError("Error while saving JSSMOD file (%i), the resulting file may be broken!\n", result); + } + + dmMsg(1, "Conversion complete.\n"); + return 0; +}