aboutsummaryrefslogtreecommitdiff
path: root/engines/sludge
diff options
context:
space:
mode:
authoryinsimei2017-05-26 05:24:38 +0200
committerEugene Sandulenko2017-07-13 18:27:45 +0200
commit219044abf9841461043d6e2acf0d5a48a7c7648b (patch)
treee9d16f9de2317e3596da5a71447e0c823ba3861d /engines/sludge
parent94439e2ce311734bfe7bb5700a6584b7550ea8f9 (diff)
downloadscummvm-rg350-219044abf9841461043d6e2acf0d5a48a7c7648b.tar.gz
scummvm-rg350-219044abf9841461043d6e2acf0d5a48a7c7648b.tar.bz2
scummvm-rg350-219044abf9841461043d6e2acf0d5a48a7c7648b.zip
SLUDGE: Add sludge files and make it compile
Diffstat (limited to 'engines/sludge')
-rw-r--r--engines/sludge/CommonCode/functionlist.h175
-rw-r--r--engines/sludge/CommonCode/specialsettings.h8
-rw-r--r--engines/sludge/CommonCode/tga.cpp278
-rw-r--r--engines/sludge/CommonCode/tga.h45
-rw-r--r--engines/sludge/CommonCode/utf8.cpp541
-rw-r--r--engines/sludge/CommonCode/utf8.h75
-rw-r--r--engines/sludge/CommonCode/version.h13
-rw-r--r--engines/sludge/allfiles.h32
-rw-r--r--engines/sludge/backdrop.cpp1753
-rw-r--r--engines/sludge/backdrop.h103
-rw-r--r--engines/sludge/bg_effects.cpp375
-rw-r--r--engines/sludge/bg_effects.h33
-rw-r--r--engines/sludge/builtin.cpp2545
-rw-r--r--engines/sludge/builtin.h31
-rw-r--r--engines/sludge/color.frag7
-rw-r--r--engines/sludge/color.vert13
-rw-r--r--engines/sludge/colours.h47
-rw-r--r--engines/sludge/csludge.h43
-rw-r--r--engines/sludge/cursors.cpp86
-rw-r--r--engines/sludge/cursors.h30
-rw-r--r--engines/sludge/debug.cpp52
-rw-r--r--engines/sludge/debug.h29
-rwxr-xr-xengines/sludge/eglport/eglport.cpp712
-rw-r--r--engines/sludge/eglport/eglport.h109
-rw-r--r--engines/sludge/errors.h72
-rw-r--r--engines/sludge/fileset.cpp237
-rw-r--r--engines/sludge/fileset.h39
-rw-r--r--engines/sludge/fixScaleSprite.frag29
-rw-r--r--engines/sludge/fixScaleSprite.vert25
-rw-r--r--engines/sludge/floor.cpp290
-rw-r--r--engines/sludge/floor.h57
-rw-r--r--engines/sludge/fonttext.cpp206
-rw-r--r--engines/sludge/fonttext.h35
-rw-r--r--engines/sludge/freeze.cpp364
-rw-r--r--engines/sludge/freeze.h55
-rw-r--r--engines/sludge/graphics.cpp956
-rw-r--r--engines/sludge/graphics.h118
-rw-r--r--engines/sludge/helpers.cpp36
-rw-r--r--engines/sludge/helpers.h27
-rw-r--r--engines/sludge/language.cpp210
-rw-r--r--engines/sludge/language.h46
-rw-r--r--engines/sludge/libvorbis/COPYING28
-rw-r--r--engines/sludge/libvorbis/vorbis_misc.h57
-rw-r--r--engines/sludge/libvorbis/vorbis_os.h182
-rw-r--r--engines/sludge/libwebm/AUTHORS.TXT5
-rw-r--r--engines/sludge/libwebm/LICENSE.TXT30
-rw-r--r--engines/sludge/libwebm/PATENTS.TXT22
-rw-r--r--engines/sludge/libwebm/mkvparser.cpp7327
-rw-r--r--engines/sludge/libwebm/mkvparser.hpp729
-rw-r--r--engines/sludge/libwebm/mkvreader.cpp118
-rw-r--r--engines/sludge/libwebm/mkvreader.hpp37
-rw-r--r--engines/sludge/line.cpp143
-rw-r--r--engines/sludge/line.h27
-rw-r--r--engines/sludge/linuxstuff.cpp225
-rw-r--r--engines/sludge/linuxstuff.h40
-rw-r--r--engines/sludge/loadsave.cpp678
-rw-r--r--engines/sludge/loadsave.h33
-rw-r--r--engines/sludge/main_loop.cpp508
-rw-r--r--engines/sludge/main_loop.h27
-rw-r--r--engines/sludge/memwatch.cpp61
-rw-r--r--engines/sludge/memwatch.h28
-rw-r--r--engines/sludge/module.mk42
-rw-r--r--engines/sludge/moreio.cpp324
-rw-r--r--engines/sludge/moreio.h42
-rw-r--r--engines/sludge/movie.cpp1043
-rw-r--r--engines/sludge/movie.h38
-rw-r--r--engines/sludge/newfatal.cpp158
-rw-r--r--engines/sludge/newfatal.h39
-rw-r--r--engines/sludge/objtypes.cpp177
-rw-r--r--engines/sludge/objtypes.h49
-rw-r--r--engines/sludge/people.cpp1177
-rw-r--r--engines/sludge/people.h134
-rw-r--r--engines/sludge/platform-dependent.h40
-rw-r--r--engines/sludge/region.cpp164
-rw-r--r--engines/sludge/region.h42
-rw-r--r--engines/sludge/savedata.cpp268
-rw-r--r--engines/sludge/savedata.h28
-rw-r--r--engines/sludge/scale.frag60
-rw-r--r--engines/sludge/scale.vert25
-rw-r--r--engines/sludge/scale_noaa.frag31
-rw-r--r--engines/sludge/shaders.cpp178
-rw-r--r--engines/sludge/shaders.h37
-rw-r--r--engines/sludge/sludge.cpp5
-rw-r--r--engines/sludge/sludger.cpp1563
-rw-r--r--engines/sludge/sludger.h98
-rw-r--r--engines/sludge/sound.h71
-rw-r--r--engines/sludge/sound_bass.cpp391
-rw-r--r--engines/sludge/sound_nosound.cpp135
-rw-r--r--engines/sludge/sound_openal.cpp817
-rw-r--r--engines/sludge/sprbanks.cpp74
-rw-r--r--engines/sludge/sprbanks.h36
-rw-r--r--engines/sludge/sprites.cpp1111
-rw-r--r--engines/sludge/sprites.h93
-rw-r--r--engines/sludge/statusba.cpp229
-rw-r--r--engines/sludge/statusba.h62
-rw-r--r--engines/sludge/stringy.cpp39
-rw-r--r--engines/sludge/stringy.h28
-rw-r--r--engines/sludge/talk.cpp274
-rw-r--r--engines/sludge/talk.h52
-rw-r--r--engines/sludge/texture.frag22
-rw-r--r--engines/sludge/texture.vert16
-rw-r--r--engines/sludge/thumbnail.cpp266
-rw-r--r--engines/sludge/thumbnail.h31
-rw-r--r--engines/sludge/timing.cpp56
-rw-r--r--engines/sludge/timing.h31
-rw-r--r--engines/sludge/transition.cpp408
-rw-r--r--engines/sludge/transition.h28
-rw-r--r--engines/sludge/variable.cpp669
-rw-r--r--engines/sludge/variable.h119
-rw-r--r--engines/sludge/vid.cpp259
-rw-r--r--engines/sludge/vid.h30
-rw-r--r--engines/sludge/winstuff.cpp227
-rw-r--r--engines/sludge/winstuff.h39
-rw-r--r--engines/sludge/yuv.frag25
-rw-r--r--engines/sludge/yuv.vert12
-rw-r--r--engines/sludge/zbuffer.cpp205
-rw-r--r--engines/sludge/zbuffer.h51
117 files changed, 31907 insertions, 3 deletions
diff --git a/engines/sludge/CommonCode/functionlist.h b/engines/sludge/CommonCode/functionlist.h
new file mode 100644
index 0000000000..3d98553129
--- /dev/null
+++ b/engines/sludge/CommonCode/functionlist.h
@@ -0,0 +1,175 @@
+/*
+This is the list of the built in functions
+
+true or false states if it's a used function
+in the current version of the engine,
+but that value currently isn't used anywhere
+*/
+
+FUNC(true, say)
+FUNC(true, skipSpeech)
+FUNC(true, statusText)
+FUNC(true, pause)
+FUNC(true, onLeftMouse)
+FUNC(true, onRightMouse)
+FUNC(true, setCursor)
+FUNC(true, addOverlay)
+FUNC(true, addCharacter)
+FUNC(true, playSound)
+FUNC(true, getMouseX)
+FUNC(true, getMouseY)
+FUNC(true, addScreenRegion)
+FUNC(true, onMoveMouse)
+FUNC(true, onFocusChange)
+FUNC(true, getOverObject)
+FUNC(true, blankScreen)
+FUNC(true, moveCharacter)
+FUNC(true, onKeyboard)
+FUNC(true, getObjectX)
+FUNC(true, getObjectY)
+FUNC(true, random)
+FUNC(true, spawnSub)
+FUNC(true, blankArea)
+FUNC(true, hideCharacter)
+FUNC(true, showCharacter)
+FUNC(true, callEvent)
+FUNC(true, removeScreenRegion)
+FUNC(true, animate)
+FUNC(true, turnCharacter)
+FUNC(true, removeAllCharacters)
+FUNC(true, removeAllScreenRegions)
+FUNC(true, setScale)
+FUNC(true, newStack)
+FUNC(true, pushToStack)
+FUNC(true, popFromStack)
+FUNC(true, clearStatus)
+FUNC(true, addStatus)
+FUNC(true, removeLastStatus)
+FUNC(true, lightStatus)
+FUNC(true, getStatusText)
+FUNC(true, setStatusColour)
+FUNC(true, deleteFromStack)
+FUNC(true, freeze)
+FUNC(true, unfreeze)
+FUNC(true, pasteImage)
+FUNC(true, copyStack)
+FUNC(true, completeTimers)
+FUNC(true, setCharacterDrawMode)
+FUNC(true, anim)
+FUNC(true, costume)
+FUNC(true, pickOne)
+FUNC(true, setCostume)
+FUNC(true, wait)
+FUNC(true, somethingSpeaking)
+FUNC(true, substring)
+FUNC(true, stringLength)
+FUNC(true, darkBackground)
+FUNC(true, saveGame)
+FUNC(true, loadGame)
+FUNC(true, quitGame)
+FUNC(true, rename)
+FUNC(true, stackSize)
+FUNC(true, pasteString)
+FUNC(true, startMusic)
+FUNC(true, setDefaultMusicVolume)
+FUNC(true, setMusicVolume)
+FUNC(true, stopMusic)
+FUNC(true, stopSound)
+FUNC(true, setFont)
+FUNC(true, alignStatus)
+FUNC(true, showFloor)
+FUNC(true, showBoxes)
+FUNC(true, positionStatus)
+FUNC(true, setFloor)
+FUNC(true, forceCharacter)
+FUNC(true, jumpCharacter)
+FUNC(true, peekStart)
+FUNC(true, peekEnd)
+FUNC(true, enqueue)
+FUNC(true, setZBuffer)
+FUNC(true, getMatchingFiles)
+FUNC(true, inFont)
+FUNC(true, onLeftMouseUp)
+FUNC(true, onRightMouseUp)
+FUNC(true, loopSound)
+FUNC(true, removeCharacter)
+FUNC(true, stopCharacter)
+FUNC(true, launch)
+FUNC(true, howFrozen)
+FUNC(true, setPasteColour)
+FUNC(true, setLitStatusColour)
+FUNC(true, fileExists)
+FUNC(true, floatCharacter)
+FUNC(true, cancelSub)
+FUNC(true, setCharacterWalkSpeed)
+FUNC(true, deleteAllFromStack)
+FUNC(true, setCharacterExtra)
+FUNC(true, mixOverlay)
+FUNC(true, pasteCharacter)
+FUNC(true, setSceneDimensions)
+FUNC(true, aimCamera)
+FUNC(true, getMouseScreenX)
+FUNC(true, getMouseScreenY)
+FUNC(true, setDefaultSoundVolume)
+FUNC(true, setSoundVolume)
+FUNC(true, setSoundLoopPoints)
+FUNC(true, setSpeechMode)
+FUNC(true, setLightMap)
+FUNC(true, think)
+FUNC(true, getCharacterDirection)
+FUNC(true, isCharacter)
+FUNC(true, isScreenRegion)
+FUNC(true, isMoving)
+FUNC(true, deleteFile)
+FUNC(true, renameFile)
+FUNC(true, hardScroll)
+FUNC(true, stringWidth)
+FUNC(true, setSpeechSpeed)
+FUNC(true, normalCharacter)
+FUNC(true, fetchEvent)
+FUNC(true, transitionLevel)
+FUNC(true, spinCharacter)
+FUNC(true, setFontSpacing)
+FUNC(true, burnString)
+FUNC(true, captureAllKeys)
+FUNC(true, cacheSound)
+FUNC(true, setCharacterSpinSpeed)
+FUNC(true, transitionMode)
+FUNC(false, _rem_movieStart)
+FUNC(false, _rem_movieAbort)
+FUNC(false, _rem_moviePlaying)
+FUNC(false, _rem_updateDisplay)
+FUNC(true, getSoundCache)
+FUNC(true, saveCustomData)
+FUNC(true, loadCustomData)
+FUNC(true, setCustomEncoding)
+FUNC(true, freeSound)
+FUNC(true, parallaxAdd)
+FUNC(true, parallaxClear)
+FUNC(true, setBlankColour)
+FUNC(true, setBurnColour)
+FUNC(true, getPixelColour)
+FUNC(true, makeFastArray)
+FUNC(true, getCharacterScale)
+FUNC(true, getLanguageID)
+FUNC(false, _rem_launchWith)
+FUNC(true, getFramesPerSecond)
+FUNC(true, showThumbnail)
+FUNC(true, setThumbnailSize)
+FUNC(true, hasFlag)
+FUNC(true, snapshotGrab)
+FUNC(true, snapshotClear)
+FUNC(true, bodgeFilenames)
+FUNC(false, _rem_registryGetString)
+FUNC(true, quitWithFatalError)
+FUNC(true, _rem_setCharacterAA)
+FUNC(true, _rem_setMaximumAA)
+FUNC(true, setBackgroundEffect)
+FUNC(true, doBackgroundEffect)
+FUNC(true, setCharacterAngleOffset)
+FUNC(true, setCharacterTransparency)
+FUNC(true, setCharacterColourise)
+FUNC(true, zoomCamera)
+FUNC(true, playMovie)
+FUNC(true, stopMovie)
+FUNC(true, pauseMovie)
diff --git a/engines/sludge/CommonCode/specialsettings.h b/engines/sludge/CommonCode/specialsettings.h
new file mode 100644
index 0000000000..1e41e4e83a
--- /dev/null
+++ b/engines/sludge/CommonCode/specialsettings.h
@@ -0,0 +1,8 @@
+#define SPECIAL_REGISTERED 1
+#define SPECIAL_FULLSCREEN 2
+#define SPECIAL_MOUSE_1 4
+#define SPECIAL_SILENT 8
+#define SPECIAL_MOUSE_2 16
+#define SPECIAL_INVISIBLE 32
+#define SPECIAL_HIDELOGO 64
+#define SPECIAL_HIDELOADING 128
diff --git a/engines/sludge/CommonCode/tga.cpp b/engines/sludge/CommonCode/tga.cpp
new file mode 100644
index 0000000000..d25ccd9787
--- /dev/null
+++ b/engines/sludge/CommonCode/tga.cpp
@@ -0,0 +1,278 @@
+#include <stdio.h>
+
+#include "tga.h"
+
+//FILE * debugFile = fopen ("TGAdebug.txt", "wt");
+
+unsigned short int makeColour (unsigned char r, unsigned char g, unsigned char b) {
+ unsigned short int reply = (unsigned short int) (r >> 3);
+ reply <<= 6;
+ reply += (unsigned short int) (g >> 2);
+ reply <<= 5;
+ reply += (unsigned short int) (b >> 3);
+ return reply & 65503;
+}
+
+int get2bytesReverse (FILE * fp) {
+ int a = fgetc (fp);
+ return a + fgetc (fp) * 256;
+}
+
+int countDown = 0;
+
+bool dither24bitImages = 0;
+
+char ditherArray[4][4] = {{4,12,6,14},{10,0,8,2},{7,15,5,13},{9,3,11,1}};
+
+void grabRGBA (FILE * fp, int bpc, unsigned char & r, unsigned char & g, unsigned char & b, unsigned char & a, palCol thePalette[])
+{
+ int grabbed1, grabbed2;
+ switch (bpc) {
+ case 8:
+ grabbed1 = fgetc (fp);
+ r = thePalette[grabbed1].r;
+ g = thePalette[grabbed1].g;
+ b = thePalette[grabbed1].b;
+ if (r == 255 && g == 0 && b == 255) {
+ r = g = b = a = 0;
+ } else a = 255;
+ break;
+
+ case 16:
+ grabbed1 = fgetc (fp);
+ grabbed2 = fgetc (fp);
+ if (grabbed2*256+grabbed1 == 31775) {
+ r=g=b=a=0;
+ break;
+ }
+
+ r = ((grabbed2 & 127) << 1),
+ g = ((grabbed1 & 224) >> 2) + (grabbed2 << 6);
+ b = ((grabbed1 & 31) << 3);
+ if (r == 255 && g == 0 && b == 255) {
+ r = g = b = a = 0;
+ } else a = 255;
+ break;
+
+ case 24:
+ b = fgetc (fp);
+ g = fgetc (fp);
+ r = fgetc (fp);
+ if (r == 255 && g == 0 && b == 255) {
+ r = g = b = a = 0;
+ } else a = 255;
+ break;
+
+ case 32:
+ b = fgetc (fp);
+ g = fgetc (fp);
+ r = fgetc (fp);
+ a = fgetc (fp);
+ break;
+ }
+}
+
+
+void grabRGB (FILE * fp, int bpc, unsigned char & r, unsigned char & g, unsigned char & b, palCol thePalette[])
+{
+ int a;
+ int grabbed1, grabbed2;
+ switch (bpc) {
+ case 8:
+ grabbed1 = fgetc (fp);
+ r = thePalette[grabbed1].r;
+ g = thePalette[grabbed1].g;
+ b = thePalette[grabbed1].b;
+ break;
+
+ case 16:
+ grabbed1 = fgetc (fp);
+ grabbed2 = fgetc (fp);
+ r = ((grabbed2 & 127) << 1),
+ g = ((grabbed1 & 224) >> 2) + (grabbed2 << 6);
+ b = ((grabbed1 & 31) << 3);
+ break;
+
+ case 24:
+ b = fgetc (fp);
+ g = fgetc (fp);
+ r = fgetc (fp);
+ break;
+
+ case 32:
+ b = fgetc (fp);
+ g = fgetc (fp);
+ r = fgetc (fp);
+ a = fgetc (fp);
+ if (a < 100) {
+ r = 255;
+ g = 0;
+ b = 255;
+ }
+ break;
+ }
+}
+
+void grabRGBACompressed (FILE * fp, int bpc, unsigned char & r2, unsigned char & g2, unsigned char & b2, unsigned char & a2, palCol thePalette[]) {
+ static unsigned char r, g, b, a;
+ static bool oneCol;
+ unsigned short col;
+
+ // Do we have to start a new packet?
+ if (countDown == 0) {
+
+ // Read the packet description thingy
+ col = fgetc (fp);
+
+ // Is it raw data?
+ if (col >= 128) {
+ oneCol = true;
+ countDown = col - 127;
+ grabRGBA (fp, bpc, r, g, b, a, thePalette);
+ // fprintf (debugFile, " %d raw colours...\n", countDown);
+ } else {
+ oneCol = false;
+ countDown = col + 1;
+ // fprintf (debugFile, " %d pixels the same colour...\n", countDown);
+ }
+ }
+
+ countDown --;
+
+ if (! oneCol) {
+ grabRGBA (fp, bpc, r2, g2, b2, a2, thePalette);
+ } else {
+ r2 = r;
+ g2 = g;
+ b2 = b;
+ a2 = a;
+ }
+}
+
+
+void grabRGBCompressed (FILE * fp, int bpc, unsigned char & r2, unsigned char & g2, unsigned char & b2, palCol thePalette[]) {
+ static unsigned char r, g, b;
+ static bool oneCol;
+ unsigned short col;
+
+ // Do we have to start a new packet?
+ if (countDown == 0) {
+
+ // Read the packet description thingy
+ col = fgetc (fp);
+
+ // Is it raw data?
+ if (col >= 128) {
+ oneCol = true;
+ countDown = col - 127;
+ grabRGB (fp, bpc, r, g, b, thePalette);
+ // fprintf (debugFile, " %d raw colours...\n", countDown);
+ } else {
+ oneCol = false;
+ countDown = col + 1;
+ // fprintf (debugFile, " %d pixels the same colour...\n", countDown);
+ }
+ }
+
+ countDown --;
+
+ if (! oneCol) {
+ grabRGB (fp, bpc, r2, g2, b2, thePalette);
+ } else {
+ r2 = r;
+ g2 = g;
+ b2 = b;
+ }
+}
+
+void addDither (unsigned char & col, const unsigned char add)
+{
+ int tot = col;
+ tot += add;
+ col = (tot > 255) ? 255 : tot;
+}
+
+unsigned short readAColour (FILE * fp, int bpc, palCol thePalette[], int x, int y) {
+ unsigned char r,g,b;
+ grabRGB (fp, bpc, r, g, b, thePalette);
+
+ if (dither24bitImages)
+ {
+ addDither (r, ditherArray[x&3][y&3]);
+ addDither (g, ditherArray[x&3][y&3] / 2);
+ addDither (b, ditherArray[x&3][y&3]);
+ }
+
+ return makeColour (r, g, b);
+}
+
+unsigned short readCompressedColour (FILE * fp, int bpc, palCol thePalette[], int x, int y) {
+ unsigned char r,g,b;
+ grabRGBCompressed (fp, bpc, r, g, b, thePalette);
+
+ if (dither24bitImages)
+ {
+ addDither (r, ditherArray[x&3][y&3]);
+ addDither (g, ditherArray[x&3][y&3] / 2);
+ addDither (b, ditherArray[x&3][y&3]);
+ }
+
+ return makeColour (r, g, b);
+}
+
+const char * readTGAHeader (TGAHeader & h, FILE * fp, palCol thePalette[]) {
+
+ h.IDBlockSize = fgetc (fp);
+ h.gotMap = fgetc (fp);
+ unsigned char imageType = fgetc (fp);
+ h.firstPalColour = get2bytesReverse (fp);
+ h.numPalColours = get2bytesReverse (fp);
+ h.bitsPerPalColour = fgetc (fp);
+ h.xOrigin = get2bytesReverse (fp);
+ h.yOrigin = get2bytesReverse (fp);
+ h.width = get2bytesReverse (fp);
+ h.height = get2bytesReverse (fp);
+ h.pixelDepth = fgetc (fp);
+ h.imageDescriptor = fgetc (fp);
+ countDown = 0;
+ // Who cares about the ID block?
+ fseek (fp, h.IDBlockSize, 1);
+
+ switch (imageType) {
+ case 1:
+ case 2:
+ h.compressed = false;
+ break;
+
+ case 9:
+ case 10:
+ h.compressed = true;
+ break;
+
+ default:
+ return "Unsupported internal image format... are you sure this is a valid TGA image file?";
+ }
+
+ if (h.pixelDepth != 8 && h.pixelDepth != 16 && h.pixelDepth != 24 && h.pixelDepth != 32) {
+ return "Colour depth is not 8, 16, 24 or 32 bits... are you sure this is a valid TGA image file?";
+ }
+
+ if (h.gotMap) {
+ int c;
+ for (c = 0; c < h.numPalColours; c ++) {
+ grabRGB (fp, h.bitsPerPalColour, thePalette[c].r, thePalette[c].g, thePalette[c].b, thePalette);
+ }
+ }
+
+ return NULL;
+}
+
+void setDither (int dither)
+{
+ dither24bitImages = dither;
+}
+
+bool getDither ()
+{
+ return dither24bitImages;
+}
diff --git a/engines/sludge/CommonCode/tga.h b/engines/sludge/CommonCode/tga.h
new file mode 100644
index 0000000000..e7f6b556db
--- /dev/null
+++ b/engines/sludge/CommonCode/tga.h
@@ -0,0 +1,45 @@
+#include <stdio.h>
+
+struct TGAHeader {
+ unsigned char IDBlockSize;
+ unsigned char gotMap;
+ bool compressed;
+ unsigned short int firstPalColour;
+ unsigned short int numPalColours;
+ unsigned char bitsPerPalColour;
+ unsigned short xOrigin;
+ unsigned short yOrigin;
+ unsigned short width;
+ unsigned short height;
+ unsigned char pixelDepth;
+ unsigned char imageDescriptor;
+};
+
+struct palCol {
+ unsigned char r, g, b;
+};
+
+void grabRGBCompressed(FILE *fp, int bpc, unsigned char &r2, unsigned char &g2, unsigned char &b2, palCol thePalette[]);
+void grabRGB(FILE *fp, int bpc, unsigned char &r, unsigned char &g, unsigned char &b, palCol thePalette[]);
+void grabRGBACompressed(FILE *fp, int bpc, unsigned char &r2, unsigned char &g2, unsigned char &b2, unsigned char &a2, palCol thePalette[]);
+void grabRGBA(FILE *fp, int bpc, unsigned char &r, unsigned char &g, unsigned char &b, unsigned char &a, palCol thePalette[]);
+
+unsigned short int makeColour(unsigned char r, unsigned char g, unsigned char b);
+unsigned short readAColour(FILE *fp, int bpc, palCol thePalette[], int x, int y);
+unsigned short readCompressedColour(FILE *fp, int bpc, palCol thePalette[], int x, int y);
+const char *readTGAHeader(TGAHeader &h, FILE *fp, palCol thePalette[]);
+void setDither(int dither);
+bool getDither();
+
+inline unsigned short redValue(unsigned short c) {
+ return (c >> 11) << 3;
+}
+inline unsigned short greenValue(unsigned short c) {
+ return ((c >> 5) & 63) << 2;
+}
+inline unsigned short blueValue(unsigned short c) {
+ return (c & 31) << 3;
+}
+inline int brightness(unsigned short c) {
+ return ((int) redValue(c)) + ((int) greenValue(c)) + ((int) blueValue(c) >> 1);
+}
diff --git a/engines/sludge/CommonCode/utf8.cpp b/engines/sludge/CommonCode/utf8.cpp
new file mode 100644
index 0000000000..24d9010764
--- /dev/null
+++ b/engines/sludge/CommonCode/utf8.cpp
@@ -0,0 +1,541 @@
+/*
+ Basic UTF-8 manipulation routines
+ by Jeff Bezanson
+ placed in the public domain Fall 2005
+
+ This code is designed to provide the utilities you need to manipulate
+ UTF-8 as an internal string encoding. These functions do not perform the
+ error checking normally needed when handling UTF-8 data, so if you happen
+ to be from the Unicode Consortium you will want to flay me alive.
+ I do this because error checking can be performed at the boundaries (I/O),
+ with these routines reserved for higher performance on data known to be
+ valid.
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef WIN32
+#include <malloc.h>
+#else
+#include <alloca.h>
+#endif
+
+#include "utf8.h"
+
+static const uint32_t offsetsFromUTF8[6] = {
+ 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL
+};
+
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/* returns length of next utf-8 sequence */
+int u8_seqlen(char *s)
+{
+ return trailingBytesForUTF8[(unsigned int)(unsigned char)s[0]] + 1;
+}
+
+/* conversions without error checking
+ only works for valid UTF-8, i.e. no 5- or 6-byte sequences
+ srcsz = source size in bytes, or -1 if 0-terminated
+ sz = dest size in # of wide characters
+
+ returns # characters converted
+ dest will always be L'\0'-terminated, even if there isn't enough room
+ for all the characters.
+ if sz = srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space.
+*/
+int u8_toucs(uint32_t *dest, int sz, char *src, int srcsz)
+{
+ uint32_t ch;
+ char *src_end = src + srcsz;
+ int nb;
+ int i=0;
+
+ while (i < sz-1) {
+ nb = trailingBytesForUTF8[(unsigned char)*src];
+ if (srcsz == -1) {
+ if (*src == 0)
+ goto done_toucs;
+ }
+ else {
+ if (src + nb >= src_end)
+ goto done_toucs;
+ }
+ ch = 0;
+ switch (nb) {
+ /* these fall through deliberately */
+ case 3: ch += (unsigned char)*src++; ch <<= 6;
+ case 2: ch += (unsigned char)*src++; ch <<= 6;
+ case 1: ch += (unsigned char)*src++; ch <<= 6;
+ case 0: ch += (unsigned char)*src++;
+ }
+ ch -= offsetsFromUTF8[nb];
+ dest[i++] = ch;
+ }
+ done_toucs:
+ dest[i] = 0;
+ return i;
+}
+
+/* srcsz = number of source characters, or -1 if 0-terminated
+ sz = size of dest buffer in bytes
+
+ returns # characters converted
+ dest will only be '\0'-terminated if there is enough space. this is
+ for consistency; imagine there are 2 bytes of space left, but the next
+ character requires 3 bytes. in this case we could NUL-terminate, but in
+ general we can't when there's insufficient space. therefore this function
+ only NUL-terminates if all the characters fit, and there's space for
+ the NUL as well.
+ the destination string will never be bigger than the source string.
+*/
+int u8_toutf8(char *dest, int sz, uint32_t *src, int srcsz)
+{
+ uint32_t ch;
+ int i = 0;
+ char *dest_end = dest + sz;
+
+ while (srcsz<0 ? src[i]!=0 : i < srcsz) {
+ ch = src[i];
+ if (ch < 0x80) {
+ if (dest >= dest_end)
+ return i;
+ *dest++ = (char)ch;
+ }
+ else if (ch < 0x800) {
+ if (dest >= dest_end-1)
+ return i;
+ *dest++ = (ch>>6) | 0xC0;
+ *dest++ = (ch & 0x3F) | 0x80;
+ }
+ else if (ch < 0x10000) {
+ if (dest >= dest_end-2)
+ return i;
+ *dest++ = (ch>>12) | 0xE0;
+ *dest++ = ((ch>>6) & 0x3F) | 0x80;
+ *dest++ = (ch & 0x3F) | 0x80;
+ }
+ else if (ch < 0x110000) {
+ if (dest >= dest_end-3)
+ return i;
+ *dest++ = (ch>>18) | 0xF0;
+ *dest++ = ((ch>>12) & 0x3F) | 0x80;
+ *dest++ = ((ch>>6) & 0x3F) | 0x80;
+ *dest++ = (ch & 0x3F) | 0x80;
+ }
+ i++;
+ }
+ if (dest < dest_end)
+ *dest = '\0';
+ return i;
+}
+
+int u8_wc_toutf8(char *dest, uint32_t ch)
+{
+ if (ch < 0x80) {
+ dest[0] = (char)ch;
+ return 1;
+ }
+ if (ch < 0x800) {
+ dest[0] = (ch>>6) | 0xC0;
+ dest[1] = (ch & 0x3F) | 0x80;
+ return 2;
+ }
+ if (ch < 0x10000) {
+ dest[0] = (ch>>12) | 0xE0;
+ dest[1] = ((ch>>6) & 0x3F) | 0x80;
+ dest[2] = (ch & 0x3F) | 0x80;
+ return 3;
+ }
+ if (ch < 0x110000) {
+ dest[0] = (ch>>18) | 0xF0;
+ dest[1] = ((ch>>12) & 0x3F) | 0x80;
+ dest[2] = ((ch>>6) & 0x3F) | 0x80;
+ dest[3] = (ch & 0x3F) | 0x80;
+ return 4;
+ }
+ return 0;
+}
+
+/* charnum => byte offset */
+int u8_offset(char *str, int charnum)
+{
+ int offs=0;
+
+ while (charnum > 0 && str[offs]) {
+ (void)(isutf(str[++offs]) || isutf(str[++offs]) ||
+ isutf(str[++offs]) || ++offs);
+ charnum--;
+ }
+ return offs;
+}
+
+/* byte offset => charnum */
+int u8_charnum(char *s, int offset)
+{
+ int charnum = 0, offs=0;
+
+ while (offs < offset && s[offs]) {
+ (void)(isutf(s[++offs]) || isutf(s[++offs]) ||
+ isutf(s[++offs]) || ++offs);
+ charnum++;
+ }
+ return charnum;
+}
+
+/* number of characters */
+int u8_strlen(char *s)
+{
+ int count = 0;
+ int i = 0;
+
+ while (u8_nextchar(s, &i) != 0)
+ count++;
+
+ return count;
+}
+
+/* reads the next utf-8 sequence out of a string, updating an index */
+uint32_t u8_nextchar(const char *s, int *i)
+{
+ uint32_t ch = 0;
+ int sz = 0;
+
+ do {
+ ch <<= 6;
+ ch += (unsigned char)s[(*i)++];
+ sz++;
+ } while (s[*i] && !isutf(s[*i]));
+ ch -= offsetsFromUTF8[sz-1];
+
+ return ch;
+}
+
+void u8_inc(char *s, int *i)
+{
+ (void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) ||
+ isutf(s[++(*i)]) || ++(*i));
+}
+
+void u8_dec(char *s, int *i)
+{
+ (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) ||
+ isutf(s[--(*i)]) || --(*i));
+}
+
+int octal_digit(char c)
+{
+ return (c >= '0' && c <= '7');
+}
+
+int hex_digit(char c)
+{
+ return ((c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'F') ||
+ (c >= 'a' && c <= 'f'));
+}
+
+/* assumes that src points to the character after a backslash
+ returns number of input characters processed */
+int u8_read_escape_sequence(char *str, uint32_t *dest)
+{
+ uint32_t ch;
+ char digs[9]="\0\0\0\0\0\0\0\0";
+ int dno=0, i=1;
+
+ ch = (uint32_t)str[0]; /* take literal character */
+ if (str[0] == 'n')
+ ch = L'\n';
+ else if (str[0] == 't')
+ ch = L'\t';
+ else if (str[0] == 'r')
+ ch = L'\r';
+ else if (str[0] == 'b')
+ ch = L'\b';
+ else if (str[0] == 'f')
+ ch = L'\f';
+ else if (str[0] == 'v')
+ ch = L'\v';
+ else if (str[0] == 'a')
+ ch = L'\a';
+ else if (octal_digit(str[0])) {
+ i = 0;
+ do {
+ digs[dno++] = str[i++];
+ } while (octal_digit(str[i]) && dno < 3);
+ ch = strtol(digs, NULL, 8);
+ }
+ else if (str[0] == 'x') {
+ while (hex_digit(str[i]) && dno < 2) {
+ digs[dno++] = str[i++];
+ }
+ if (dno > 0)
+ ch = strtol(digs, NULL, 16);
+ }
+ else if (str[0] == 'u') {
+ while (hex_digit(str[i]) && dno < 4) {
+ digs[dno++] = str[i++];
+ }
+ if (dno > 0)
+ ch = strtol(digs, NULL, 16);
+ }
+ else if (str[0] == 'U') {
+ while (hex_digit(str[i]) && dno < 8) {
+ digs[dno++] = str[i++];
+ }
+ if (dno > 0)
+ ch = strtol(digs, NULL, 16);
+ }
+ *dest = ch;
+
+ return i;
+}
+
+/* convert a string with literal \uxxxx or \Uxxxxxxxx characters to UTF-8
+ example: u8_unescape(mybuf, 256, "hello\\u220e")
+ note the double backslash is needed if called on a C string literal */
+int u8_unescape(char *buf, int sz, char *src)
+{
+ int c=0, amt;
+ uint32_t ch;
+ char temp[4];
+
+ while (*src && c < sz) {
+ if (*src == '\\') {
+ src++;
+ amt = u8_read_escape_sequence(src, &ch);
+ }
+ else {
+ ch = (uint32_t)*src;
+ amt = 1;
+ }
+ src += amt;
+ amt = u8_wc_toutf8(temp, ch);
+ if (amt > sz-c)
+ break;
+ memcpy(&buf[c], temp, amt);
+ c += amt;
+ }
+ if (c < sz)
+ buf[c] = '\0';
+ return c;
+}
+
+int u8_escape_wchar(char *buf, int sz, uint32_t ch)
+{
+ if (ch == L'\n')
+ return snprintf(buf, sz, "\\n");
+ else if (ch == L'\t')
+ return snprintf(buf, sz, "\\t");
+ else if (ch == L'\r')
+ return snprintf(buf, sz, "\\r");
+ else if (ch == L'\b')
+ return snprintf(buf, sz, "\\b");
+ else if (ch == L'\f')
+ return snprintf(buf, sz, "\\f");
+ else if (ch == L'\v')
+ return snprintf(buf, sz, "\\v");
+ else if (ch == L'\a')
+ return snprintf(buf, sz, "\\a");
+ else if (ch == L'\\')
+ return snprintf(buf, sz, "\\\\");
+ else if (ch < 32 || ch == 0x7f)
+ return snprintf(buf, sz, "\\x%hhX", (unsigned char)ch);
+ else if (ch > 0xFFFF)
+ return snprintf(buf, sz, "\\U%.8X", (uint32_t)ch);
+ else if (ch >= 0x80 && ch <= 0xFFFF)
+ return snprintf(buf, sz, "\\u%.4hX", (unsigned short)ch);
+
+ return snprintf(buf, sz, "%c", (char)ch);
+}
+
+int u8_escape(char *buf, int sz, char *src, int escape_quotes)
+{
+ int c=0, i=0, amt;
+
+ while (src[i] && c < sz) {
+ if (escape_quotes && src[i] == '"') {
+ amt = snprintf(buf, sz - c, "\\\"");
+ i++;
+ }
+ else {
+ amt = u8_escape_wchar(buf, sz - c, u8_nextchar(src, &i));
+ }
+ c += amt;
+ buf += amt;
+ }
+ if (c < sz)
+ *buf = '\0';
+ return c;
+}
+
+char *u8_strchr(char *s, uint32_t ch, int *charn)
+{
+ int i = 0, lasti=0;
+ uint32_t c;
+
+ *charn = 0;
+ while (s[i]) {
+ c = u8_nextchar(s, &i);
+ if (c == ch) {
+ return &s[lasti];
+ }
+ lasti = i;
+ (*charn)++;
+ }
+ return NULL;
+}
+
+char *u8_memchr(char *s, uint32_t ch, size_t sz, int *charn)
+{
+ int i = 0, lasti=0;
+ uint32_t c;
+ int csz;
+
+ *charn = 0;
+ while (i < sz) {
+ c = csz = 0;
+ do {
+ c <<= 6;
+ c += (unsigned char)s[i++];
+ csz++;
+ } while (i < sz && !isutf(s[i]));
+ c -= offsetsFromUTF8[csz-1];
+
+ if (c == ch) {
+ return &s[lasti];
+ }
+ lasti = i;
+ (*charn)++;
+ }
+ return NULL;
+}
+
+int u8_is_locale_utf8(char *locale)
+{
+ /* this code based on libutf8 */
+ const char* cp = locale;
+
+ for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++) {
+ if (*cp == '.') {
+ const char* encoding = ++cp;
+ for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++)
+ ;
+ if ((cp-encoding == 5 && !strncmp(encoding, "UTF-8", 5))
+ || (cp-encoding == 4 && !strncmp(encoding, "utf8", 4)))
+ return 1; /* it's UTF-8 */
+ break;
+ }
+ }
+ return 0;
+}
+
+int u8_vprintf(char *fmt, va_list ap)
+{
+ int cnt, sz=0;
+ char *buf;
+ uint32_t *wcs;
+
+ sz = 512;
+ buf = (char*)alloca(sz);
+ try_print:
+ cnt = vsnprintf(buf, sz, fmt, ap);
+ if (cnt >= sz) {
+ buf = (char*)alloca(cnt - sz + 1);
+ sz = cnt + 1;
+ goto try_print;
+ }
+ wcs = (uint32_t*)alloca((cnt+1) * sizeof(uint32_t));
+ cnt = u8_toucs(wcs, cnt+1, buf, cnt);
+ printf("%ls", (wchar_t*)wcs);
+ return cnt;
+}
+
+int u8_printf(char *fmt, ...)
+{
+ int cnt;
+ va_list args;
+
+ va_start(args, fmt);
+
+ cnt = u8_vprintf(fmt, args);
+
+ va_end(args);
+ return cnt;
+}
+
+
+// This function checks a string to see if it's valid UTF-8
+// It returns true if the string is valid.
+//
+// based on the valid_utf8 routine from the PCRE library by Philip Hazel
+
+int u8_isvalid(const char *str)
+{
+ const unsigned char *p;
+ unsigned char c;
+ int ab;
+
+ for (p = (unsigned char*)str; *p; p++) {
+ c = *p;
+ if (c < 128)
+ continue;
+ if ((c & 0xc0) != 0xc0)
+ return 0;
+ ab = trailingBytesForUTF8[c];
+
+ p++;
+ /* Check top bits in the second byte */
+ if ((*p & 0xc0) != 0x80)
+ return 0;
+
+ /* Check for overlong sequences for each different length */
+ switch (ab) {
+ /* Check for xx00 000x */
+ case 1:
+ if ((c & 0x3e) == 0) return 0;
+ continue; /* We know there aren't any more bytes to check */
+
+ /* Check for 1110 0000, xx0x xxxx */
+ case 2:
+ if (c == 0xe0 && (*p & 0x20) == 0) return 0;
+ break;
+
+ /* Check for 1111 0000, xx00 xxxx */
+ case 3:
+ if (c == 0xf0 && (*p & 0x30) == 0) return 0;
+ break;
+
+ /* Check for 1111 1000, xx00 0xxx */
+ case 4:
+ if (c == 0xf8 && (*p & 0x38) == 0) return 0;
+ break;
+
+ /* Check for leading 0xfe or 0xff,
+ and then for 1111 1100, xx00 00xx */
+ case 5:
+ if (c == 0xfe || c == 0xff ||
+ (c == 0xfc && (*p & 0x3c) == 0)) return 0;
+ break;
+ }
+
+ /* Check for valid bytes after the 2nd, if any; all must start 10 */
+ while (--ab > 0) {
+ if ((*(++p) & 0xc0) != 0x80) return 0;
+ }
+ }
+
+ return 1;
+}
+
diff --git a/engines/sludge/CommonCode/utf8.h b/engines/sludge/CommonCode/utf8.h
new file mode 100644
index 0000000000..85989eec37
--- /dev/null
+++ b/engines/sludge/CommonCode/utf8.h
@@ -0,0 +1,75 @@
+#include <stdint.h>
+#include <stdarg.h>
+
+/* is c the start of a utf8 sequence? */
+#define isutf(c) (((c)&0xC0)!=0x80)
+
+/* convert UTF-8 data to wide character */
+int u8_toucs(uint32_t *dest, int sz, char *src, int srcsz);
+
+/* the opposite conversion */
+int u8_toutf8(char *dest, int sz, uint32_t *src, int srcsz);
+
+/* single character to UTF-8 */
+int u8_wc_toutf8(char *dest, uint32_t ch);
+
+/* character number to byte offset */
+int u8_offset(char *str, int charnum);
+
+/* byte offset to character number */
+int u8_charnum(char *s, int offset);
+
+/* return next character, updating an index variable */
+uint32_t u8_nextchar(const char *s, int *i);
+
+/* move to next character */
+void u8_inc(char *s, int *i);
+
+/* move to previous character */
+void u8_dec(char *s, int *i);
+
+/* returns length of next utf-8 sequence */
+int u8_seqlen(char *s);
+
+/* assuming src points to the character after a backslash, read an
+ escape sequence, storing the result in dest and returning the number of
+ input characters processed */
+int u8_read_escape_sequence(char *src, uint32_t *dest);
+
+/* given a wide character, convert it to an ASCII escape sequence stored in
+ buf, where buf is "sz" bytes. returns the number of characters output. */
+int u8_escape_wchar(char *buf, int sz, uint32_t ch);
+
+/* convert a string "src" containing escape sequences to UTF-8 */
+int u8_unescape(char *buf, int sz, char *src);
+
+/* convert UTF-8 "src" to ASCII with escape sequences.
+ if escape_quotes is nonzero, quote characters will be preceded by
+ backslashes as well. */
+int u8_escape(char *buf, int sz, char *src, int escape_quotes);
+
+/* utility predicates used by the above */
+int octal_digit(char c);
+int hex_digit(char c);
+
+/* return a pointer to the first occurrence of ch in s, or NULL if not
+ found. character index of found character returned in *charn. */
+char *u8_strchr(char *s, uint32_t ch, int *charn);
+
+/* same as the above, but searches a buffer of a given size instead of
+ a NUL-terminated string. */
+char *u8_memchr(char *s, uint32_t ch, size_t sz, int *charn);
+
+/* count the number of characters in a UTF-8 string */
+int u8_strlen(char *s);
+
+int u8_is_locale_utf8(char *locale);
+
+/* printf where the format string and arguments may be in UTF-8.
+ you can avoid this function and just use ordinary printf() if the current
+ locale is UTF-8. */
+int u8_vprintf(char *fmt, va_list ap);
+int u8_printf(char *fmt, ...);
+
+
+int u8_isvalid(const char *input);
diff --git a/engines/sludge/CommonCode/version.h b/engines/sludge/CommonCode/version.h
new file mode 100644
index 0000000000..2b640a0656
--- /dev/null
+++ b/engines/sludge/CommonCode/version.h
@@ -0,0 +1,13 @@
+#define MAJOR_VERSION 2
+#define MINOR_VERSION 2
+#define RELEASE_VERSION 1
+#define BUILD_VERSION 208
+#define TEXT_VERSION "2.2.1"
+#define WHOLE_VERSION (MAJOR_VERSION * 256 + MINOR_VERSION) // This version
+#define MINIM_VERSION (1 * 256 + 2) // Earliest version of games the engine can run
+
+#define COPYRIGHT_TEXT "\251 Hungry Software and contributors 2000-2014"
+
+#define VERSION(a,b) (a * 256 + b)
+
+extern int gameVersion;
diff --git a/engines/sludge/allfiles.h b/engines/sludge/allfiles.h
new file mode 100644
index 0000000000..9478da56ee
--- /dev/null
+++ b/engines/sludge/allfiles.h
@@ -0,0 +1,32 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SLUDGE_ALLFILES_H
+#define SLUDGE_ALLFILES_H
+
+#include "common/system.h"
+
+//#define debuggy2 int
+#define IN_THE_CENTRE 65535
+typedef unsigned char byte;
+
+#endif
diff --git a/engines/sludge/backdrop.cpp b/engines/sludge/backdrop.cpp
new file mode 100644
index 0000000000..ed7177035e
--- /dev/null
+++ b/engines/sludge/backdrop.cpp
@@ -0,0 +1,1753 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if 0
+#if defined __unix__ && !(defined __APPLE__)
+#include <png.h>
+#else
+#include <libpng/png.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#if !defined(HAVE_GLES2)
+#include "GLee.h"
+#else
+#include <GLES2/gl2.h>
+#endif
+#endif
+
+#include "allfiles.h"
+#include "debug.h"
+#include "newfatal.h"
+#include "colours.h"
+#include "fileset.h"
+#include "cursors.h"
+#include "backdrop.h"
+#include "language.h"
+#include "moreio.h"
+#include "variable.h"
+#include "zbuffer.h"
+#include "graphics.h"
+#include "line.h"
+#include "people.h"
+#include "talk.h"
+#include "sludger.h"
+#include "statusba.h"
+
+#include "CommonCode/version.h"
+
+extern inputType input;
+
+
+bool freeze();
+void unfreeze(bool); // Because FREEZE.H needs a load of other includes
+
+#if 0
+GLubyte *backdropTexture = NULL;
+GLuint backdropTextureName = 0;
+bool backdropExists = false;
+GLfloat backdropTexW = 1.0;
+GLfloat backdropTexH = 1.0;
+
+texture lightMap;
+
+GLuint snapshotTextureName = 0;
+#endif
+
+float snapTexW = 1.0;
+float snapTexH = 1.0;
+
+int lightMapMode = LIGHTMAPMODE_PIXEL;
+parallaxLayer *parallaxStuff = NULL;
+int cameraPX = 0, cameraPY = 0;
+
+unsigned int sceneWidth, sceneHeight;
+int lightMapNumber;
+unsigned int currentBlankColour = makeColour(0, 0, 0);
+
+extern int cameraX, cameraY;
+extern float cameraZoom;
+
+void nosnapshot() {
+#if 0
+ deleteTextures(1, &snapshotTextureName);
+ snapshotTextureName = 0;
+#endif
+}
+
+#if ALLOW_FILE
+void saveSnapshot(FILE *fp) {
+ if (snapshotTextureName) {
+ fputc(1, fp); // 1 for snapshot follows
+ saveCoreHSI(fp, snapshotTextureName, winWidth, winHeight);
+ } else {
+ fputc(0, fp);
+ }
+}
+#endif
+
+bool snapshot() {
+
+ nosnapshot();
+ if (! freeze()) return false;
+#if 0
+ setPixelCoords(true);
+ glGenTextures(1, &snapshotTextureName);
+
+ int w = winWidth;
+ int h = winHeight;
+ if (! NPOT_textures) {
+ w = getNextPOT(winWidth);
+ h = getNextPOT(winHeight);
+ snapTexW = ((double)winWidth) / w;
+ snapTexH = ((double)winHeight) / h;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, snapshotTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, snapshotTextureName);
+
+ // Render scene
+ glDepthMask(GL_TRUE);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen
+ glDepthMask(GL_FALSE);
+
+ drawBackDrop(); // Draw the room
+ drawZBuffer(cameraX, cameraY, false);
+
+ glEnable(GL_DEPTH_TEST);
+
+ drawPeople(); // Then add any moving characters...
+
+ glDisable(GL_DEPTH_TEST);
+
+ viewSpeech(); // ...and anything being said
+ drawStatusBar();
+ // Copy Our ViewPort To The Texture
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, viewportOffsetX, viewportOffsetY, winWidth, winHeight, snapshotTextureName);
+
+ setPixelCoords(false);
+#endif
+ unfreeze(false);
+ return true;
+}
+
+#if ALLOW_FILE
+bool restoreSnapshot(FILE *fp) {
+ unsigned int picWidth = get2bytes(fp);
+ unsigned int picHeight = get2bytes(fp);
+
+ if ((picWidth != winWidth) || (picHeight != winHeight))
+ return false;
+
+ unsigned int t1, t2, n;
+ unsigned short c;
+
+ GLubyte *target;
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(picWidth);
+ picHeight = getNextPOT(picHeight);
+ snapTexW = ((double)winWidth) / picWidth;
+ snapTexH = ((double)winHeight) / picHeight;
+ }
+ GLubyte *snapshotTexture = new GLubyte [picHeight * picWidth * 4];
+ if (! snapshotTexture) return fatal("Out of memory while restoring snapshot.");
+
+ for (t2 = 0; t2 < winHeight; t2 ++) {
+ t1 = 0;
+ while (t1 < winWidth) {
+ c = (unsigned short) get2bytes(fp);
+ if (c & 32) {
+ n = fgetc(fp) + 1;
+ c -= 32;
+ } else {
+ n = 1;
+ }
+ while (n --) {
+ target = snapshotTexture + 4 * picWidth * t2 + t1 * 4;
+ target[0] = (GLubyte) redValue(c);
+ target[1] = (GLubyte) greenValue(c);
+ target[2] = (GLubyte) blueValue(c);
+ target[3] = (GLubyte) 255;
+ t1++;
+ }
+ }
+ }
+
+ if (! snapshotTextureName) glGenTextures(1, &snapshotTextureName);
+ glBindTexture(GL_TEXTURE_2D, snapshotTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, snapshotTexture, snapshotTextureName);
+
+ delete snapshotTexture;
+ snapshotTexture = NULL;
+
+ return true;
+}
+#endif
+
+void killBackDrop() {
+#if 0
+ deleteTextures(1, &backdropTextureName);
+ backdropTextureName = 0;
+ backdropExists = false;
+#endif
+}
+
+void killLightMap() {
+#if 0
+ deleteTextures(1, &lightMap.name);
+ lightMap.name = 0;
+ if (lightMap.data) {
+ delete lightMap.data;
+ lightMap.data = NULL;
+ }
+ lightMapNumber = 0;
+#endif
+}
+
+void killParallax() {
+#if 0
+ while (parallaxStuff) {
+
+ parallaxLayer *k = parallaxStuff;
+ parallaxStuff = k -> next;
+
+ // Now kill the image
+ deleteTextures(1, &k -> textureName);
+ delete k -> texture;
+ delete k;
+ k = NULL;
+ }
+#endif
+}
+
+bool reserveBackdrop() {
+ cameraX = 0;
+ cameraY = 0;
+ input.mouseX = (int)((float)input.mouseX * cameraZoom);
+ input.mouseY = (int)((float)input.mouseY * cameraZoom);
+ cameraZoom = 1.0;
+ input.mouseX = (int)((float)input.mouseX / cameraZoom);
+ input.mouseY = (int)((float)input.mouseY / cameraZoom);
+ setPixelCoords(false);
+ int picWidth = sceneWidth;
+ int picHeight = sceneHeight;
+#if 0
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ if (backdropTexture) delete backdropTexture;
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(sceneWidth);
+ picHeight = getNextPOT(sceneHeight);
+ backdropTexW = ((double)sceneWidth) / picWidth;
+ backdropTexH = ((double)sceneHeight) / picHeight;
+ }
+ backdropTexture = new GLubyte [picWidth * picHeight * 4];
+ if (! checkNew(backdropTexture)) return false;
+
+ if (! backdropTextureName) glGenTextures(1, &backdropTextureName);
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, backdropTexture, backdropTextureName);
+#endif
+ return true;
+}
+
+bool resizeBackdrop(int x, int y) {
+ killLightMap();
+ killBackDrop();
+ killParallax();
+ killZBuffer();
+ sceneWidth = x;
+ sceneHeight = y;
+ return reserveBackdrop();
+}
+
+void loadBackDrop(int fileNum, int x, int y) {
+ setResourceForFatal(fileNum);
+#if ALLOW_FILE
+ if (! openFileFromNum(fileNum)) {
+ fatal("Can't load overlay image");
+ return;
+ }
+
+ if (! loadHSI(bigDataFile, x, y, false)) {
+ char mess[200];
+ sprintf(mess, "Can't paste overlay image outside scene dimensions\n\nX = %i\nY = %i\nWidth = %i\nHeight = %i", x, y, sceneWidth, sceneHeight);
+ fatal(mess);
+ }
+
+ finishAccess();
+#endif
+ setResourceForFatal(-1);
+}
+
+void mixBackDrop(int fileNum, int x, int y) {
+#if ALLOW_FILE
+ setResourceForFatal(fileNum);
+ if (! openFileFromNum(fileNum)) {
+ fatal("Can't load overlay image");
+ return;
+ }
+
+ if (! mixHSI(bigDataFile, x, y)) {
+ fatal("Can't paste overlay image outside screen dimensions");
+ }
+
+ finishAccess();
+#endif
+ setResourceForFatal(-1);
+}
+
+void blankScreen(int x1, int y1, int x2, int y2) {
+
+ if (y1 < 0) y1 = 0;
+ if (x1 < 0) x1 = 0;
+ if (x2 > (int) sceneWidth) x2 = (int)sceneWidth;
+ if (y2 > (int) sceneHeight) y2 = (int)sceneHeight;
+
+ int picWidth = x2 - x1;
+ int picHeight = y2 - y1;
+#if 0
+ setPixelCoords(true);
+
+ int xoffset = 0;
+ while (xoffset < picWidth) {
+ int w = (picWidth - xoffset < viewportWidth) ? picWidth - xoffset : viewportWidth;
+
+ int yoffset = 0;
+ while (yoffset < picHeight) {
+ int h = (picHeight - yoffset < viewportHeight) ? picHeight - yoffset : viewportHeight;
+
+ // Render the scene
+
+
+
+ const GLfloat vertices[] = {
+ -10.325f, -1.325f, 0.0f,
+ w + 1.325f, -1.325f, 0.0f,
+ -10.325f, h + 1.325f, 0.0f,
+ w + 1.325f, h + 1.325f, 0.0f
+ };
+
+ glUseProgram(shader.color);
+
+ setPMVMatrix(shader.color);
+ setPrimaryColor(redValue(currentBlankColour) / 255.0f, greenValue(currentBlankColour) / 255.0f, blueValue(currentBlankColour) / 255.0f, 1.0f);
+ drawQuad(shader.color, vertices, 0);
+
+ glUseProgram(0);
+
+ // Copy Our ViewPort To The Texture
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, x1 + xoffset, y1 + yoffset, viewportOffsetX, viewportOffsetY, w, h, backdropTextureName);
+
+ yoffset += viewportHeight;
+ }
+ xoffset += viewportWidth;
+ }
+
+ setPixelCoords(false);
+#endif
+}
+
+void hardScroll(int distance) {
+ if (abs(distance) >= sceneHeight) {
+ blankScreen(0, 0, sceneWidth, sceneHeight);
+ return;
+ }
+
+ if (! distance) return;
+#if 0
+ const GLfloat backdropTexCoords[] = {
+ 0.0f, 0.0f,
+ backdropTexW, 0.0f,
+ 0.0f, backdropTexH,
+ backdropTexW, backdropTexH
+ };
+
+ setPixelCoords(true);
+
+ unsigned int xoffset = 0;
+ while (xoffset < sceneWidth) {
+ int w = (sceneWidth - xoffset < viewportWidth) ? sceneWidth - xoffset : viewportWidth;
+
+ unsigned int yoffset = 0;
+ while (yoffset < sceneHeight) {
+ int h = (sceneHeight - yoffset < viewportHeight) ? sceneHeight - yoffset : viewportHeight;
+
+
+ glClear(GL_COLOR_BUFFER_BIT); // Clear The Screen
+
+ // Render the backdrop
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ const GLfloat vertices[] = {
+ (GLfloat) - xoffset, (GLfloat) - distance - yoffset, 0.,
+ (GLfloat)sceneWidth - xoffset, (GLfloat) - distance - yoffset, 0.,
+ (GLfloat) - xoffset, (GLfloat)sceneHeight - distance - yoffset, 0.,
+ (GLfloat)sceneWidth - xoffset, (GLfloat)sceneHeight - distance - yoffset, 0.
+ };
+
+
+ glUseProgram(shader.texture);
+
+ setPMVMatrix(shader.texture);
+
+ drawQuad(shader.texture, vertices, 1, backdropTexCoords);
+
+ glUseProgram(0);
+
+ // Copy Our ViewPort To The Texture
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, viewportOffsetX, viewportOffsetY, w, h, backdropTextureName);
+
+ yoffset += viewportHeight;
+ }
+ xoffset += viewportWidth;
+ }
+ setPixelCoords(false);
+#endif
+}
+
+void drawVerticalLine(unsigned int x, unsigned int y1, unsigned int y2) {
+ drawLine(x, y1, x, y2);
+}
+
+void drawHorizontalLine(unsigned int x1, unsigned int y, unsigned int x2) {
+ drawLine(x1, y, x2, y);
+}
+
+void darkScreen() {
+ setPixelCoords(true);
+
+ int xoffset = 0;
+ while (xoffset < sceneWidth) {
+ int w = (sceneWidth - xoffset < viewportWidth) ? sceneWidth - xoffset : viewportWidth;
+
+ int yoffset = 0;
+ while (yoffset < sceneHeight) {
+ int h = (sceneHeight - yoffset < viewportHeight) ? sceneHeight - yoffset : viewportHeight;
+
+ // Render the scene - first the old backdrop
+#if 0
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ const GLfloat vertices[] = {
+ (GLfloat) - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat)sceneWidth - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat) - xoffset, (GLfloat)sceneHeight - yoffset, 0.,
+ (GLfloat)sceneWidth - xoffset, (GLfloat)sceneHeight - yoffset, 0.
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ backdropTexW, 0.0f,
+ 0.0f, backdropTexH,
+ backdropTexW, backdropTexH
+ };
+
+
+ glUseProgram(shader.texture);
+
+ setPMVMatrix(shader.texture);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+
+ // Then the darkness
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+
+ glEnable(GL_BLEND);
+
+ glUseProgram(shader.color);
+
+ setPMVMatrix(shader.color);
+ setPrimaryColor(0.0f, 0.0f, 0.0f, 0.5f);
+ drawQuad(shader.color, vertices, 0);
+
+ glUseProgram(0);
+
+ glDisable(GL_BLEND);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+ // Copy Our ViewPort To The Texture
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, viewportOffsetX, viewportOffsetY, w, h, backdropTextureName);
+
+ yoffset += h;
+
+ glClear(GL_COLOR_BUFFER_BIT);
+#endif
+ }
+ xoffset += w;
+ }
+
+
+ setPixelCoords(false);
+}
+
+
+
+inline int sortOutPCamera(int cX, int fX, int sceneMax, int boxMax) {
+ return (fX == 65535) ?
+ (sceneMax ? ((cX * boxMax) / sceneMax) : 0)
+ :
+ ((cX * fX) / 100);
+}
+
+
+void drawBackDrop() {
+#if 0
+ setPrimaryColor(1.0, 1.0, 1.0, 1.0);
+
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glEnable(GL_BLEND);
+
+ glUseProgram(shader.smartScaler);
+ GLuint uniform = glGetUniformLocation(shader.smartScaler, "useLightTexture");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+
+ setPMVMatrix(shader.smartScaler);
+
+ if (gameSettings.antiAlias == 1) {
+ glUniform1i(glGetUniformLocation(shader.smartScaler, "antialias"), 1);
+ } else {
+ glUniform1i(glGetUniformLocation(shader.smartScaler, "antialias"), 0);
+ }
+
+ if (parallaxStuff) {
+ parallaxLayer *ps = parallaxStuff;
+
+ while (ps->next) ps = ps->next;
+
+ while (ps) {
+ ps -> cameraX = sortOutPCamera(cameraX, ps -> fractionX, (int)(sceneWidth - (float)winWidth / cameraZoom), (int)(ps -> width - (float)winWidth / cameraZoom));
+ ps -> cameraY = sortOutPCamera(cameraY, ps -> fractionY, (int)(sceneHeight - (float)winHeight / cameraZoom), (int)(ps -> height - (float)winHeight / cameraZoom));
+ glBindTexture(GL_TEXTURE_2D, ps->textureName);
+
+ float w = (ps->wrapS) ? sceneWidth : ps->width;
+ float h = (ps->wrapT) ? sceneHeight : ps->height;
+ float texw;
+ float texh;
+ if (! NPOT_textures) {
+ texw = (ps->wrapS) ? (float) sceneWidth / ps->width : (float) ps->width / getNextPOT(ps->width);
+ texh = (ps->wrapT) ? (float) sceneHeight / ps->height : (float) ps->height / getNextPOT(ps->height);
+ } else {
+ texw = (ps->wrapS) ? (float) sceneWidth / ps->width : 1.0;
+ texh = (ps->wrapT) ? (float) sceneHeight / ps->height : 1.0;
+ }
+
+ const GLfloat vertices[] = {
+ (GLfloat) - ps -> cameraX, (GLfloat) - ps -> cameraY, 0.1f,
+ w - ps -> cameraX, (GLfloat) - ps -> cameraY, 0.1f,
+ (GLfloat) - ps -> cameraX, h - ps -> cameraY, 0.1f,
+ w - ps -> cameraX, h - ps -> cameraY, 0.1f
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ texw, 0.0f,
+ 0.0f, texh,
+ texw, texh
+ };
+ drawQuad(shader.smartScaler, vertices, 1, texCoords);
+
+ ps = ps -> prev;
+ }
+ }
+
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+
+ const GLfloat backdropTexCoords[] = {
+ 0.0f, 0.0f,
+ backdropTexW, 0.0f,
+ 0.0f, backdropTexH,
+ backdropTexW, backdropTexH
+ };
+
+ const GLfloat vertices[] = {
+ (GLfloat) - cameraX, (GLfloat) - cameraY, 0.,
+ (GLfloat)sceneWidth - (GLfloat)cameraX, (GLfloat) - cameraY, 0.,
+ (GLfloat) - cameraX, (GLfloat)sceneHeight - (GLfloat)cameraY, 0.,
+ (GLfloat)sceneWidth - (GLfloat)cameraX, (GLfloat)sceneHeight - (GLfloat)cameraY, 0.
+ };
+
+ drawQuad(shader.smartScaler, vertices, 1, backdropTexCoords);
+
+ glDisable(GL_BLEND);
+
+ glUseProgram(0);
+#endif
+}
+
+bool loadLightMap(int v) {
+ int newPicWidth, newPicHeight;
+#if ALLOW_FILE
+ setResourceForFatal(v);
+ if (! openFileFromNum(v)) return fatal("Can't open light map.");
+ long file_pointer = ftell(bigDataFile);
+
+ png_structp png_ptr;
+ png_infop info_ptr, end_info;
+
+
+ int fileIsPNG = true;
+
+ // Is this a PNG file?
+
+ char tmp[10];
+ size_t bytes_read = fread(tmp, 1, 8, bigDataFile);
+ if (bytes_read != 8 && ferror(bigDataFile)) {
+ debugOut("Reading error in loadLightMap.\n");
+ }
+
+ if (png_sig_cmp((png_byte *) tmp, 0, 8)) {
+ // No, it's old-school HSI
+ fileIsPNG = false;
+ fseek(bigDataFile, file_pointer, SEEK_SET);
+
+ newPicWidth = lightMap.w = get2bytes(bigDataFile);
+ newPicHeight = lightMap.h = get2bytes(bigDataFile);
+ } else {
+ // Read the PNG header
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
+ return false;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ return false;
+ }
+ png_init_io(png_ptr, bigDataFile); // Tell libpng which file to read
+ png_set_sig_bytes(png_ptr, 8); // 8 bytes already read
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, compression_type, filter_method;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ newPicWidth = lightMap.w = width;
+ newPicHeight = lightMap.h = height;
+
+ if (bit_depth < 8) png_set_packing(png_ptr);
+ png_set_expand(png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr);
+ if (bit_depth == 16) png_set_strip_16(png_ptr);
+
+ png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
+
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ //int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ }
+
+ if (lightMapMode == LIGHTMAPMODE_HOTSPOT) {
+ if (lightMap.w != sceneWidth || lightMap.h != sceneHeight) {
+ return fatal("Light map width and height don't match scene width and height. That is required for lightmaps in HOTSPOT mode.");
+ }
+ }
+
+ if (! NPOT_textures) {
+ newPicWidth = getNextPOT(lightMap.w);
+ newPicHeight = getNextPOT(lightMap.h);
+ lightMap.texW = (double) lightMap.w / newPicWidth;
+ lightMap.texH = (double) lightMap.h / newPicHeight;
+ } else {
+ lightMap.texW = 1.0;
+ lightMap.texH = 1.0;
+ }
+
+ killLightMap();
+ lightMapNumber = v;
+#if 0
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+#endif
+ if (lightMap.data) delete [] lightMap.data;
+
+ lightMap.data = new GLubyte [newPicWidth * newPicHeight * 4];
+ if (! lightMap.data) {
+ return fatal("Out of memory loading light map.");
+ }
+
+ int t1, t2, n;
+ unsigned short c;
+ GLubyte *target;
+
+ if (fileIsPNG) {
+ unsigned char *row_pointers[lightMap.h];
+ for (int i = 0; i < lightMap.h; i++)
+ row_pointers[i] = lightMap.data + 4 * i * newPicWidth;
+
+ png_read_image(png_ptr, (png_byte **) row_pointers);
+ png_read_end(png_ptr, NULL);
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ } else {
+
+ for (t2 = 0; t2 < lightMap.h; t2 ++) {
+ t1 = 0;
+ while (t1 < lightMap.w) {
+ c = (unsigned short) get2bytes(bigDataFile);
+ if (c & 32) {
+ n = fgetc(bigDataFile) + 1;
+ c -= 32;
+ } else {
+ n = 1;
+ }
+ while (n --) {
+ target = lightMap.data + 4 * newPicWidth * t2 + t1 * 4;
+ target[0] = (GLubyte) redValue(c);
+ target[1] = (GLubyte) greenValue(c);
+ target[2] = (GLubyte) blueValue(c);
+ target[3] = (GLubyte) 255;
+ t1++;
+ }
+ }
+ }
+ }
+#if 0
+ if (! lightMap.name) glGenTextures(1, &lightMap.name);
+ glBindTexture(GL_TEXTURE_2D, lightMap.name);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+#endif
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newPicWidth, newPicHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, lightMap.data, lightMap.name);
+
+ finishAccess();
+
+#endif
+ setResourceForFatal(-1);
+
+ return true;
+}
+
+void reloadParallaxTextures() {
+#if 0
+ parallaxLayer *nP = parallaxStuff;
+ if (! nP) return;
+
+ while (nP) {
+ //fprintf (stderr, "Reloading parallax. (%d, %d) ", nP->width, nP->height);
+ nP->textureName = 0;
+
+ glGenTextures(1, &nP->textureName);
+ glBindTexture(GL_TEXTURE_2D, nP->textureName);
+ if (nP -> wrapS)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ else
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ if (nP -> wrapT)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ else
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+
+ if (! NPOT_textures) {
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getNextPOT(nP->width), getNextPOT(nP->height), 0, GL_RGBA, GL_UNSIGNED_BYTE, nP->texture, nP->textureName);
+ } else {
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nP->width, nP->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nP->texture, nP->textureName);
+ }
+
+ nP = nP->next;
+ }
+#endif
+}
+
+bool loadParallax(unsigned short v, unsigned short fracX, unsigned short fracY) {
+
+#if ALLOW_FILE
+ setResourceForFatal(v);
+ if (! openFileFromNum(v)) return fatal("Can't open parallax image");
+
+ parallaxLayer *nP = new parallaxLayer;
+ if (! checkNew(nP)) return false;
+
+ nP -> next = parallaxStuff;
+ parallaxStuff = nP;
+ if (nP -> next) {
+ nP -> next -> prev = nP;
+ }
+ nP -> prev = NULL;
+
+ int picWidth;
+ int picHeight;
+
+ long file_pointer = ftell(bigDataFile);
+
+ png_structp png_ptr;
+ png_infop info_ptr, end_info;
+
+
+ int fileIsPNG = true;
+
+ // Is this a PNG file?
+
+ char tmp[10];
+ size_t bytes_read = fread(tmp, 1, 8, bigDataFile);
+ if (bytes_read != 8 && ferror(bigDataFile)) {
+ debugOut("Reading error in loadParallax.\n");
+ }
+ if (png_sig_cmp((png_byte *) tmp, 0, 8)) {
+ // No, it's old-school HSI
+ fileIsPNG = false;
+ fseek(bigDataFile, file_pointer, SEEK_SET);
+
+ picWidth = nP -> width = get2bytes(bigDataFile);
+ picHeight = nP -> height = get2bytes(bigDataFile);
+ } else {
+ // Read the PNG header
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
+ return false;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ return false;
+ }
+ png_init_io(png_ptr, bigDataFile); // Tell libpng which file to read
+ png_set_sig_bytes(png_ptr, 8); // 8 bytes already read
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, compression_type, filter_method;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ picWidth = nP -> width = width;
+ picHeight = nP -> height = height;
+
+ if (bit_depth < 8) png_set_packing(png_ptr);
+ png_set_expand(png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr);
+ if (bit_depth == 16) png_set_strip_16(png_ptr);
+
+ png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
+
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ //int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ }
+
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(picWidth);
+ picHeight = getNextPOT(picHeight);
+ }
+
+ nP -> fileNum = v;
+ nP -> fractionX = fracX;
+ nP -> fractionY = fracY;
+
+ if (fracX == 65535) {
+ nP -> wrapS = false;
+ if (nP -> width < winWidth) {
+ fatal("For AUTOFIT parallax backgrounds, the image must be at least as wide as the game window/screen.");
+ return false;
+ }
+ } else {
+ nP -> wrapS = true;
+ }
+
+ if (fracY == 65535) {
+ nP -> wrapT = false;
+ if (nP -> height < winHeight) {
+ fatal("For AUTOFIT parallax backgrounds, the image must be at least as tall as the game window/screen.");
+ return false;
+ }
+ } else {
+ nP -> wrapT = true;
+ }
+
+ nP -> texture = new GLubyte [picHeight * picWidth * 4];
+ if (! checkNew(nP -> texture)) return false;
+
+ if (fileIsPNG) {
+ unsigned char *row_pointers[nP -> height];
+ for (int i = 0; i < nP -> height; i++)
+ row_pointers[i] = nP -> texture + 4 * i * picWidth;
+
+ png_read_image(png_ptr, (png_byte **) row_pointers);
+ png_read_end(png_ptr, NULL);
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ } else {
+
+ int t1, t2, n;
+ unsigned short c;
+ GLubyte *target;
+
+ for (t2 = 0; t2 < nP -> height; t2 ++) {
+ t1 = 0;
+ while (t1 < nP -> width) {
+ c = (unsigned short) get2bytes(bigDataFile);
+ if (c & 32) {
+ n = fgetc(bigDataFile) + 1;
+ c -= 32;
+ } else {
+ n = 1;
+ }
+ while (n--) {
+ target = nP -> texture + 4 * picWidth * t2 + t1 * 4;
+ if (c == 63519 || c == 2015) {
+ target[0] = (GLubyte) 0;
+ target[1] = (GLubyte) 0;
+ target[2] = (GLubyte) 0;
+ target[3] = (GLubyte) 0;
+ } else {
+ target[0] = (GLubyte) redValue(c);
+ target[1] = (GLubyte) greenValue(c);
+ target[2] = (GLubyte) blueValue(c);
+ target[3] = (GLubyte) 255;
+ }
+ t1 ++;
+ }
+ }
+ }
+ }
+
+ glGenTextures(1, &nP->textureName);
+ glBindTexture(GL_TEXTURE_2D, nP->textureName);
+ if (nP -> wrapS)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ else
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ if (nP -> wrapT)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ else
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nP->texture, nP->textureName);
+
+ finishAccess();
+#endif
+ setResourceForFatal(-1);
+ return true;
+}
+
+extern int viewportOffsetX, viewportOffsetY;
+
+#if ALLOW_FILE
+bool loadHSI(FILE *fp, int x, int y, bool reserve) {
+
+ int t1, t2, n;
+ unsigned short c;
+ GLubyte *target;
+ int32_t transCol = reserve ? -1 : 63519;
+ int picWidth;
+ int picHeight;
+ int realPicWidth, realPicHeight;
+ long file_pointer = ftell(fp);
+
+ png_structp png_ptr;
+ png_infop info_ptr, end_info;
+
+
+ int fileIsPNG = true;
+
+ // Is this a PNG file?
+
+ char tmp[10];
+ size_t bytes_read = fread(tmp, 1, 8, fp);
+ if (bytes_read != 8 && ferror(fp)) {
+ debugOut("Reading error in loadHSI.\n");
+ }
+ if (png_sig_cmp((png_byte *) tmp, 0, 8)) {
+ // No, it's old-school HSI
+ fileIsPNG = false;
+ fseek(fp, file_pointer, SEEK_SET);
+
+ picWidth = realPicWidth = get2bytes(fp);
+ picHeight = realPicHeight = get2bytes(fp);
+ } else {
+ // Read the PNG header
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
+ return false;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ return false;
+ }
+ png_init_io(png_ptr, fp); // Tell libpng which file to read
+ png_set_sig_bytes(png_ptr, 8); // 8 bytes already read
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, compression_type, filter_method;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ picWidth = realPicWidth = width;
+ picHeight = realPicHeight = height;
+
+ if (bit_depth < 8) png_set_packing(png_ptr);
+ png_set_expand(png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr);
+ if (bit_depth == 16) png_set_strip_16(png_ptr);
+
+ png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
+
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ //int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ }
+
+
+ GLfloat texCoordW = 1.0;
+ GLfloat texCoordH = 1.0;
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(picWidth);
+ picHeight = getNextPOT(picHeight);
+ texCoordW = ((double)realPicWidth) / picWidth;
+ texCoordH = ((double)realPicHeight) / picHeight;
+ }
+
+ if (reserve) {
+ if (! resizeBackdrop(realPicWidth, realPicHeight)) return false;
+ }
+
+ if (x == IN_THE_CENTRE) x = (sceneWidth - realPicWidth) >> 1;
+ if (y == IN_THE_CENTRE) y = (sceneHeight - realPicHeight) >> 1;
+ if (x < 0 || x + realPicWidth > sceneWidth || y < 0 || y + realPicHeight > sceneHeight) return false;
+
+ if (fileIsPNG) {
+ unsigned char *row_pointers[realPicHeight];
+ for (int i = 0; i < realPicHeight; i++)
+ row_pointers[i] = backdropTexture + 4 * i * picWidth;
+
+ png_read_image(png_ptr, (png_byte **) row_pointers);
+ png_read_end(png_ptr, NULL);
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ } else {
+ for (t2 = 0; t2 < realPicHeight; t2 ++) {
+ t1 = 0;
+ while (t1 < realPicWidth) {
+ c = (unsigned short) get2bytes(fp);
+ if (c & 32) {
+ n = fgetc(fp) + 1;
+ c -= 32;
+ } else {
+ n = 1;
+ }
+ while (n --) {
+ target = backdropTexture + 4 * picWidth * t2 + t1 * 4;
+ if (c == transCol || c == 2015) {
+ target[0] = (GLubyte) 0;
+ target[1] = (GLubyte) 0;
+ target[2] = (GLubyte) 0;
+ target[3] = (GLubyte) 0;
+ } else {
+ target[0] = (GLubyte) redValue(c);
+ target[1] = (GLubyte) greenValue(c);
+ target[2] = (GLubyte) blueValue(c);
+ target[3] = (GLubyte) 255;
+ }
+ t1++;
+ }
+ }
+ }
+ }
+
+ GLuint tmpTex;
+#if 0
+ glGenTextures(1, &tmpTex);
+ glBindTexture(GL_TEXTURE_2D, tmpTex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+#endif
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, backdropTexture, tmpTex);
+
+
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+
+ float btx1;
+ float btx2;
+ float bty1;
+ float bty2;
+ if (! NPOT_textures) {
+ btx1 = backdropTexW * x / sceneWidth;
+ btx2 = backdropTexW * (x + realPicWidth) / sceneWidth;
+ bty1 = backdropTexH * y / sceneHeight;
+ bty2 = backdropTexH * (y + realPicHeight) / sceneHeight;
+ } else {
+ btx1 = (float) x / sceneWidth;
+ btx2 = (float)(x + realPicWidth) / sceneWidth;
+ bty1 = (float) y / sceneHeight;
+ bty2 = (float)(y + realPicHeight) / sceneHeight;
+ }
+
+ const GLfloat btexCoords[] = {
+ btx1, bty1,
+ btx2, bty1,
+ btx1, bty2,
+ btx2, bty2
+ };
+
+ setPixelCoords(true);
+
+ int xoffset = 0;
+ while (xoffset < realPicWidth) {
+ int w = (realPicWidth - xoffset < viewportWidth) ? realPicWidth - xoffset : viewportWidth;
+
+ int yoffset = 0;
+ while (yoffset < realPicHeight) {
+ int h = (realPicHeight - yoffset < viewportHeight) ? realPicHeight - yoffset : viewportHeight;
+#if 0
+ glClear(GL_COLOR_BUFFER_BIT); // Clear The Screen
+#endif
+ const GLfloat vertices[] = {
+ (GLfloat) - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat)realPicWidth - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat) - xoffset, (GLfloat) - yoffset + realPicHeight, 0.,
+ (GLfloat)realPicWidth - xoffset, (GLfloat) - yoffset + realPicHeight, 0.
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ texCoordW, 0.0f,
+ 0.0f, texCoordH,
+ texCoordW, texCoordH
+ };
+
+ if (backdropExists) {
+#if 0
+ // Render the sprite to the backdrop
+ // (using mulitexturing, so the old backdrop is seen where alpha < 1.0)
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glActiveTexture(GL_TEXTURE0);
+
+ glUseProgram(shader.paste);
+ GLint uniform = glGetUniformLocation(shader.paste, "useLightTexture");
+ if (uniform >= 0) glUniform1i(uniform, 0); // No lighting
+#endif
+ setPMVMatrix(shader.paste);
+
+ setPrimaryColor(1.0, 1.0, 1.0, 1.0);
+#if 0
+ glBindTexture(GL_TEXTURE_2D, tmpTex);
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+#endif
+ drawQuad(shader.paste, vertices, 3, texCoords, NULL, btexCoords);
+#if 0
+ glUseProgram(0);
+#endif
+ } else {
+ // It's all new - nothing special to be done.
+
+#if 0
+ glUseProgram(shader.texture);
+#endif
+ setPMVMatrix(shader.texture);
+#if 0
+ glBindTexture(GL_TEXTURE_2D, tmpTex);
+#endif
+ setPrimaryColor(1.0, 0.0, 0.0, 0.0);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+
+#if 0
+ glUseProgram(0);
+#endif
+ }
+
+ // Copy Our ViewPort To The Texture
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, x + xoffset, y + yoffset, viewportOffsetX, viewportOffsetY, w, h, backdropTextureName);
+
+ yoffset += viewportHeight;
+ }
+
+ xoffset += viewportWidth;
+ }
+ deleteTextures(1, &tmpTex);
+
+ setPixelCoords(false);
+
+ backdropExists = true;
+ return true;
+}
+
+bool mixHSI(FILE *fp, int x, int y) {
+ int realPicWidth, realPicHeight;
+ int picWidth;
+ int picHeight;
+
+ long file_pointer = ftell(fp);
+
+ png_structp png_ptr;
+ png_infop info_ptr, end_info;
+
+
+ int fileIsPNG = true;
+
+ // Is this a PNG file?
+ char tmp[10];
+ size_t bytes_read = fread(tmp, 1, 8, fp);
+ if (bytes_read != 8 && ferror(fp)) {
+ debugOut("Reading error in mixHSI.\n");
+ }
+ if (png_sig_cmp((png_byte *) tmp, 0, 8)) {
+ // No, it's old-school HSI
+ fileIsPNG = false;
+ fseek(fp, file_pointer, SEEK_SET);
+
+ picWidth = realPicWidth = get2bytes(fp);
+ picHeight = realPicHeight = get2bytes(fp);
+ } else {
+ // Read the PNG header
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
+ return false;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ return false;
+ }
+ png_init_io(png_ptr, fp); // Tell libpng which file to read
+ png_set_sig_bytes(png_ptr, 8); // 8 bytes already read
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, compression_type, filter_method;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ picWidth = realPicWidth = width;
+ picHeight = realPicHeight = height;
+
+ if (bit_depth < 8) png_set_packing(png_ptr);
+ png_set_expand(png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr);
+ if (bit_depth == 16) png_set_strip_16(png_ptr);
+
+ png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
+
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ //int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ }
+
+ if (x == IN_THE_CENTRE) x = (sceneWidth - realPicWidth) >> 1;
+ if (y == IN_THE_CENTRE) y = (sceneHeight - realPicHeight) >> 1;
+ if (x < 0 || x + realPicWidth > sceneWidth || y < 0 || y + realPicHeight > sceneHeight) return false;
+
+ float btx1, tx1;
+ float btx2, tx2;
+ float bty1, ty1;
+ float bty2, ty2;
+
+ if (! NPOT_textures) {
+ tx1 = 0.0;
+ ty1 = 0.0;
+ tx2 = ((double)picWidth) / getNextPOT(picWidth);
+ ty2 = ((double)picHeight) / getNextPOT(picHeight);
+ picWidth = getNextPOT(picWidth);
+ picHeight = getNextPOT(picHeight);
+ btx1 = backdropTexW * x / sceneWidth;
+ btx2 = backdropTexW * (x + realPicWidth) / sceneWidth;
+ bty1 = backdropTexH * y / sceneHeight;
+ bty2 = backdropTexH * (y + realPicHeight) / sceneHeight;
+ } else {
+ tx1 = 0.0;
+ ty1 = 0.0;
+ tx2 = 1.0;
+ ty2 = 1.0;
+ btx1 = (float) x / sceneWidth;
+ btx2 = (float)(x + picWidth) / sceneWidth;
+ bty1 = (float) y / sceneHeight;
+ bty2 = (float)(y + picHeight) / sceneHeight;
+ }
+
+ const GLfloat texCoords[] = {
+ tx1, ty1,
+ tx2, ty1,
+ tx1, ty2,
+ tx2, ty2
+ };
+
+ const GLfloat btexCoords[] = {
+ btx1, bty1,
+ btx2, bty1,
+ btx1, bty2,
+ btx2, bty2
+ };
+
+ int t1, t2, n;
+ unsigned short c;
+ GLubyte *target;
+ int32_t transCol = 63519;
+
+
+ if (fileIsPNG) {
+ unsigned char *row_pointers[realPicHeight];
+ for (int i = 0; i < realPicHeight; i++)
+ row_pointers[i] = backdropTexture + 4 * i * picWidth;
+
+ png_read_image(png_ptr, (png_byte **) row_pointers);
+ png_read_end(png_ptr, NULL);
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ } else {
+ for (t2 = 0; t2 < realPicHeight; t2 ++) {
+ t1 = 0;
+ while (t1 < realPicWidth) {
+ c = (unsigned short) get2bytes(fp);
+ if (c & 32) {
+ n = fgetc(fp) + 1;
+ c -= 32;
+ } else {
+ n = 1;
+ }
+ while (n --) {
+ target = backdropTexture + 4 * picWidth * t2 + t1 * 4;
+ if (c == transCol || c == 2015) {
+ target[0] = (GLubyte) 0;
+ target[1] = (GLubyte) 0;
+ target[2] = (GLubyte) 0;
+ target[3] = (GLubyte) 0;
+ } else {
+ target[0] = (GLubyte) redValue(c);
+ target[1] = (GLubyte) greenValue(c);
+ target[2] = (GLubyte) blueValue(c);
+ target[3] = (GLubyte) 255;
+ }
+ t1++;
+ }
+ }
+ }
+ }
+
+ GLuint tmpTex;
+#if 0
+ glGenTextures(1, &tmpTex);
+ glBindTexture(GL_TEXTURE_2D, tmpTex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+#endif
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, backdropTexture, tmpTex);
+
+
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ setPixelCoords(true);
+
+
+ int xoffset = 0;
+ while (xoffset < realPicWidth) {
+ int w = (realPicWidth - xoffset < viewportWidth) ? realPicWidth - xoffset : viewportWidth;
+
+ int yoffset = 0;
+ while (yoffset < realPicHeight) {
+ int h = (realPicHeight - yoffset < viewportHeight) ? realPicHeight - yoffset : viewportHeight;
+#if 0
+ glClear(GL_COLOR_BUFFER_BIT); // Clear The Screen
+
+ // Render the sprite to the backdrop
+ // (using mulitexturing, so the backdrop is seen where alpha < 1.0)
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glActiveTexture(GL_TEXTURE0);
+
+ glUseProgram(shader.paste);
+ GLint uniform = glGetUniformLocation(shader.paste, "useLightTexture");
+ if (uniform >= 0) glUniform1i(uniform, 0); // No lighting
+#endif
+ setPMVMatrix(shader.paste);
+
+ setPrimaryColor(1.0, 1.0, 1.0, 0.5);
+#if 0
+ glBindTexture(GL_TEXTURE_2D, tmpTex);
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+#endif
+ const GLfloat vertices[] = {
+ (GLfloat) - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat)realPicWidth - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat) - xoffset, (GLfloat) - yoffset + realPicHeight, 0.,
+ (GLfloat)realPicWidth - xoffset, (GLfloat) - yoffset + realPicHeight, 0.
+ };
+
+ drawQuad(shader.paste, vertices, 3, texCoords, NULL, btexCoords);
+#if 0
+ // Copy Our ViewPort To The Texture
+ glUseProgram(0);
+#endif
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, (int)((x < 0) ? xoffset : x + xoffset), (int)((y < 0) ? yoffset : y + yoffset), (int)((x < 0) ? viewportOffsetX - x : viewportOffsetX), (int)((y < 0) ? viewportOffsetY - y : viewportOffsetY), w, h, backdropTextureName);
+
+ yoffset += viewportHeight;
+ }
+
+ xoffset += viewportWidth;
+ }
+ deleteTextures(1, &tmpTex);
+ setPixelCoords(false);
+ return true;
+}
+
+void saveCorePNG(FILE *writer, GLuint texture, int w, int h) {
+ GLint tw, th;
+#if 0
+ glBindTexture(GL_TEXTURE_2D, texture);
+#endif
+ getTextureDimensions(texture, &tw, &th);
+
+ GLubyte *image = new GLubyte [tw * th * 4];
+ if (! checkNew(image)) return;
+#if 0
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+// glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
+#endif
+#ifdef HAVE_GLES2
+#if 0
+ GLuint old_fbo, new_fbo;
+ GLint old_vp[4];
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&old_fbo);
+ glGetIntegerv(GL_VIEWPORT, old_vp);
+ glGenFramebuffers(1, &new_fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, new_fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+ glViewport(0, 0, tw, th);
+ glReadPixels(0, 0, tw, th, GL_RGBA, GL_UNSIGNED_BYTE, image);
+ glBindFramebuffer(GL_FRAMEBUFFER, old_fbo);
+ glViewport(old_vp[0], old_vp[1], old_vp[2], old_vp[3]);
+ glDeleteFramebuffers(1, &new_fbo);
+#endif
+#else
+ setPixelCoords(true);
+
+
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f
+ };
+
+ int xoffset = 0;
+ while (xoffset < tw) {
+ int w = (tw - xoffset < viewportWidth) ? tw - xoffset : viewportWidth;
+
+ int yoffset = 0;
+ while (yoffset < th) {
+ int h = (th - yoffset < viewportHeight) ? th - yoffset : viewportHeight;
+#if 0
+ glClear(GL_COLOR_BUFFER_BIT); // Clear The Screen
+#endif
+ const GLfloat vertices[] = {
+ (GLfloat) - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat)tw - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat) - xoffset, (GLfloat) - yoffset + th, 0.,
+ (GLfloat)tw - xoffset, (GLfloat) - yoffset + th, 0.
+ };
+
+#if 0
+ glUseProgram(shader.texture);
+#endif
+ setPMVMatrix(shader.texture);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+#if 0
+ glUseProgram(0);
+
+ for (int i = 0; i < h; i++) {
+ glReadPixels(viewportOffsetX, viewportOffsetY + i, w, 1, GL_RGBA, GL_UNSIGNED_BYTE, image + xoffset * 4 + (yoffset + i) * 4 * tw);
+ }
+#endif
+ yoffset += viewportHeight;
+ }
+
+ xoffset += viewportWidth;
+ }
+ setPixelCoords(false);
+#endif
+
+
+
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ fatal("Error saving image!");
+ return;
+ }
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ fatal("Error saving image!");
+ return;
+ }
+
+ png_init_io(png_ptr, writer);
+
+ png_set_IHDR(png_ptr, info_ptr, w, h,
+ 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ unsigned char *row_pointers[h];
+
+ for (int i = 0; i < h; i++) {
+ row_pointers[i] = image + 4 * i * tw;
+ }
+
+ png_set_rows(png_ptr, info_ptr, row_pointers);
+ png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+
+ delete [] image;
+ image = NULL;
+}
+
+void saveCoreHSI(FILE *writer, GLuint texture, int w, int h) {
+
+ GLint tw, th;
+#if 0
+ glBindTexture(GL_TEXTURE_2D, texture);
+#endif
+ getTextureDimensions(texture, &tw, &th);
+
+ GLushort *image = new GLushort [tw * th];
+ if (! checkNew(image)) return;
+#if 0
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+// glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, image);
+#endif
+ setPixelCoords(true);
+
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f
+ };
+
+ int xoffset = 0;
+ while (xoffset < tw) {
+ int w = (tw - xoffset < viewportWidth) ? tw - xoffset : viewportWidth;
+
+ int yoffset = 0;
+ while (yoffset < th) {
+ int h = (th - yoffset < viewportHeight) ? th - yoffset : viewportHeight;
+#if 0
+ glClear(GL_COLOR_BUFFER_BIT); // Clear The Screen
+#endif
+ const GLfloat vertices[] = {
+ (GLfloat) - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat)w - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat) - xoffset, (GLfloat) - yoffset + h, 0.,
+ (GLfloat)w - xoffset, (GLfloat) - yoffset + h, 0.
+ };
+
+#if 0
+ glUseProgram(shader.texture);
+#endif
+ setPMVMatrix(shader.texture);
+ drawQuad(shader.texture, vertices, 1, texCoords);
+#if 0
+ glUseProgram(0);
+
+ for (int i = 0; i < h; i++) {
+ glReadPixels(viewportOffsetX, viewportOffsetY + i, w, 1, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, image + xoffset + (yoffset + i)*tw);
+ }
+#endif
+ yoffset += viewportHeight;
+ }
+
+ xoffset += viewportWidth;
+ }
+ //glReadPixels(viewportOffsetX, viewportOffsetY, tw, th, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ setPixelCoords(false);
+
+
+
+ int x, y, lookAhead;
+ unsigned short int *fromHere, * lookPointer;
+
+ put2bytes(w, writer);
+ put2bytes(h, writer);
+
+ for (y = 0; y < h; y ++) {
+ fromHere = image + (y * tw);
+ x = 0;
+ while (x < w) {
+ lookPointer = fromHere + 1;
+ for (lookAhead = x + 1; lookAhead < w; lookAhead ++) {
+ if (lookAhead - x == 256) break;
+ if (* fromHere != * lookPointer) break;
+ lookPointer ++;
+ }
+ if (lookAhead == x + 1) {
+ put2bytes((* fromHere) & 65503, writer);
+ } else {
+ put2bytes(* fromHere | 32, writer);
+ fputc(lookAhead - x - 1, writer);
+ }
+ fromHere = lookPointer;
+ x = lookAhead;
+ }
+ }
+ delete [] image;
+ image = NULL;
+}
+
+void saveHSI(FILE *writer) {
+ saveCorePNG(writer, backdropTextureName, sceneWidth, sceneHeight);
+}
+
+
+void saveParallaxRecursive(parallaxLayer *me, FILE *fp) {
+ if (me) {
+ saveParallaxRecursive(me -> next, fp);
+ fputc(1, fp);
+ put2bytes(me->fileNum, fp);
+ put2bytes(me ->fractionX, fp);
+ put2bytes(me->fractionY, fp);
+ }
+}
+#endif
+
+bool getRGBIntoStack(unsigned int x, unsigned int y, stackHandler *sH) {
+#if 0
+ if (x >= sceneWidth || y >= sceneHeight) {
+ return fatal("Co-ordinates are outside current scene!");
+ }
+
+ variable newValue;
+
+ newValue.varType = SVT_NULL;
+
+ saveTexture(backdropTextureName, backdropTexture);
+
+ GLubyte *target;
+ if (! NPOT_textures) {
+ target = backdropTexture + 4 * getNextPOT(sceneWidth) * y + x * 4;
+ } else {
+ target = backdropTexture + 4 * sceneWidth * y + x * 4;
+ }
+
+ setVariable(newValue, SVT_INT, target[2]);
+ if (! addVarToStackQuick(newValue, sH -> first)) return false;
+ sH -> last = sH -> first;
+
+ setVariable(newValue, SVT_INT, target[1]);
+ if (! addVarToStackQuick(newValue, sH -> first)) return false;
+
+ setVariable(newValue, SVT_INT, target[0]);
+ if (! addVarToStackQuick(newValue, sH -> first)) return false;
+#endif
+ return true;
+}
diff --git a/engines/sludge/backdrop.h b/engines/sludge/backdrop.h
new file mode 100644
index 0000000000..97d3377504
--- /dev/null
+++ b/engines/sludge/backdrop.h
@@ -0,0 +1,103 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SLUDGE_BACKDROP_H
+#define SLUDGE_BACKDROP_H
+
+#if 0
+#if !defined(HAVE_GLES2)
+#include "GLee.h"
+#else
+#include <GLES2/gl2.h>
+#endif
+#endif
+
+#include "variable.h"
+#include "graphics.h"
+
+enum {
+ LIGHTMAPMODE_NONE = -1,
+ LIGHTMAPMODE_HOTSPOT,
+ LIGHTMAPMODE_PIXEL,
+ LIGHTMAPMODE_NUM
+};
+
+extern unsigned int winWidth, winHeight, sceneWidth, sceneHeight;
+extern int lightMapMode;
+
+
+struct parallaxLayer {
+#if 0
+ GLubyte *texture;
+ GLuint textureName;
+#endif
+ int width, height, speedX, speedY;
+ bool wrapS, wrapT;
+ unsigned short fileNum, fractionX, fractionY;
+ int cameraX, cameraY;
+ parallaxLayer *next;
+ parallaxLayer *prev;
+};
+
+bool resizeBackdrop(int x, int y);
+void killBackDrop();
+void loadBackDrop(int fileNum, int x, int y);
+void mixBackDrop(int fileNum, int x, int y);
+void drawBackDrop();
+void blankScreen(int x1, int y1, int x2, int y2);
+void darkScreen();
+#if ALLOW_FILE
+void saveHSI(FILE *writer);
+void saveCoreHSI(FILE *writer, GLuint texture, int w, int h);
+bool loadHSI(FILE *fp, int, int, bool);
+bool mixHSI(FILE *fp, int x = 0, int y = 0);
+#endif
+void drawHorizontalLine(unsigned int, unsigned int, unsigned int);
+void drawVerticalLine(unsigned int, unsigned int, unsigned int);
+void hardScroll(int distance);
+bool getRGBIntoStack(unsigned int x, unsigned int y, stackHandler *sH);
+
+// Also the light map stuff
+
+void killLightMap();
+bool loadLightMap(int v);
+
+#if 0
+extern texture lightMap;
+#endif
+
+// And background parallax scrolling
+
+void killParallax();
+bool loadParallax(unsigned short v, unsigned short fracX, unsigned short fracY);
+#if 0
+void saveParallaxRecursive(parallaxLayer *me, FILE *fp);
+#endif
+void reloadParallaxTextures();
+
+void nosnapshot();
+bool snapshot();
+#if ALLOW_FILE
+void saveSnapshot(FILE *fp);
+bool restoreSnapshot(FILE *fp);
+#endif
+#endif
diff --git a/engines/sludge/bg_effects.cpp b/engines/sludge/bg_effects.cpp
new file mode 100644
index 0000000000..ad6438b8a6
--- /dev/null
+++ b/engines/sludge/bg_effects.cpp
@@ -0,0 +1,375 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "allfiles.h"
+#include "backdrop.h"
+#include "colours.h"
+#include "debug.h"
+#include "graphics.h"
+#include "newfatal.h"
+#include "moreio.h"
+
+#if 0
+//extern unsigned short int * * backDropImage;
+extern GLuint backdropTextureName;
+#endif
+
+#if 0
+// Raised
+static int s_matrixEffectDivide = 2;
+static int s_matrixEffectWidth = 3;
+static int s_matrixEffectHeight = 3;
+static int s_matrixEffectData[9] = {0, 0, 0, 0, -1, 0, 0, 0, 2};
+static int s_matrixEffectBase = 0;
+#elif 0
+// Stay put
+static int s_matrixEffectDivide = 1;
+static int s_matrixEffectWidth = 3;
+static int s_matrixEffectHeight = 3;
+static int s_matrixEffectData[9] = {0, 0, 0, 0, 1, 0, 0, 0, 0};
+static int s_matrixEffectBase = 0;
+#elif 0
+// Brighten
+static int s_matrixEffectDivide = 9;
+static int s_matrixEffectWidth = 1;
+static int s_matrixEffectHeight = 1;
+static int s_matrixEffectData[9] = {10};
+static int s_matrixEffectBase = 15;
+#elif 0
+// Raised up/left
+static int s_matrixEffectDivide = 4;
+static int s_matrixEffectWidth = 3;
+static int s_matrixEffectHeight = 3;
+static int s_matrixEffectData[9] = { -2, -1, 0, -1, 1, 1, 0, 1, 2};
+static int s_matrixEffectBase = 16;
+#elif 0
+// Standard emboss
+static int s_matrixEffectDivide = 2;
+static int s_matrixEffectWidth = 3;
+static int s_matrixEffectHeight = 3;
+static int s_matrixEffectData[9] = { -1, 0, 0, 0, 0, 0, 0, 0, 1};
+static int s_matrixEffectBase = 128;
+#elif 0
+// Horizontal blur
+static int s_matrixEffectDivide = 11;
+static int s_matrixEffectWidth = 5;
+static int s_matrixEffectHeight = 1;
+static int s_matrixEffectData[9] = {1, 3, 3, 3, 1};
+static int s_matrixEffectBase = 0;
+#elif 0
+// Double vision
+static int s_matrixEffectDivide = 6;
+static int s_matrixEffectWidth = 13;
+static int s_matrixEffectHeight = 2;
+static int s_matrixEffectData[26] = {2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3};
+static int s_matrixEffectBase = 0;
+#elif 0
+// Negative
+static int s_matrixEffectDivide = 1;
+static int s_matrixEffectWidth = 1;
+static int s_matrixEffectHeight = 1;
+static int s_matrixEffectData[9] = { -1};
+static int s_matrixEffectBase = 255;
+#elif 0
+// Fog
+static int s_matrixEffectDivide = 4;
+static int s_matrixEffectWidth = 1;
+static int s_matrixEffectHeight = 1;
+static int s_matrixEffectData[9] = {3};
+static int s_matrixEffectBase = 45;
+#elif 0
+// Blur
+static int s_matrixEffectDivide = 14;
+static int s_matrixEffectWidth = 3;
+static int s_matrixEffectHeight = 3;
+static int s_matrixEffectData[9] = {1, 2, 1, 2, 2, 2, 1, 2, 1};
+static int s_matrixEffectBase = 0;
+#else
+static int s_matrixEffectDivide = 0;
+static int s_matrixEffectWidth = 0;
+static int s_matrixEffectHeight = 0;
+static int *s_matrixEffectData = NULL;
+static int s_matrixEffectBase = 0;
+#endif
+
+#if ALLOW_FILE
+void blur_saveSettings(FILE *fp) {
+ if (s_matrixEffectData) {
+ put4bytes(s_matrixEffectDivide, fp);
+ put4bytes(s_matrixEffectWidth, fp);
+ put4bytes(s_matrixEffectHeight, fp);
+ put4bytes(s_matrixEffectBase, fp);
+ fwrite(s_matrixEffectData, sizeof(int), s_matrixEffectWidth * s_matrixEffectHeight, fp);
+ } else {
+ put4bytes(0, fp);
+ put4bytes(0, fp);
+ put4bytes(0, fp);
+ put4bytes(0, fp);
+ }
+}
+#endif
+
+static int *blur_allocateMemoryForEffect() {
+ free(s_matrixEffectData);
+ s_matrixEffectData = NULL;
+
+ if (s_matrixEffectWidth && s_matrixEffectHeight) {
+ s_matrixEffectData = (int *) malloc(sizeof(int) * s_matrixEffectHeight * s_matrixEffectWidth);
+ checkNew(s_matrixEffectData);
+ }
+ return s_matrixEffectData;
+}
+
+#if ALLOW_FILE
+void blur_loadSettings(FILE *fp) {
+ s_matrixEffectDivide = get4bytes(fp);
+ s_matrixEffectWidth = get4bytes(fp);
+ s_matrixEffectHeight = get4bytes(fp);
+ s_matrixEffectBase = get4bytes(fp);
+
+ if (blur_allocateMemoryForEffect()) {
+ size_t bytes_read = fread(s_matrixEffectData, sizeof(int), s_matrixEffectWidth * s_matrixEffectHeight, fp);
+ if (bytes_read != sizeof(int) * s_matrixEffectWidth * s_matrixEffectHeight && ferror(fp)) {
+ debugOut("Reading error in blur_loadSettings.\n");
+ }
+ } else {
+ fseek(fp, sizeof(int) * s_matrixEffectWidth * s_matrixEffectHeight, SEEK_CUR);
+ }
+}
+#endif
+
+bool blur_createSettings(int numParams, variableStack *&stack) {
+ bool createNullThing = true;
+ const char *error = NULL;
+
+ if (numParams >= 3) {
+ // PARAMETERS: base, divide, stack (, stack (, stack...))
+
+ int height = numParams - 2;
+ int width = 0;
+
+ variableStack *justToCheckSizes = stack;
+ for (int a = 0; a < height; a ++) {
+ if (justToCheckSizes->thisVar.varType != SVT_STACK) {
+ error = "Third and subsequent parameters in setBackgroundEffect should be arrays";
+ break;
+ } else {
+ int w = stackSize(justToCheckSizes->thisVar.varData.theStack);
+ if (a) {
+ if (w != width) {
+ error = "Arrays in setBackgroundEffect must be the same size";
+ break;
+ }
+ if (w < width) {
+ width = w;
+ }
+ } else {
+ width = w;
+ }
+ }
+ }
+
+ if (width == 0 && ! error) {
+ error = "Empty arrays found in setBackgroundEffect parameters";
+ }
+
+ if (! error) {
+ s_matrixEffectWidth = width;
+ s_matrixEffectHeight = height;
+
+ if (blur_allocateMemoryForEffect()) {
+ for (int y = height - 1; y >= 0; y --) {
+ variableStack *eachNumber = stack->thisVar.varData.theStack->first;
+ if (! error) {
+ for (int x = 0; x < width; x ++) {
+ int arraySlot = x + (y * width);
+// s_matrixEffectData[arraySlot] = (rand() % 4);
+ if (!getValueType(s_matrixEffectData[arraySlot], SVT_INT, eachNumber->thisVar)) {
+ error = "";
+ break;
+ }
+ eachNumber = eachNumber->next;
+ }
+ trimStack(stack);
+ }
+ }
+ if (! error && !getValueType(s_matrixEffectDivide, SVT_INT, stack -> thisVar))
+ error = "";
+ trimStack(stack);
+ if (! error && !getValueType(s_matrixEffectBase, SVT_INT, stack -> thisVar))
+ error = "";
+ trimStack(stack);
+ if (! error) {
+ if (s_matrixEffectDivide) {
+ createNullThing = false;
+ } else {
+ error = "Second parameter of setBackgroundEffect (the 'divide' value) should not be 0!";
+ }
+ }
+ } else {
+ error = "Couldn't allocate memory for effect";
+ }
+ }
+ } else {
+ if (numParams) {
+ error = "setBackgroundEffect should either have 0 parameters or more than 2";
+ }
+ }
+
+ if (createNullThing) {
+ s_matrixEffectDivide = 0;
+ s_matrixEffectWidth = 0;
+ s_matrixEffectHeight = 0;
+ s_matrixEffectBase = 0;
+ delete s_matrixEffectData;
+ s_matrixEffectData = NULL;
+ }
+
+ if (error && error[0]) {
+ fatal(error);
+ }
+
+ return ! createNullThing;
+}
+
+static inline int clampi(int i, int min, int max) {
+ return (i >= max) ? max : ((i <= min) ? min : i);
+}
+
+static inline void blur_createSourceLine(unsigned char *createLine, unsigned char *fromLine, int overlapOnLeft, int width) {
+ int miniX;
+ memcpy(createLine + overlapOnLeft * 4, fromLine, width * 4);
+
+ for (miniX = 0; miniX < overlapOnLeft; miniX ++) {
+ createLine[miniX * 4] = fromLine[0];
+ createLine[miniX * 4 + 1] = fromLine[1];
+ createLine[miniX * 4 + 2] = fromLine[2];
+ }
+
+ for (miniX = width + overlapOnLeft; miniX < width + s_matrixEffectWidth - 1; miniX ++) {
+ createLine[miniX * 4] = fromLine[width * 4 - 4];
+ createLine[miniX * 4 + 1] = fromLine[width * 4 - 3];
+ createLine[miniX * 4 + 2] = fromLine[width * 4 - 2];
+ }
+}
+
+bool blurScreen() {
+#if 0
+ if (s_matrixEffectWidth && s_matrixEffectHeight && s_matrixEffectDivide && s_matrixEffectData) {
+ unsigned char *thisLine;
+ int y, x;
+ bool ok = true;
+ int overlapOnLeft = s_matrixEffectWidth / 2;
+ int overlapAbove = s_matrixEffectHeight / 2;
+
+ unsigned char **sourceLine = new unsigned char *[s_matrixEffectHeight];
+ if (! checkNew(sourceLine)) return false;
+
+ int picWidth = sceneWidth;
+ int picHeight = sceneHeight;
+
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(sceneWidth);
+ picHeight = getNextPOT(sceneHeight);
+ }
+
+ // Retrieve the texture
+ saveTexture(backdropTextureName, backdropTexture);
+
+ for (y = 0; y < s_matrixEffectHeight; y ++) {
+ sourceLine[y] = new unsigned char[(s_matrixEffectWidth - 1 + picWidth) * 4];
+ ok &= (sourceLine[y] != NULL);
+ }
+
+ if (ok) {
+ for (y = 0; y < s_matrixEffectHeight; y ++) {
+ int miniY = clampi(y - overlapAbove - 1, 0, sceneHeight - 1);
+
+ blur_createSourceLine(sourceLine[y], backdropTexture + miniY * picWidth * 4, overlapOnLeft, picWidth);
+ }
+
+ for (y = 0; y < sceneHeight; y ++) {
+ thisLine = backdropTexture + y * picWidth * 4;
+
+ //-------------------------
+ // Scroll source lines
+ //-------------------------
+ unsigned char *tempLine = sourceLine[0];
+ for (int miniY = 0; miniY < s_matrixEffectHeight - 1; miniY ++) {
+ sourceLine[miniY] = sourceLine[miniY + 1];
+ }
+ sourceLine[s_matrixEffectHeight - 1] = tempLine;
+ {
+ int h = s_matrixEffectHeight - 1;
+ int miniY = clampi(y + (s_matrixEffectHeight - overlapAbove - 1), 0, sceneHeight - 1);
+
+ blur_createSourceLine(sourceLine[h], backdropTexture + miniY * picWidth * 4, overlapOnLeft, picWidth);
+ }
+ for (x = 0; x < sceneWidth; x ++) {
+ int totalRed = 0;
+ int totalGreen = 0;
+ int totalBlue = 0;
+ int *matrixElement = s_matrixEffectData;
+ for (int miniY = 0; miniY < s_matrixEffectHeight; ++ miniY) {
+ unsigned char *pixel = & sourceLine[miniY][x * 4];
+ for (int miniX = 0; miniX < s_matrixEffectWidth; ++ miniX) {
+
+ totalRed += pixel[0] * * matrixElement;
+ totalGreen += pixel[1] * * matrixElement;
+ totalBlue += pixel[2] * * matrixElement;
+ ++ matrixElement;
+ pixel += 4;
+ }
+ }
+ totalRed = (totalRed + s_matrixEffectDivide / 2) / s_matrixEffectDivide + s_matrixEffectBase;
+ totalRed = (totalRed < 0) ? 0 : ((totalRed > 255) ? 255 : totalRed);
+
+ totalGreen = (totalGreen + s_matrixEffectDivide / 2) / s_matrixEffectDivide + s_matrixEffectBase;
+ totalGreen = (totalGreen < 0) ? 0 : ((totalGreen > 255) ? 255 : totalGreen);
+
+ totalBlue = (totalBlue + s_matrixEffectDivide / 2) / s_matrixEffectDivide + s_matrixEffectBase;
+ totalBlue = (totalBlue < 0) ? 0 : ((totalBlue > 255) ? 255 : totalBlue);
+
+ * thisLine = totalRed;
+ ++ thisLine;
+ * thisLine = totalGreen;
+ ++ thisLine;
+ * thisLine = totalBlue;
+ ++ thisLine;
+// * thisLine = totalAlpha;
+ ++ thisLine;
+ }
+ }
+ }
+
+ for (y = 0; y < s_matrixEffectHeight; y ++) {
+ delete sourceLine[y];
+ }
+ delete sourceLine;
+ sourceLine = NULL;
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, backdropTexture, backdropTextureName);
+ return true;
+ }
+#endif
+ return false;
+}
diff --git a/engines/sludge/bg_effects.h b/engines/sludge/bg_effects.h
new file mode 100644
index 0000000000..067fa8d92e
--- /dev/null
+++ b/engines/sludge/bg_effects.h
@@ -0,0 +1,33 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SLUDGE_BG_EFFECTS_H
+#define SLUDGE_BG_EFFECTS_H
+
+bool blurScreen();
+#if ALLOW_FILE
+void blur_saveSettings(FILE *fp);
+void blur_loadSettings(FILE *fp);
+#endif
+bool blur_createSettings(int numParams, variableStack *&stack);
+
+#endif
diff --git a/engines/sludge/builtin.cpp b/engines/sludge/builtin.cpp
new file mode 100644
index 0000000000..9ba633de65
--- /dev/null
+++ b/engines/sludge/builtin.cpp
@@ -0,0 +1,2545 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if 0
+#include <SDL/SDL.h>
+#endif
+
+#include "debug.h"
+#include "allfiles.h"
+#include "sludger.h"
+#include "builtin.h"
+#include "stringy.h"
+#include "newfatal.h"
+#include "cursors.h"
+#include "statusba.h"
+#include "loadsave.h"
+#include "backdrop.h"
+#include "bg_effects.h"
+#include "sprites.h"
+#include "fonttext.h"
+#include "sprbanks.h"
+#include "people.h"
+#include "sound.h"
+#include "objtypes.h"
+#include "floor.h"
+#include "zbuffer.h"
+#include "talk.h"
+#include "region.h"
+#include "language.h"
+#include "moreio.h"
+#include "movie.h"
+#include "savedata.h"
+#include "freeze.h"
+#include "colours.h"
+#include "language.h"
+#include "thumbnail.h"
+#include "graphics.h"
+#include "CommonCode/utf8.h"
+
+extern char *gamePath;
+
+int speechMode = 0;
+int cameraX, cameraY;
+float cameraZoom = 1.0;
+spritePalette pastePalette;
+
+char *launchMe = NULL;
+variable *launchResult = NULL;
+
+extern int lastFramesPerSecond, thumbWidth, thumbHeight;
+extern bool allowAnyFilename;
+extern bool captureAllKeys;
+extern short fontSpace;
+extern eventHandlers *currentEvents;
+extern variableStack *noStack;
+extern statusStuff *nowStatus;
+extern screenRegion *overRegion;
+extern HWND hMainWindow;
+extern unsigned int sceneWidth, sceneHeight;
+extern int numBIFNames, numUserFunc;
+extern char builtInFunctionNames[][25];
+
+extern char * *allUserFunc;
+extern char * *allBIFNames;
+extern inputType input;
+extern char *loadNow;
+
+#if 0
+extern GLuint backdropTextureName;
+#endif
+
+extern float speechSpeed;
+extern unsigned char brightnessLevel;
+extern unsigned char fadeMode;
+extern unsigned short saveEncoding;
+extern frozenStuffStruct *frozenStuff;
+extern unsigned int currentBlankColour;
+extern unsigned int languageID;
+extern unsigned char currentBurnR, currentBurnG, currentBurnB;
+
+int paramNum[] = { -1, 0, 1, 1, -1, -1, 1, 3, 4, 1, 0, 0, 8, -1, // SAY -> MOVEMOUSE
+ -1, 0, 0, -1, -1, 1, 1, 1, 1, 4, 1, 1, 2, 1,// FOCUS -> REMOVEREGION
+ 2, 2, 0, 0, 2, // ANIMATE -> SETSCALE
+ -1, 2, 1, 0, 0, 0, 1, 0, 3, // new/push/pop stack, status stuff
+ 2, 0, 0, 3, 1, 0, 2, // delFromStack -> completeTimers
+ -1, -1, -1, 2, 2, 0, 3, 1, // anim, costume, pO, setC, wait, sS, substring, stringLength
+ 0, 1, 1, 0, 2, // dark, save, load, quit, rename
+ 1, 3, 3, 1, 2, 1, 1, 3, 1, 0, 0, 2, 1, // stackSize, pasteString, startMusic, defvol, vol, stopmus, stopsound, setfont, alignStatus, show x 2, pos'Status, setFloor
+ -1, -1, 1, 1, 2, 1, 1, 1, -1, -1, -1, 1, 1, // force, jump, peekstart, peekend, enqueue, getSavedGames, inFont, loopSound, removeChar, stopCharacter
+ 1, 0, 3, 3, 1, 2, 1, 2, 2, // launch, howFrozen, pastecol, litcol, checksaved, float, cancelfunc, walkspeed, delAll
+ 2, 3, 1, 2, 2, 0, 0, 1, 2, 3, 1, -1, // extras, mixoverlay, pastebloke, getMScreenX/Y, setSound(Default/-)Volume, looppoints, speechMode, setLightMap
+ -1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, // think, getCharacterDirection, is(char/region/moving), deleteGame, renameGame, hardScroll, stringWidth, speechSpeed, normalCharacter
+ 2, 1, 2, 1, 3, 1, 1, 2, 1, // fetchEvent, setBrightness, spin, fontSpace, burnString, captureAll, cacheSound, setSpinSpeed, transitionMode
+ 1, 0, 0, 1, 0, 2, 1, 1, 1, // movie(Start/Abort/Playing), updateDisplay, getSoundCache, savedata, loaddata, savemode, freeSound
+ 3, 0, 3, 3, 2, 1, 1, // setParallax, clearParallax, setBlankColour, setBurnColour, getPixelColour, makeFastArray, getCharacterScale
+ 0, 2, 0, // getLanguage, launchWith, getFramesPerSecond
+ 3, 2, 2, 0, 0, 1, // readThumbnail, setThumbnailSize, hasFlag, snapshot, clearSnapshot, anyFilename
+ 2, 1, // regGet, fatal
+ 4, 3, -1, 0, // chr AA, max AA, setBackgroundEffect, doBackgroundEffect
+ 2, // setCharacterAngleOffset
+ 2, 5, // setCharacterTransparency, setCharacterColourise
+ 1, // zoomCamera
+ 1, 0, 0 // playMovie, stopMovie, pauseMovie
+ };
+
+bool failSecurityCheck(char *fn) {
+ if (fn == NULL) return true;
+
+ int a = 0;
+
+ while (fn[a]) {
+ switch (fn[a]) {
+ case ':':
+ case '\\':
+ case '/':
+ case '*':
+ case '?':
+ case '"':
+ case '<':
+ case '>':
+ case '|':
+ fatal("Filenames may not contain the following characters: \n\n\\ / : \" < > | ? *\n\nConsequently, the following filename is not allowed:", fn);
+ return true;
+ }
+ a ++;
+ }
+ return false;
+}
+
+loadedFunction *saverFunc;
+
+typedef builtReturn(* builtInSludgeFunc)(int numParams, loadedFunction *fun);
+struct builtInFunctionData {
+ builtInSludgeFunc func;
+};
+
+#define builtIn(a) static builtReturn builtIn_ ## a (int numParams, loadedFunction * fun)
+#define UNUSEDALL (void) (0 && sizeof(numParams) && sizeof (fun));
+
+static builtReturn sayCore(int numParams, loadedFunction *fun, bool sayIt) {
+ int fileNum = -1;
+ char *newText;
+ int objT, p;
+ killSpeechTimers();
+
+ switch (numParams) {
+ case 3:
+ if (!getValueType(fileNum, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ // No break; here
+
+ case 2:
+ newText = getTextFromAnyVar(fun -> stack -> thisVar);
+ if (!newText) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(objT, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ p = wrapSpeech(newText, objT, fileNum, sayIt);
+ fun -> timeLeft = p;
+ //debugOut ("BUILTIN: sayCore: %s (%i)\n", newText, p);
+ fun -> isSpeech = true;
+ delete newText;
+ newText = NULL;
+ return BR_KEEP_AND_PAUSE;
+ }
+
+ fatal("Function should have either 2 or 3 parameters");
+ return BR_ERROR;
+}
+
+#pragma mark -
+#pragma mark Built in functions
+
+builtIn(say) {
+ UNUSEDALL
+ return sayCore(numParams, fun, true);
+}
+
+builtIn(think) {
+ UNUSEDALL
+ return sayCore(numParams, fun, false);
+}
+
+builtIn(freeze) {
+ UNUSEDALL
+ freeze();
+ freezeSubs();
+ fun -> freezerLevel = 0;
+ return BR_CONTINUE;
+}
+
+builtIn(unfreeze) {
+ UNUSEDALL
+ unfreeze();
+ unfreezeSubs();
+ return BR_CONTINUE;
+}
+
+builtIn(howFrozen) {
+ UNUSEDALL
+ setVariable(fun -> reg, SVT_INT, howFrozen());
+ return BR_CONTINUE;
+}
+
+builtIn(setCursor) {
+ UNUSEDALL
+ personaAnimation *aa = getAnimationFromVar(fun -> stack -> thisVar);
+ pickAnimCursor(aa);
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+}
+
+builtIn(getMouseX) {
+ UNUSEDALL
+ setVariable(fun -> reg, SVT_INT, input.mouseX + cameraX);
+ return BR_CONTINUE;
+}
+
+builtIn(getMouseY) {
+ UNUSEDALL
+ setVariable(fun -> reg, SVT_INT, input.mouseY + cameraY);
+ return BR_CONTINUE;
+}
+
+builtIn(getMouseScreenX) {
+ UNUSEDALL
+ setVariable(fun -> reg, SVT_INT, input.mouseX * cameraZoom);
+ return BR_CONTINUE;
+}
+
+builtIn(getMouseScreenY) {
+ UNUSEDALL
+ setVariable(fun -> reg, SVT_INT, input.mouseY * cameraZoom);
+ return BR_CONTINUE;
+}
+
+builtIn(getStatusText) {
+ UNUSEDALL
+ makeTextVar(fun -> reg, statusBarText());
+ return BR_CONTINUE;
+}
+
+builtIn(getMatchingFiles) {
+ UNUSEDALL
+ char *newText = getTextFromAnyVar(fun -> stack -> thisVar);
+ if (!newText) return BR_ERROR;
+ trimStack(fun -> stack);
+ unlinkVar(fun -> reg);
+
+ // Return value
+ fun -> reg.varType = SVT_STACK;
+ fun -> reg.varData.theStack = new stackHandler;
+ if (!checkNew(fun -> reg.varData.theStack)) return BR_ERROR;
+ fun -> reg.varData.theStack -> first = NULL;
+ fun -> reg.varData.theStack -> last = NULL;
+ fun -> reg.varData.theStack -> timesUsed = 1;
+ if (!getSavedGamesStack(fun -> reg.varData.theStack, newText)) return BR_ERROR;
+ delete newText;
+ newText = NULL;
+ return BR_CONTINUE;
+}
+
+builtIn(saveGame) {
+ UNUSEDALL
+
+ if (frozenStuff) {
+ fatal("Can't save game state while the engine is frozen");
+ }
+
+ loadNow = getTextFromAnyVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+
+ char *aaaaa = encodeFilename(loadNow);
+ delete[] loadNow;
+ if (failSecurityCheck(aaaaa)) return BR_ERROR; // Won't fail if encoded, how cool is that? OK, not very.
+
+ loadNow = joinStrings(":", aaaaa);
+ delete[] aaaaa;
+
+ setVariable(fun -> reg, SVT_INT, 0);
+ saverFunc = fun;
+ return BR_KEEP_AND_PAUSE;
+}
+
+builtIn(fileExists) {
+ UNUSEDALL
+ loadNow = getTextFromAnyVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ char *aaaaa = encodeFilename(loadNow);
+ delete loadNow;
+ if (failSecurityCheck(aaaaa)) return BR_ERROR;
+#if ALLOW_FILE
+ FILE *fp = fopen(aaaaa, "rb");
+ if (!fp) {
+ char currentDir[1000];
+ if (!getcwd(currentDir, 998)) {
+ debugOut("Can't get current directory.\n");
+ }
+
+ if (chdir(gamePath)) {
+ debugOut("Error: Failed changing to directory %s\n", gamePath);
+ }
+ fp = fopen(aaaaa, "rb");
+ if (chdir(currentDir)) {
+ debugOut("Error: Failed changing to directory %s\n", currentDir);
+ }
+ }
+ // Return value
+ setVariable(fun -> reg, SVT_INT, (fp != NULL));
+ if (fp) fclose(fp);
+ delete[] aaaaa;
+ loadNow = NULL;
+#endif
+ return BR_CONTINUE;
+}
+
+builtIn(loadGame) {
+ UNUSEDALL
+ char *aaaaa = getTextFromAnyVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ loadNow = encodeFilename(aaaaa);
+ delete aaaaa;
+
+ if (frozenStuff) {
+ fatal("Can't load a saved game while the engine is frozen");
+ }
+#if ALLOW_FILE
+ if (failSecurityCheck(loadNow)) return BR_ERROR;
+ FILE *fp = fopen(loadNow, "rb");
+ if (fp) {
+ fclose(fp);
+ return BR_KEEP_AND_PAUSE;
+ }
+ delete loadNow;
+ loadNow = NULL;
+#endif
+ return BR_CONTINUE;
+}
+
+//--------------------------------------
+#pragma mark -
+#pragma mark Background image - Painting
+
+builtIn(blankScreen) {
+ UNUSEDALL
+ blankScreen(0, 0, sceneWidth, sceneHeight);
+ return BR_CONTINUE;
+}
+
+builtIn(blankArea) {
+ UNUSEDALL
+ int x1, y1, x2, y2;
+ if (!getValueType(y2, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x2, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(y1, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x1, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ blankScreen(x1, y1, x2, y2);
+ return BR_CONTINUE;
+}
+
+builtIn(darkBackground) {
+ UNUSEDALL
+ darkScreen();
+ return BR_CONTINUE;
+}
+
+builtIn(addOverlay) {
+ UNUSEDALL
+ int fileNumber, xPos, yPos;
+ if (!getValueType(yPos, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(xPos, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(fileNumber, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ loadBackDrop(fileNumber, xPos, yPos);
+ return BR_CONTINUE;
+}
+
+builtIn(mixOverlay) {
+ UNUSEDALL
+ int fileNumber, xPos, yPos;
+ if (!getValueType(yPos, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(xPos, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(fileNumber, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ mixBackDrop(fileNumber, xPos, yPos);
+ return BR_CONTINUE;
+}
+
+builtIn(pasteImage) {
+ UNUSEDALL
+ int x, y;
+ if (!getValueType(y, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ personaAnimation *pp = getAnimationFromVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ if (pp == NULL) return BR_CONTINUE;
+
+ pasteCursor(x, y, pp);
+ return BR_CONTINUE;
+}
+
+#pragma mark -
+#pragma mark Background Image - Scrolling
+
+builtIn(setSceneDimensions) {
+ UNUSEDALL
+ int x, y;
+ if (!getValueType(y, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (resizeBackdrop(x, y)) {
+ blankScreen(0, 0, x, y);
+ return BR_CONTINUE;
+ }
+ fatal("Out of memory creating new backdrop.");
+ return BR_ERROR;
+}
+
+builtIn(aimCamera) {
+ UNUSEDALL
+ if (!getValueType(cameraY, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(cameraX, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ cameraX -= (float)(winWidth >> 1) / cameraZoom;
+ cameraY -= (float)(winHeight >> 1) / cameraZoom;
+
+ if (cameraX < 0) cameraX = 0;
+ else if (cameraX > sceneWidth - (float)winWidth / cameraZoom) cameraX = sceneWidth - (float)winWidth / cameraZoom;
+ if (cameraY < 0) cameraY = 0;
+ else if (cameraY > sceneHeight - (float)winHeight / cameraZoom) cameraY = sceneHeight - (float)winHeight / cameraZoom;
+ return BR_CONTINUE;
+}
+
+
+builtIn(zoomCamera) {
+ UNUSEDALL
+ int z;
+ if (!getValueType(z, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ input.mouseX = input.mouseX * cameraZoom;
+ input.mouseY = input.mouseY * cameraZoom;
+
+
+ cameraZoom = (float) z * 0.01;
+ if ((float) winWidth / cameraZoom > sceneWidth) cameraZoom = (float)winWidth / sceneWidth;
+ if ((float) winHeight / cameraZoom > sceneHeight) cameraZoom = (float)winHeight / sceneHeight;
+ setPixelCoords(false);
+
+ input.mouseX = input.mouseX / cameraZoom;
+ input.mouseY = input.mouseY / cameraZoom;
+
+ return BR_CONTINUE;
+}
+
+#pragma mark -
+#pragma mark Variables
+
+
+builtIn(pickOne) {
+ UNUSEDALL
+ if (!numParams) {
+ fatal("Built-in function should have at least 1 parameter");
+ return BR_ERROR;
+ }
+
+ int i;
+#if 0
+ i = rand() % numParams;
+#endif
+
+ // Return value
+ while (numParams --) {
+ if (i == numParams) copyVariable(fun -> stack -> thisVar, fun -> reg);
+ trimStack(fun -> stack);
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(substring) {
+ UNUSEDALL
+ char *wholeString;
+ char *newString;
+ int start, length;
+
+ //debugOut ("BUILTIN: substring\n");
+
+ if (!getValueType(length, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(start, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ wholeString = getTextFromAnyVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+
+ if (u8_strlen(wholeString) < start + length) {
+ length = u8_strlen(wholeString) - start;
+ if (u8_strlen(wholeString) < start) {
+ start = 0;
+ }
+ }
+ if (length < 0) {
+ length = 0;
+ }
+
+ int startoffset = u8_offset(wholeString, start);
+ int endoffset = u8_offset(wholeString, start + length);
+
+ newString = new char[endoffset - startoffset + 1];
+ if (!checkNew(newString)) {
+ return BR_ERROR;
+ }
+
+ memcpy(newString, wholeString + startoffset, endoffset - startoffset);
+ newString[endoffset - startoffset] = 0;
+
+ makeTextVar(fun -> reg, newString);
+ delete newString;
+ return BR_CONTINUE;
+}
+
+builtIn(stringLength) {
+ UNUSEDALL
+ char *newText = getTextFromAnyVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, stringLength(newText));
+ delete[] newText;
+ return BR_CONTINUE;
+}
+
+builtIn(newStack) {
+ UNUSEDALL
+ unlinkVar(fun -> reg);
+
+ // Return value
+ fun -> reg.varType = SVT_STACK;
+ fun -> reg.varData.theStack = new stackHandler;
+ if (!checkNew(fun -> reg.varData.theStack)) return BR_ERROR;
+ fun -> reg.varData.theStack -> first = NULL;
+ fun -> reg.varData.theStack -> last = NULL;
+ fun -> reg.varData.theStack -> timesUsed = 1;
+ while (numParams --) {
+ if (!addVarToStack(fun -> stack -> thisVar, fun -> reg.varData.theStack -> first)) return BR_ERROR;
+ if (fun -> reg.varData.theStack -> last == NULL) {
+ fun -> reg.varData.theStack -> last = fun -> reg.varData.theStack -> first;
+ }
+ trimStack(fun -> stack);
+ }
+ return BR_CONTINUE;
+}
+
+// wait is exactly the same function, but limited to 2 parameters
+#define builtIn_wait builtIn_newStack
+
+builtIn(stackSize) {
+ UNUSEDALL
+ switch (fun -> stack -> thisVar.varType) {
+ case SVT_STACK:
+ // Return value
+ setVariable(fun -> reg, SVT_INT, stackSize(fun -> stack -> thisVar.varData.theStack));
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+
+ case SVT_FASTARRAY:
+ // Return value
+ setVariable(fun -> reg, SVT_INT, fun -> stack -> thisVar.varData.fastArray -> size);
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+
+ default:
+ break;
+ }
+ fatal("Parameter isn't a stack or a fast array.");
+ return BR_ERROR;
+}
+
+builtIn(copyStack) {
+ UNUSEDALL
+ if (fun -> stack -> thisVar.varType != SVT_STACK) {
+ fatal("Parameter isn't a stack.");
+ return BR_ERROR;
+ }
+ // Return value
+ if (!copyStack(fun -> stack -> thisVar, fun -> reg)) return BR_ERROR;
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+}
+
+builtIn(pushToStack) {
+ UNUSEDALL
+ if (fun -> stack -> next -> thisVar.varType != SVT_STACK) {
+ fatal("Parameter isn't a stack");
+ return BR_ERROR;
+ }
+
+ if (!addVarToStack(fun -> stack -> thisVar, fun -> stack -> next -> thisVar.varData.theStack -> first))
+ return BR_ERROR;
+
+ if (fun -> stack -> next -> thisVar.varData.theStack -> first -> next == NULL)
+ fun -> stack -> next -> thisVar.varData.theStack -> last = fun -> stack -> next -> thisVar.varData.theStack -> first;
+
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+}
+
+builtIn(enqueue) {
+ UNUSEDALL
+ if (fun -> stack -> next -> thisVar.varType != SVT_STACK) {
+ fatal("Parameter isn't a stack");
+ return BR_ERROR;
+ }
+
+ if (fun -> stack -> next -> thisVar.varData.theStack -> first == NULL) {
+ if (!addVarToStack(fun -> stack -> thisVar, fun -> stack -> next -> thisVar.varData.theStack -> first))
+ return BR_ERROR;
+
+ fun -> stack -> next -> thisVar.varData.theStack -> last = fun -> stack -> next -> thisVar.varData.theStack -> first;
+ } else {
+ if (!addVarToStack(fun -> stack -> thisVar,
+ fun -> stack -> next -> thisVar.varData.theStack -> last -> next))
+ return BR_ERROR;
+ fun -> stack -> next -> thisVar.varData.theStack -> last = fun -> stack -> next -> thisVar.varData.theStack -> last -> next;
+ }
+
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+}
+
+builtIn(deleteFromStack) {
+ UNUSEDALL
+ if (fun -> stack -> next -> thisVar.varType != SVT_STACK) {
+ fatal("Parameter isn't a stack.");
+ return BR_ERROR;
+ }
+
+ // Return value
+ setVariable(fun -> reg, SVT_INT,
+ deleteVarFromStack(fun -> stack -> thisVar,
+ fun -> stack -> next -> thisVar.varData.theStack -> first, false));
+
+ // Horrible hacking because 'last' value might now be wrong!
+ fun->stack->next->thisVar.varData.theStack->last = stackFindLast(fun->stack->next->thisVar.varData.theStack->first);
+
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+}
+
+builtIn(deleteAllFromStack) {
+ UNUSEDALL
+ if (fun -> stack -> next -> thisVar.varType != SVT_STACK) {
+ fatal("Parameter isn't a stack.");
+ return BR_ERROR;
+ }
+
+ // Return value
+ setVariable(fun -> reg, SVT_INT,
+ deleteVarFromStack(fun -> stack -> thisVar,
+ fun -> stack -> next -> thisVar.varData.theStack -> first, true));
+
+ // Horrible hacking because 'last' value might now be wrong!
+ fun->stack->next->thisVar.varData.theStack->last = stackFindLast(fun->stack->next->thisVar.varData.theStack->first);
+
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+}
+
+builtIn(popFromStack) {
+ UNUSEDALL
+ if (fun -> stack -> thisVar.varType != SVT_STACK) {
+ fatal("Parameter isn't a stack.");
+ return BR_ERROR;
+ }
+ if (fun -> stack -> thisVar.varData.theStack -> first == NULL) {
+ fatal("The stack's empty.");
+ return BR_ERROR;
+ }
+
+ // Return value
+ copyVariable(fun -> stack -> thisVar.varData.theStack -> first -> thisVar, fun -> reg);
+ trimStack(fun -> stack -> thisVar.varData.theStack -> first);
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+}
+
+builtIn(peekStart) {
+ UNUSEDALL
+ if (fun -> stack -> thisVar.varType != SVT_STACK) {
+ fatal("Parameter isn't a stack.");
+ return BR_ERROR;
+ }
+ if (fun -> stack -> thisVar.varData.theStack -> first == NULL) {
+ fatal("The stack's empty.");
+ return BR_ERROR;
+ }
+
+ // Return value
+ copyVariable(fun -> stack -> thisVar.varData.theStack -> first -> thisVar, fun -> reg);
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+}
+
+builtIn(peekEnd) {
+ UNUSEDALL
+ if (fun -> stack -> thisVar.varType != SVT_STACK) {
+ fatal("Parameter isn't a stack.");
+ return BR_ERROR;
+ }
+ if (fun -> stack -> thisVar.varData.theStack -> first == NULL) {
+ fatal("The stack's empty.");
+ return BR_ERROR;
+ }
+
+ // Return value
+ copyVariable(fun -> stack -> thisVar.varData.theStack -> last -> thisVar, fun -> reg);
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+}
+
+builtIn(random) {
+ UNUSEDALL
+ int num;
+
+ if (!getValueType(num, SVT_INT, fun -> stack -> thisVar))
+ return BR_ERROR;
+
+ trimStack(fun -> stack);
+ if (num <= 0) num = 1;
+ setVariable(fun -> reg, SVT_INT, 0 /*rand() % num*/); //TODO:false value
+ return BR_CONTINUE;
+}
+
+static bool getRGBParams(int &red, int &green, int &blue, loadedFunction *fun) {
+ if (!getValueType(blue, SVT_INT, fun -> stack -> thisVar)) return false;
+ trimStack(fun -> stack);
+ if (!getValueType(green, SVT_INT, fun -> stack -> thisVar)) return false;
+ trimStack(fun -> stack);
+ if (!getValueType(red, SVT_INT, fun -> stack -> thisVar)) return false;
+ trimStack(fun -> stack);
+ return true;
+}
+
+builtIn(setStatusColour) {
+ UNUSEDALL
+ int red, green, blue;
+
+ if (!getRGBParams(red, green, blue, fun))
+ return BR_ERROR;
+
+ statusBarColour((byte) red, (byte) green, (byte) blue);
+ return BR_CONTINUE;
+}
+
+builtIn(setLitStatusColour) {
+ UNUSEDALL
+ int red, green, blue;
+
+ if (!getRGBParams(red, green, blue, fun))
+ return BR_ERROR;
+
+ statusBarLitColour((byte) red, (byte) green, (byte) blue);
+ return BR_CONTINUE;
+}
+
+builtIn(setPasteColour) {
+ UNUSEDALL
+ int red, green, blue;
+
+ if (!getRGBParams(red, green, blue, fun))
+ return BR_ERROR;
+
+ setFontColour(pastePalette, (byte) red, (byte) green, (byte) blue);
+ return BR_CONTINUE;
+}
+
+builtIn(setBlankColour) {
+ UNUSEDALL
+ int red, green, blue;
+
+ if (!getRGBParams(red, green, blue, fun))
+ return BR_ERROR;
+
+ currentBlankColour = makeColour(red & 255, green & 255, blue & 255);
+ setVariable(fun -> reg, SVT_INT, 1);
+ return BR_CONTINUE;
+}
+
+builtIn(setBurnColour) {
+ UNUSEDALL
+ int red, green, blue;
+
+ if (!getRGBParams(red, green, blue, fun))
+ return BR_ERROR;
+
+ currentBurnR = red;
+ currentBurnG = green;
+ currentBurnB = blue;
+ setVariable(fun -> reg, SVT_INT, 1);
+ return BR_CONTINUE;
+}
+
+
+builtIn(setFont) {
+ UNUSEDALL
+ int fileNumber, newHeight;
+ if (!getValueType(newHeight, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ // newDebug (" Height:", newHeight);
+ trimStack(fun -> stack);
+ char *newText = getTextFromAnyVar(fun -> stack -> thisVar);
+ if (!newText) return BR_ERROR;
+ // newDebug (" Character supported:", newText);
+ trimStack(fun -> stack);
+ if (!getValueType(fileNumber, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ // newDebug (" File:", fileNumber);
+ trimStack(fun -> stack);
+ if (!loadFont(fileNumber, newText, newHeight)) return BR_ERROR;
+ // newDebug (" Done!");
+ delete newText;
+
+ return BR_CONTINUE;
+}
+
+builtIn(inFont) {
+ UNUSEDALL
+ char *newText = getTextFromAnyVar(fun -> stack -> thisVar);
+ if (!newText) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ // Return value
+
+ setVariable(fun -> reg, SVT_INT, isInFont(newText));
+ return BR_CONTINUE;
+}
+
+builtIn(pasteString) {
+ UNUSEDALL
+ char *newText = getTextFromAnyVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ int y, x;
+ if (!getValueType(y, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (x == IN_THE_CENTRE) x = (winWidth - stringWidth(newText)) >> 1;
+ fixFont(pastePalette);
+ pasteStringToBackdrop(newText, x, y, pastePalette);
+ delete[] newText;
+ return BR_CONTINUE;
+}
+
+builtIn(anim) {
+ UNUSEDALL
+ if (numParams < 2) {
+ fatal("Built-in function anim() must have at least 2 parameters.");
+ return BR_ERROR;
+ }
+
+ // First store the frame numbers and take 'em off the stack
+ personaAnimation *ba = createPersonaAnim(numParams - 1, fun -> stack);
+
+ // Only remaining paramter is the file number
+ int fileNumber;
+ if (!getValueType(fileNumber, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ // Load the required sprite bank
+ loadedSpriteBank *sprBanky = loadBankForAnim(fileNumber);
+ if (!sprBanky) return BR_ERROR; // File not found, fatal done already
+ setBankFile(ba, sprBanky);
+
+ // Return value
+ newAnimationVariable(fun -> reg, ba);
+
+ return BR_CONTINUE;
+}
+
+builtIn(costume) {
+ UNUSEDALL
+ persona *newPersona = new persona;
+ if (!checkNew(newPersona)) return BR_ERROR;
+ newPersona -> numDirections = numParams / 3;
+ if (numParams == 0 || newPersona -> numDirections * 3 != numParams) {
+ fatal("Illegal number of parameters (should be greater than 0 and divisible by 3)");
+ return BR_ERROR;
+ }
+ int iii;
+ newPersona -> animation = new personaAnimation * [numParams];
+ if (!checkNew(newPersona -> animation)) return BR_ERROR;
+ for (iii = numParams - 1; iii >= 0; iii --) {
+ newPersona -> animation[iii] = getAnimationFromVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ }
+
+ // Return value
+ newCostumeVariable(fun -> reg, newPersona);
+ return BR_CONTINUE;
+}
+
+builtIn(launch) {
+ UNUSEDALL
+ char *newTextA = getTextFromAnyVar(fun -> stack -> thisVar);
+ if (!newTextA) return BR_ERROR;
+
+ char *newText = encodeFilename(newTextA);
+
+ trimStack(fun -> stack);
+ if (newTextA[0] == 'h' &&
+ newTextA[1] == 't' &&
+ newTextA[2] == 't' &&
+ newTextA[3] == 'p' &&
+ (newTextA[4] == ':' || (newTextA[4] == 's' && newTextA[5] == ':'))) {
+
+ // IT'S A WEBSITE!
+ launchMe = copyString(newTextA);
+ } else {
+ char *gameDir;
+#ifdef _WIN32
+ gameDir = joinStrings(gamePath, "\\");
+#else
+ gameDir = joinStrings(gamePath, "/");
+#endif
+ launchMe = joinStrings(gameDir, newText);
+ delete newText;
+ if (!launchMe) return BR_ERROR;
+ }
+ delete newTextA;
+ setGraphicsWindow(false);
+ setVariable(fun -> reg, SVT_INT, 1);
+ launchResult = &fun->reg;
+
+ return BR_KEEP_AND_PAUSE;
+}
+
+builtIn(pause) {
+ UNUSEDALL
+ int theTime;
+ if (!getValueType(theTime, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (theTime > 0) {
+ fun -> timeLeft = theTime - 1;
+ fun -> isSpeech = false;
+ return BR_KEEP_AND_PAUSE;
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(completeTimers) {
+ UNUSEDALL
+ completeTimers();
+ return BR_CONTINUE;
+}
+
+builtIn(callEvent) {
+ UNUSEDALL
+ int obj1, obj2;
+ if (!getValueType(obj2, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj1, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ int fNum = getCombinationFunction(obj1, obj2);
+
+ // Return value
+ if (fNum) {
+ setVariable(fun -> reg, SVT_FUNC, fNum);
+ return BR_CALLAFUNC;
+ }
+ setVariable(fun -> reg, SVT_INT, 0);
+ return BR_CONTINUE;
+}
+
+#if 0
+SDL_Event quit_event;
+#endif
+
+bool reallyWantToQuit = false;
+
+builtIn(quitGame) {
+ UNUSEDALL
+ reallyWantToQuit = true;
+#if 0
+ quit_event.type = SDL_QUIT;
+ SDL_PushEvent(&quit_event);
+#endif
+ return BR_CONTINUE;
+}
+
+
+#pragma mark -
+#pragma mark Movie functions
+
+// The old movie functions are deprecated and does nothing.
+builtIn(_rem_movieStart) {
+ UNUSEDALL
+ trimStack(fun -> stack);
+ return BR_CONTINUE;
+}
+
+builtIn(_rem_movieAbort) {
+ UNUSEDALL
+ setVariable(fun -> reg, SVT_INT, 0);
+ return BR_CONTINUE;
+}
+
+builtIn(_rem_moviePlaying) {
+ UNUSEDALL
+ setVariable(fun -> reg, SVT_INT, 0);
+ return BR_CONTINUE;
+}
+
+builtIn(playMovie) {
+ UNUSEDALL
+ int fileNumber, r;
+
+ if (movieIsPlaying) return BR_PAUSE;
+
+ if (!getValueType(fileNumber, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ r = playMovie(fileNumber);
+
+ setVariable(fun -> reg, SVT_INT, r);
+
+ if (r && (!fun->next)) {
+ restartFunction(fun);
+ return BR_ALREADY_GONE;
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(stopMovie) {
+ UNUSEDALL
+ int r;
+
+ r = stopMovie();
+
+ setVariable(fun -> reg, SVT_INT, 0);
+ return BR_CONTINUE;
+}
+
+builtIn(pauseMovie) {
+ UNUSEDALL
+ int r;
+
+ r = pauseMovie();
+
+ setVariable(fun -> reg, SVT_INT, 0);
+ return BR_CONTINUE;
+}
+
+
+#pragma mark -
+#pragma mark Audio functions
+
+builtIn(startMusic) {
+ UNUSEDALL
+ int fromTrack, musChan, fileNumber;
+ if (!getValueType(fromTrack, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(musChan, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(fileNumber, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!playMOD(fileNumber, musChan, fromTrack)) return BR_CONTINUE; //BR_ERROR;
+ return BR_CONTINUE;
+}
+
+builtIn(stopMusic) {
+ UNUSEDALL
+ int v;
+ if (!getValueType(v, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ stopMOD(v);
+ return BR_CONTINUE;
+}
+
+builtIn(setMusicVolume) {
+ UNUSEDALL
+ int musChan, v;
+ if (!getValueType(v, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(musChan, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setMusicVolume(musChan, v);
+ return BR_CONTINUE;
+}
+
+builtIn(setDefaultMusicVolume) {
+ UNUSEDALL
+ int v;
+ if (!getValueType(v, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setDefaultMusicVolume(v);
+ return BR_CONTINUE;
+}
+
+builtIn(playSound) {
+ UNUSEDALL
+ int fileNumber;
+ if (!getValueType(fileNumber, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!startSound(fileNumber, false)) return BR_CONTINUE; // Was BR_ERROR
+ return BR_CONTINUE;
+}
+builtIn(loopSound) {
+ UNUSEDALL
+ int fileNumber;
+
+ if (numParams < 1) {
+ fatal("Built-in function loopSound() must have at least 1 parameter.");
+ return BR_ERROR;
+ } else if (numParams < 2) {
+
+ if (!getValueType(fileNumber, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!startSound(fileNumber, true)) return BR_CONTINUE; // Was BR_ERROR
+ return BR_CONTINUE;
+ } else {
+ // We have more than one sound to play!
+
+ int doLoop = 2;
+ soundList *s = NULL;
+ soundList *old = NULL;
+
+ // Should we loop?
+ if (fun->stack->thisVar.varType != SVT_FILE) {
+ getValueType(doLoop, SVT_INT, fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ numParams--;
+ }
+ while (numParams) {
+ if (!getValueType(fileNumber, SVT_FILE, fun -> stack -> thisVar)) {
+ fatal("Illegal parameter given built-in function loopSound().");
+ return BR_ERROR;
+ }
+ s = new soundList;
+ if (!checkNew(s)) return BR_ERROR;
+
+ s-> next = old;
+ s-> prev = NULL;
+ s-> sound = fileNumber;
+ old = s;
+
+ trimStack(fun->stack);
+ numParams--;
+ }
+ while (s->next) s = s-> next;
+ if (doLoop > 1) {
+ s->next = old;
+ old->prev = s;
+ } else if (doLoop) {
+ s->next = s;
+ }
+ old->vol = -1;
+ playSoundList(old);
+ return BR_CONTINUE;
+ }
+}
+
+builtIn(stopSound) {
+ UNUSEDALL
+ int v;
+ if (!getValueType(v, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ huntKillSound(v);
+ return BR_CONTINUE;
+}
+
+builtIn(setDefaultSoundVolume) {
+ UNUSEDALL
+ int v;
+ if (!getValueType(v, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setDefaultSoundVolume(v);
+ return BR_CONTINUE;
+}
+
+builtIn(setSoundVolume) {
+ UNUSEDALL
+ int musChan, v;
+ if (!getValueType(v, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(musChan, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setSoundVolume(musChan, v);
+ return BR_CONTINUE;
+}
+
+
+builtIn(setSoundLoopPoints) {
+ UNUSEDALL
+ int musChan, theEnd, theStart;
+ if (!getValueType(theEnd, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(theStart, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(musChan, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setSoundLoop(musChan, theStart, theEnd);
+ return BR_CONTINUE;
+}
+
+#pragma mark -
+#pragma mark Extra room bits
+
+builtIn(setFloor) {
+ UNUSEDALL
+ if (fun -> stack -> thisVar.varType == SVT_FILE) {
+ int v;
+ getValueType(v, SVT_FILE, fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ if (!setFloor(v)) return BR_ERROR;
+ } else {
+ trimStack(fun -> stack);
+ setFloorNull();
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(showFloor) {
+ UNUSEDALL
+ drawFloor();
+ return BR_CONTINUE;
+}
+
+builtIn(setZBuffer) {
+ UNUSEDALL
+ if (fun -> stack -> thisVar.varType == SVT_FILE) {
+ int v;
+ getValueType(v, SVT_FILE, fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ if (!setZBuffer(v)) return BR_ERROR;
+ } else {
+ trimStack(fun -> stack);
+ killZBuffer();
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(setLightMap) {
+ UNUSEDALL
+ switch (numParams) {
+ case 2:
+ if (!getValueType(lightMapMode, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ lightMapMode %= LIGHTMAPMODE_NUM;
+ // No break;
+
+ case 1:
+ if (fun -> stack -> thisVar.varType == SVT_FILE) {
+ int v;
+ getValueType(v, SVT_FILE, fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ if (!loadLightMap(v)) return BR_ERROR;
+ setVariable(fun -> reg, SVT_INT, 1);
+ } else {
+ trimStack(fun -> stack);
+ killLightMap();
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ break;
+
+ default:
+ fatal("Function should have either 2 or 3 parameters");
+ return BR_ERROR;
+ }
+ return BR_CONTINUE;
+}
+
+
+#pragma mark -
+#pragma mark Objects
+
+builtIn(setSpeechMode) {
+ UNUSEDALL
+ if (!getValueType(speechMode, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (speechMode < 0 || speechMode > 2) {
+ fatal("Valid parameters are be SPEECHANDTEXT, SPEECHONLY or TEXTONLY");
+ return BR_ERROR;
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(somethingSpeaking) {
+ UNUSEDALL
+ int i = isThereAnySpeechGoingOn();
+ if (i == -1) {
+ setVariable(fun -> reg, SVT_INT, 0);
+ } else {
+ setVariable(fun -> reg, SVT_OBJTYPE, i);
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(skipSpeech) {
+ UNUSEDALL
+ killSpeechTimers();
+ return BR_CONTINUE;
+}
+
+builtIn(getOverObject) {
+ UNUSEDALL
+ if (overRegion)
+ // Return value
+ setVariable(fun -> reg, SVT_OBJTYPE, overRegion -> thisType -> objectNum);
+ else
+ // Return value
+ setVariable(fun -> reg, SVT_INT, 0);
+ return BR_CONTINUE;
+}
+
+builtIn(rename) {
+ UNUSEDALL
+ char *newText = getTextFromAnyVar(fun -> stack -> thisVar);
+ int objT;
+ if (!newText) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(objT, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ objectType *o = findObjectType(objT);
+ delete o -> screenName;
+ o -> screenName = newText;
+ return BR_CONTINUE;
+}
+
+builtIn(getObjectX) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ onScreenPerson *pers = findPerson(objectNumber);
+ if (pers) {
+ setVariable(fun -> reg, SVT_INT, pers -> x);
+ } else {
+ screenRegion *la = getRegionForObject(objectNumber);
+ if (la) {
+ setVariable(fun -> reg, SVT_INT, la -> sX);
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(getObjectY) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ onScreenPerson *pers = findPerson(objectNumber);
+ if (pers) {
+ setVariable(fun -> reg, SVT_INT, pers -> y);
+ } else {
+ screenRegion *la = getRegionForObject(objectNumber);
+ if (la) {
+ setVariable(fun -> reg, SVT_INT, la -> sY);
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ }
+ return BR_CONTINUE;
+}
+
+
+builtIn(addScreenRegion) {
+ UNUSEDALL
+ int sX, sY, x1, y1, x2, y2, di, objectNumber;
+ if (!getValueType(di, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(sY, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(sX, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(y2, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x2, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(y1, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x1, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (addScreenRegion(x1, y1, x2, y2, sX, sY, di, objectNumber)) return BR_CONTINUE;
+ return BR_ERROR;
+
+}
+
+builtIn(removeScreenRegion) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ removeScreenRegion(objectNumber);
+ return BR_CONTINUE;
+}
+
+builtIn(showBoxes) {
+ UNUSEDALL
+ showBoxes();
+ return BR_CONTINUE;
+}
+
+builtIn(removeAllScreenRegions) {
+ UNUSEDALL
+ killAllRegions();
+ return BR_CONTINUE;
+}
+
+builtIn(addCharacter) {
+ UNUSEDALL
+ persona *p;
+ int x, y, objectNumber;
+
+ p = getCostumeFromVar(fun -> stack -> thisVar);
+ if (p == NULL) return BR_ERROR;
+
+ trimStack(fun -> stack);
+ if (!getValueType(y, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (addPerson(x, y, objectNumber, p)) return BR_CONTINUE;
+ return BR_ERROR;
+}
+
+builtIn(hideCharacter) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setShown(false, objectNumber);
+ return BR_CONTINUE;
+}
+
+builtIn(showCharacter) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setShown(true, objectNumber);
+ return BR_CONTINUE;
+}
+
+builtIn(removeAllCharacters) {
+ UNUSEDALL
+ killSpeechTimers();
+ killMostPeople();
+ return BR_CONTINUE;
+}
+
+builtIn(setCharacterDrawMode) {
+ UNUSEDALL
+ int obj, di;
+ if (!getValueType(di, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setDrawMode(di, obj);
+ return BR_CONTINUE;
+}
+builtIn(setCharacterTransparency) {
+ UNUSEDALL
+ int obj, x;
+ if (!getValueType(x, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setPersonTransparency(obj, x);
+ return BR_CONTINUE;
+}
+builtIn(setCharacterColourise) {
+ UNUSEDALL
+ int obj, r, g, b, mix;
+ if (!getValueType(mix, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(b, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(g, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(r, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setPersonColourise(obj, r, g, b, mix);
+ return BR_CONTINUE;
+}
+
+builtIn(setScale) {
+ UNUSEDALL
+ int val1, val2;
+ if (!getValueType(val2, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(val1, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setScale((short int) val1, (short int) val2);
+ return BR_CONTINUE;
+}
+
+builtIn(stopCharacter) {
+ UNUSEDALL
+ int obj;
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ // Return value
+ setVariable(fun -> reg, SVT_INT, stopPerson(obj));
+ return BR_CONTINUE;
+}
+
+builtIn(pasteCharacter) {
+ UNUSEDALL
+ int obj;
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ onScreenPerson *thisPerson = findPerson(obj);
+ if (thisPerson) {
+ personaAnimation *myAnim;
+ myAnim = thisPerson -> myAnim;
+ if (myAnim != thisPerson -> lastUsedAnim) {
+ thisPerson -> lastUsedAnim = myAnim;
+ thisPerson -> frameNum = 0;
+ thisPerson -> frameTick = myAnim -> frames[0].howMany;
+ }
+
+ int fNum = myAnim -> frames[thisPerson -> frameNum].frameNum;
+ fixScaleSprite(thisPerson -> x, thisPerson -> y, myAnim -> theSprites -> bank.sprites[abs(fNum)], myAnim -> theSprites -> bank.myPalette, thisPerson, 0, 0, fNum < 0);
+ setVariable(fun -> reg, SVT_INT, 1);
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(animate) {
+ UNUSEDALL
+ int obj;
+ personaAnimation *pp = getAnimationFromVar(fun -> stack -> thisVar);
+ if (pp == NULL) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ animatePerson(obj, pp);
+ setVariable(fun -> reg, SVT_INT, timeForAnim(pp));
+ return BR_CONTINUE;
+}
+
+builtIn(setCostume) {
+ UNUSEDALL
+ int obj;
+ persona *pp = getCostumeFromVar(fun -> stack -> thisVar);
+ if (pp == NULL) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ animatePerson(obj, pp);
+ return BR_CONTINUE;
+}
+
+builtIn(floatCharacter) {
+ UNUSEDALL
+ int obj, di;
+ if (!getValueType(di, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, floatCharacter(di, obj));
+ return BR_CONTINUE;
+}
+
+builtIn(setCharacterWalkSpeed) {
+ UNUSEDALL
+ int obj, di;
+ if (!getValueType(di, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, setCharacterWalkSpeed(di, obj));
+ return BR_CONTINUE;
+}
+
+builtIn(turnCharacter) {
+ UNUSEDALL
+ int obj, di;
+ if (!getValueType(di, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, turnPersonToFace(obj, di));
+ return BR_CONTINUE;
+}
+
+builtIn(setCharacterExtra) {
+ UNUSEDALL
+ int obj, di;
+ if (!getValueType(di, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, setPersonExtra(obj, di));
+ return BR_CONTINUE;
+}
+
+builtIn(removeCharacter) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ removeOneCharacter(objectNumber);
+ return BR_CONTINUE;
+}
+
+static builtReturn moveChr(int numParams, loadedFunction *fun, bool force, bool immediate) {
+ switch (numParams) {
+ case 3: {
+ int x, y, objectNumber;
+
+ if (!getValueType(y, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ if (force) {
+ if (forceWalkingPerson(x, y, objectNumber, fun, -1)) return BR_PAUSE;
+ } else if (immediate) {
+ jumpPerson(x, y, objectNumber);
+ } else {
+ if (makeWalkingPerson(x, y, objectNumber, fun, -1)) return BR_PAUSE;
+ }
+ return BR_CONTINUE;
+ }
+
+ case 2: {
+ int toObj, objectNumber;
+ screenRegion *reggie;
+
+ if (!getValueType(toObj, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ reggie = getRegionForObject(toObj);
+ if (reggie == NULL) return BR_CONTINUE;
+
+ if (force) {
+ if (forceWalkingPerson(reggie -> sX, reggie -> sY, objectNumber, fun, reggie -> di)) return BR_PAUSE;
+ } else if (immediate) {
+ jumpPerson(reggie -> sX, reggie -> sY, objectNumber);
+ } else {
+ if (makeWalkingPerson(reggie -> sX, reggie -> sY, objectNumber, fun, reggie -> di)) return BR_PAUSE;
+ }
+ return BR_CONTINUE;
+ }
+
+ default:
+ fatal("Built-in function must have either 2 or 3 parameters.");
+ return BR_ERROR;
+ }
+}
+
+builtIn(moveCharacter) {
+ UNUSEDALL
+ return moveChr(numParams, fun, false, false);
+}
+
+builtIn(forceCharacter) {
+ UNUSEDALL
+ return moveChr(numParams, fun, true, false);
+}
+
+builtIn(jumpCharacter) {
+ UNUSEDALL
+ return moveChr(numParams, fun, false, true);
+}
+
+builtIn(clearStatus) {
+ UNUSEDALL
+ clearStatusBar();
+ return BR_CONTINUE;
+}
+
+builtIn(removeLastStatus) {
+ UNUSEDALL
+ killLastStatus();
+ return BR_CONTINUE;
+}
+
+builtIn(addStatus) {
+ UNUSEDALL
+ addStatusBar();
+ return BR_CONTINUE;
+}
+
+builtIn(statusText) {
+ UNUSEDALL
+ char *newText = getTextFromAnyVar(fun -> stack -> thisVar);
+ if (!newText) return BR_ERROR;
+ trimStack(fun -> stack);
+ setStatusBar(newText);
+ delete newText;
+ return BR_CONTINUE;
+}
+
+builtIn(lightStatus) {
+ UNUSEDALL
+ int val;
+ if (!getValueType(val, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setLitStatus(val);
+ return BR_CONTINUE;
+}
+
+builtIn(positionStatus) {
+ UNUSEDALL
+ int x, y;
+ if (!getValueType(y, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ positionStatus(x, y);
+ return BR_CONTINUE;
+}
+
+builtIn(alignStatus) {
+ UNUSEDALL
+ int val;
+ if (!getValueType(val, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ nowStatus -> alignStatus = (short) val;
+ return BR_CONTINUE;
+}
+
+static bool getFuncNumForCallback(int numParams, loadedFunction *fun, int &functionNum) {
+ switch (numParams) {
+ case 0:
+ functionNum = 0;
+ break;
+
+ case 1:
+ if (!getValueType(functionNum, SVT_FUNC, fun -> stack -> thisVar)) return false;
+ trimStack(fun -> stack);
+ break;
+
+ default:
+ fatal("Too many parameters.");
+ return false;
+ }
+ return true;
+}
+
+builtIn(onLeftMouse) {
+ UNUSEDALL
+ int functionNum;
+ if (getFuncNumForCallback(numParams, fun, functionNum)) {
+ currentEvents -> leftMouseFunction = functionNum;
+ return BR_CONTINUE;
+ }
+ return BR_ERROR;
+}
+
+builtIn(onLeftMouseUp) {
+ UNUSEDALL
+ int functionNum;
+ if (getFuncNumForCallback(numParams, fun, functionNum)) {
+ currentEvents -> leftMouseUpFunction = functionNum;
+ return BR_CONTINUE;
+ }
+ return BR_ERROR;
+}
+
+builtIn(onRightMouse) {
+ UNUSEDALL
+ int functionNum;
+ if (getFuncNumForCallback(numParams, fun, functionNum)) {
+ currentEvents -> rightMouseFunction = functionNum;
+ return BR_CONTINUE;
+ }
+ return BR_ERROR;
+}
+
+builtIn(onRightMouseUp) {
+ UNUSEDALL
+ int functionNum;
+ if (getFuncNumForCallback(numParams, fun, functionNum)) {
+ currentEvents -> rightMouseUpFunction = functionNum;
+ return BR_CONTINUE;
+ }
+ return BR_ERROR;
+}
+
+builtIn(onFocusChange) {
+ UNUSEDALL
+ int functionNum;
+ if (getFuncNumForCallback(numParams, fun, functionNum)) {
+ currentEvents -> focusFunction = functionNum;
+ return BR_CONTINUE;
+ }
+ return BR_ERROR;
+}
+
+builtIn(onMoveMouse) {
+ UNUSEDALL
+ int functionNum;
+ if (getFuncNumForCallback(numParams, fun, functionNum)) {
+ currentEvents -> moveMouseFunction = functionNum;
+ return BR_CONTINUE;
+ }
+ return BR_ERROR;
+}
+
+builtIn(onKeyboard) {
+ UNUSEDALL
+ int functionNum;
+ if (getFuncNumForCallback(numParams, fun, functionNum)) {
+ currentEvents -> spaceFunction = functionNum;
+ return BR_CONTINUE;
+ }
+ return BR_ERROR;
+}
+
+builtIn(spawnSub) {
+ UNUSEDALL
+ int functionNum;
+ if (getFuncNumForCallback(numParams, fun, functionNum)) {
+ if (!startNewFunctionNum(functionNum, 0, NULL, noStack)) return BR_ERROR;
+ return BR_CONTINUE;
+ }
+ return BR_ERROR;
+}
+
+builtIn(cancelSub) {
+ UNUSEDALL
+ int functionNum;
+ if (getFuncNumForCallback(numParams, fun, functionNum)) {
+ bool killedMyself;
+ cancelAFunction(functionNum, fun, killedMyself);
+ if (killedMyself) {
+ abortFunction(fun);
+ return BR_ALREADY_GONE;
+ }
+ return BR_CONTINUE;
+ }
+ return BR_ERROR;
+}
+
+builtIn(stringWidth) {
+ UNUSEDALL
+ char *theText = getTextFromAnyVar(fun -> stack -> thisVar);
+ if (!theText) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ // Return value
+ setVariable(fun -> reg, SVT_INT, stringWidth(theText));
+ delete theText;
+ return BR_CONTINUE;
+}
+
+builtIn(hardScroll) {
+ UNUSEDALL
+ int v;
+ if (!getValueType(v, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ hardScroll(v);
+ return BR_CONTINUE;
+}
+
+
+builtIn(isScreenRegion) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, getRegionForObject(objectNumber) != NULL);
+ return BR_CONTINUE;
+}
+
+builtIn(setSpeechSpeed) {
+ UNUSEDALL
+ int number;
+ if (!getValueType(number, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ speechSpeed = number * 0.01;
+ setVariable(fun -> reg, SVT_INT, 1);
+ return BR_CONTINUE;
+}
+
+builtIn(setFontSpacing) {
+ UNUSEDALL
+ int fontSpaceI;
+ if (!getValueType(fontSpaceI, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ fontSpace = fontSpaceI;
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, 1);
+ return BR_CONTINUE;
+}
+
+builtIn(transitionLevel) {
+ UNUSEDALL
+ int number;
+ if (!getValueType(number, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ if (number < 0)
+ brightnessLevel = 0;
+ else if (number > 255)
+ brightnessLevel = 255;
+ else
+ brightnessLevel = number;
+
+ setVariable(fun -> reg, SVT_INT, 1);
+ return BR_CONTINUE;
+}
+
+builtIn(captureAllKeys) {
+ UNUSEDALL
+ captureAllKeys = getBoolean(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, captureAllKeys);
+ return BR_CONTINUE;
+}
+
+
+builtIn(spinCharacter) {
+ UNUSEDALL
+ int number, objectNumber;
+ if (!getValueType(number, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ onScreenPerson *thisPerson = findPerson(objectNumber);
+ if (thisPerson) {
+ thisPerson -> wantAngle = number;
+ thisPerson -> spinning = true;
+ thisPerson -> continueAfterWalking = fun;
+ setVariable(fun -> reg, SVT_INT, 1);
+ return BR_PAUSE;
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ return BR_CONTINUE;
+ }
+}
+
+builtIn(getCharacterDirection) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ onScreenPerson *thisPerson = findPerson(objectNumber);
+ if (thisPerson) {
+ setVariable(fun -> reg, SVT_INT, thisPerson -> direction);
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(isCharacter) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ onScreenPerson *thisPerson = findPerson(objectNumber);
+ setVariable(fun -> reg, SVT_INT, thisPerson != NULL);
+ return BR_CONTINUE;
+}
+
+builtIn(normalCharacter) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ onScreenPerson *thisPerson = findPerson(objectNumber);
+ if (thisPerson) {
+ thisPerson -> myAnim = thisPerson -> myPersona -> animation[thisPerson -> direction];
+ setVariable(fun -> reg, SVT_INT, 1);
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(isMoving) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ onScreenPerson *thisPerson = findPerson(objectNumber);
+ if (thisPerson) {
+ setVariable(fun -> reg, SVT_INT, thisPerson -> walking);
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(fetchEvent) {
+ UNUSEDALL
+ int obj1, obj2;
+ if (!getValueType(obj2, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(obj1, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ int fNum = getCombinationFunction(obj1, obj2);
+
+ // Return value
+ if (fNum) {
+ setVariable(fun -> reg, SVT_FUNC, fNum);
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(deleteFile) {
+ UNUSEDALL
+
+ char *namNormal = getTextFromAnyVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ char *nam = encodeFilename(namNormal);
+ delete namNormal;
+ if (failSecurityCheck(nam)) return BR_ERROR;
+ setVariable(fun -> reg, SVT_INT, remove(nam));
+ delete nam;
+
+ return BR_CONTINUE;
+}
+
+builtIn(renameFile) {
+ UNUSEDALL
+ char *temp;
+
+ temp = getTextFromAnyVar(fun -> stack -> thisVar);
+ char *newnam = encodeFilename(temp);
+ trimStack(fun -> stack);
+ if (failSecurityCheck(newnam)) return BR_ERROR;
+ delete temp;
+
+ temp = getTextFromAnyVar(fun -> stack -> thisVar);
+ char *nam = encodeFilename(temp);
+ trimStack(fun -> stack);
+ if (failSecurityCheck(nam)) return BR_ERROR;
+ delete temp;
+
+ setVariable(fun -> reg, SVT_INT, rename(nam, newnam));
+ delete nam;
+ delete newnam;
+
+
+ return BR_CONTINUE;
+}
+
+builtIn(cacheSound) {
+ UNUSEDALL
+ int fileNumber;
+ if (!getValueType(fileNumber, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (cacheSound(fileNumber) == -1) return BR_ERROR;
+ return BR_CONTINUE;
+}
+
+builtIn(burnString) {
+ UNUSEDALL
+ char *newText = getTextFromAnyVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ int y, x;
+ if (!getValueType(y, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (x == IN_THE_CENTRE) x = (winWidth - stringWidth(newText)) >> 1;
+ fixFont(pastePalette);
+ burnStringToBackdrop(newText, x, y, pastePalette);
+ delete[] newText;
+ return BR_CONTINUE;
+}
+
+builtIn(setCharacterSpinSpeed) {
+ UNUSEDALL
+ int speed, who;
+ if (!getValueType(speed, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(who, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ onScreenPerson *thisPerson = findPerson(who);
+
+ if (thisPerson) {
+ thisPerson -> spinSpeed = speed;
+ setVariable(fun -> reg, SVT_INT, 1);
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(setCharacterAngleOffset) {
+ UNUSEDALL
+ int angle, who;
+ if (!getValueType(angle, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(who, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ onScreenPerson *thisPerson = findPerson(who);
+
+ if (thisPerson) {
+ thisPerson -> angleOffset = angle;
+ setVariable(fun -> reg, SVT_INT, 1);
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ return BR_CONTINUE;
+}
+
+
+builtIn(transitionMode) {
+ UNUSEDALL
+ int n;
+ if (!getValueType(n, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ fadeMode = n;
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, 1);
+ return BR_CONTINUE;
+}
+
+
+// Removed function - does nothing
+builtIn(_rem_updateDisplay) {
+ UNUSEDALL
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, true);
+ return BR_CONTINUE;
+}
+
+builtIn(getSoundCache) {
+ UNUSEDALL
+ fun -> reg.varType = SVT_STACK;
+ fun -> reg.varData.theStack = new stackHandler;
+ if (!checkNew(fun -> reg.varData.theStack)) return BR_ERROR;
+ fun -> reg.varData.theStack -> first = NULL;
+ fun -> reg.varData.theStack -> last = NULL;
+ fun -> reg.varData.theStack -> timesUsed = 1;
+ if (!getSoundCacheStack(fun -> reg.varData.theStack)) return BR_ERROR;
+ return BR_CONTINUE;
+}
+
+builtIn(saveCustomData) {
+ UNUSEDALL
+ // saveCustomData (thisStack, fileName);
+ char *fileNameB = getTextFromAnyVar(fun -> stack -> thisVar);
+ if (!checkNew(fileNameB)) return BR_ERROR;
+
+ char *fileName = encodeFilename(fileNameB);
+ delete fileNameB;
+
+ if (failSecurityCheck(fileName)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ if (fun -> stack -> thisVar.varType != SVT_STACK) {
+ fatal("First parameter isn't a stack");
+ return BR_ERROR;
+ }
+ if (!stackToFile(fileName, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ delete fileName;
+ return BR_CONTINUE;
+}
+
+builtIn(loadCustomData) {
+ UNUSEDALL
+
+ char *newTextA = getTextFromAnyVar(fun -> stack -> thisVar);
+ if (!checkNew(newTextA)) return BR_ERROR;
+
+ char *newText = encodeFilename(newTextA);
+ delete newTextA;
+
+ if (failSecurityCheck(newText)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ unlinkVar(fun -> reg);
+ fun -> reg.varType = SVT_STACK;
+ fun -> reg.varData.theStack = new stackHandler;
+ if (!checkNew(fun -> reg.varData.theStack)) return BR_ERROR;
+ fun -> reg.varData.theStack -> first = NULL;
+ fun -> reg.varData.theStack -> last = NULL;
+ fun -> reg.varData.theStack -> timesUsed = 1;
+ if (!fileToStack(newText, fun -> reg.varData.theStack)) return BR_ERROR;
+ delete newText;
+ return BR_CONTINUE;
+}
+
+builtIn(setCustomEncoding) {
+ UNUSEDALL
+ int n;
+ if (!getValueType(n, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ saveEncoding = n;
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, 1);
+ return BR_CONTINUE;
+}
+
+builtIn(freeSound) {
+ UNUSEDALL
+ int v;
+ if (!getValueType(v, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ huntKillFreeSound(v);
+ return BR_CONTINUE;
+}
+
+builtIn(parallaxAdd) {
+ UNUSEDALL
+ if (frozenStuff) {
+ fatal("Can't set background parallax image while frozen");
+ return BR_ERROR;
+ } else {
+ int wrapX, wrapY, v;
+ if (!getValueType(wrapY, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(wrapX, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(v, SVT_FILE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ if (!loadParallax(v, wrapX, wrapY)) return BR_ERROR;
+ setVariable(fun -> reg, SVT_INT, 1);
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(parallaxClear) {
+ UNUSEDALL
+ killParallax();
+ setVariable(fun -> reg, SVT_INT, 1);
+ return BR_CONTINUE;
+}
+
+builtIn(getPixelColour) {
+ UNUSEDALL
+ int x, y;
+ if (!getValueType(y, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ unlinkVar(fun -> reg);
+ fun -> reg.varType = SVT_STACK;
+ fun -> reg.varData.theStack = new stackHandler;
+ if (!checkNew(fun -> reg.varData.theStack)) return BR_ERROR;
+ fun -> reg.varData.theStack -> first = NULL;
+ fun -> reg.varData.theStack -> last = NULL;
+ fun -> reg.varData.theStack -> timesUsed = 1;
+ if (!getRGBIntoStack(x, y, fun -> reg.varData.theStack)) return BR_ERROR;
+
+ return BR_CONTINUE;
+}
+
+builtIn(makeFastArray) {
+ UNUSEDALL
+ switch (fun -> stack -> thisVar.varType) {
+ case SVT_STACK: {
+ bool success = makeFastArrayFromStack(fun -> reg, fun -> stack -> thisVar.varData.theStack);
+ trimStack(fun -> stack);
+ return success ? BR_CONTINUE : BR_ERROR;
+ }
+ break;
+
+ case SVT_INT: {
+ int i = fun -> stack -> thisVar.varData.intValue;
+ trimStack(fun -> stack);
+ return makeFastArraySize(fun -> reg, i) ? BR_CONTINUE : BR_ERROR;
+ }
+ break;
+
+ default:
+ break;
+ }
+ fatal("Parameter must be a number or a stack.");
+ return BR_ERROR;
+}
+
+builtIn(getCharacterScale) {
+ UNUSEDALL
+ int objectNumber;
+ if (!getValueType(objectNumber, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ onScreenPerson *pers = findPerson(objectNumber);
+ if (pers) {
+ setVariable(fun -> reg, SVT_INT, pers -> scale * 100);
+ } else {
+ setVariable(fun -> reg, SVT_INT, 0);
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(getLanguageID) {
+ UNUSEDALL
+ setVariable(fun -> reg, SVT_INT, gameSettings.languageID);
+ return BR_CONTINUE;
+}
+
+// Removed function
+builtIn(_rem_launchWith) {
+ UNUSEDALL
+
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, false);
+
+ return BR_CONTINUE;
+
+}
+
+builtIn(getFramesPerSecond) {
+ UNUSEDALL
+ setVariable(fun -> reg, SVT_INT, lastFramesPerSecond);
+ return BR_CONTINUE;
+}
+
+builtIn(showThumbnail) {
+ UNUSEDALL
+ int x, y;
+ if (!getValueType(y, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(x, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+
+ // Encode the name!Encode the name!
+ char *aaaaa = getTextFromAnyVar(fun -> stack -> thisVar);
+ // deb ("Got name:", aaaaa;)
+ trimStack(fun -> stack);
+ // deb ("About to encode", aaaaa);
+ char *file = encodeFilename(aaaaa);
+ // deb ("Made new name", file);
+ // deb ("aaaaa is still ", aaaaa);
+ delete[] aaaaa;
+ // deb ("Deleted", "aaaaa");
+ showThumbnail(file, x, y);
+ delete[] file;
+ return BR_CONTINUE;
+}
+
+builtIn(setThumbnailSize) {
+ UNUSEDALL
+ if (!getValueType(thumbHeight, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(thumbWidth, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (thumbWidth < 0 || thumbHeight < 0 || thumbWidth > winWidth || thumbHeight > winHeight) {
+ char buff[50];
+ sprintf(buff, "%d x %d", thumbWidth, thumbHeight);
+ fatal("Invalid thumbnail size", buff);
+ return BR_ERROR;
+ }
+ return BR_CONTINUE;
+}
+
+builtIn(hasFlag) {
+ UNUSEDALL
+ int objNum, flagIndex;
+ if (!getValueType(flagIndex, SVT_INT, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ if (!getValueType(objNum, SVT_OBJTYPE, fun -> stack -> thisVar)) return BR_ERROR;
+ trimStack(fun -> stack);
+ objectType *objT = findObjectType(objNum);
+ if (!objT) return BR_ERROR;
+ setVariable(fun -> reg, SVT_INT, objT->flags & (1 << flagIndex));
+ return BR_CONTINUE;
+}
+
+builtIn(snapshotGrab) {
+ UNUSEDALL
+ return snapshot() ? BR_CONTINUE : BR_ERROR;
+}
+
+builtIn(snapshotClear) {
+ UNUSEDALL
+ nosnapshot();
+ return BR_CONTINUE;
+}
+
+builtIn(bodgeFilenames) {
+ UNUSEDALL
+ bool lastValue = allowAnyFilename;
+ allowAnyFilename = getBoolean(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, lastValue);
+ return BR_CONTINUE;
+}
+
+// Deprecated - does nothing.
+builtIn(_rem_registryGetString) {
+ UNUSEDALL
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ setVariable(fun -> reg, SVT_INT, 0);
+
+ return BR_CONTINUE;
+}
+
+builtIn(quitWithFatalError) {
+ UNUSEDALL
+ char *mess = getTextFromAnyVar(fun -> stack -> thisVar);
+ trimStack(fun -> stack);
+ fatal(mess);
+ return BR_ERROR;
+}
+
+builtIn(_rem_setCharacterAA) {
+ UNUSEDALL
+
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+
+ return BR_CONTINUE;
+}
+
+builtIn(_rem_setMaximumAA) {
+ UNUSEDALL
+
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+
+ return BR_CONTINUE;
+
+}
+
+builtIn(setBackgroundEffect) {
+ UNUSEDALL
+ bool done = blur_createSettings(numParams, fun->stack);
+ setVariable(fun -> reg, SVT_INT, done ? 1 : 0);
+ return BR_CONTINUE;
+}
+
+builtIn(doBackgroundEffect) {
+ UNUSEDALL
+ bool done = blurScreen();
+ setVariable(fun -> reg, SVT_INT, done ? 1 : 0);
+ return BR_CONTINUE;
+}
+
+#pragma mark -
+#pragma mark Other functions
+
+//-------------------------------------
+
+#define FUNC(special,name) {builtIn_ ## name},
+static builtInFunctionData builtInFunctionArray[] = {
+#include "CommonCode/functionlist.h"
+};
+#undef FUNC
+
+#define FUNC(special,name) {#name},
+char builtInFunctionNames[][25] = {
+#include "CommonCode/functionlist.h"
+};
+#undef FUNC
+
+#define NUM_FUNCS (sizeof (builtInFunctionArray) / sizeof (builtInFunctionArray[0]))
+
+
+
+builtReturn callBuiltIn(int whichFunc, int numParams, loadedFunction *fun) {
+ // fprintf (stderr, "Calling function %d: %s\n", whichFunc, builtInFunctionNames[whichFunc]); fflush (stderr);
+ if (numBIFNames) {
+
+ // deb ("IN:", (fun -> originalNumber < numUserFunc) ? allUserFunc[fun -> originalNumber] : "Unknown user function");
+ // deb ("GO:", (whichFunc < numBIFNames) ? allBIFNames[whichFunc] : "Unknown built-in function");
+
+ setFatalInfo(
+ (fun -> originalNumber < numUserFunc) ? allUserFunc[fun -> originalNumber] : "Unknown user function",
+ (whichFunc < numBIFNames) ? allBIFNames[whichFunc] : "Unknown built-in function");
+ }
+
+ if (whichFunc < NUM_FUNCS) {
+ if (paramNum[whichFunc] != -1) {
+ if (paramNum[whichFunc] != numParams) {
+ char buff[100];
+ sprintf(buff, "Built in function must have %i parameter%s",
+ paramNum[whichFunc],
+ (paramNum[whichFunc] == 1) ? "" : "s");
+
+ fatal(copyString(buff));
+ return BR_ERROR;
+ }
+ }
+
+ if (builtInFunctionArray[whichFunc].func) {
+ //fprintf (stderr, "Calling %i: %s\n", whichFunc, builtInFunctionNames[whichFunc]);
+ return builtInFunctionArray[whichFunc].func(numParams, fun);
+ }
+ }
+
+ fatal("Unknown / unimplemented built-in function.");
+ return BR_ERROR;
+}
+
diff --git a/engines/sludge/builtin.h b/engines/sludge/builtin.h
new file mode 100644
index 0000000000..8f5ac5972e
--- /dev/null
+++ b/engines/sludge/builtin.h
@@ -0,0 +1,31 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SLUDGE_BUILTIN_H
+#define SLUDGE_BUILTIN_H
+
+enum builtReturn {BR_KEEP_AND_PAUSE, BR_ERROR, BR_CONTINUE, BR_PAUSE, BR_CALLAFUNC, BR_ALREADY_GONE};
+
+bool failSecurityCheck(char *fn);
+builtReturn callBuiltIn(int whichFunc, int numParams, loadedFunction *fun);
+
+#endif
diff --git a/engines/sludge/color.frag b/engines/sludge/color.frag
new file mode 100644
index 0000000000..1f3e45aec0
--- /dev/null
+++ b/engines/sludge/color.frag
@@ -0,0 +1,7 @@
+varying vec4 color;
+
+void main(void)
+{
+ gl_FragColor = color;
+}
+
diff --git a/engines/sludge/color.vert b/engines/sludge/color.vert
new file mode 100644
index 0000000000..819d377b05
--- /dev/null
+++ b/engines/sludge/color.vert
@@ -0,0 +1,13 @@
+attribute vec4 myVertex;
+
+uniform mat4 myPMVMatrix;
+uniform vec4 myColor;
+
+varying vec4 color;
+
+void main(void)
+{
+ gl_Position = myPMVMatrix * myVertex;
+ color = myColor;
+}
+
diff --git a/engines/sludge/colours.h b/engines/sludge/colours.h
new file mode 100644
index 0000000000..503823fa27
--- /dev/null
+++ b/engines/sludge/colours.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SLUDGE_COLOURS_H
+#define SLUDGE_COLOURS_H
+
+// Simple colour conversion routines to deal with 16-bit graphics
+//unsigned short int makeColour (byte r, byte g, byte b);
+
+inline unsigned short redValue(unsigned short c) {
+ return (c >> 11) << 3;
+}
+inline unsigned short greenValue(unsigned short c) {
+ return ((c >> 5) & 63) << 2;
+}
+inline unsigned short blueValue(unsigned short c) {
+ return (c & 31) << 3;
+}
+
+inline unsigned short makeGrey(unsigned short int r) {
+ return ((r >> 3) << 11) | ((r >> 2) << 5) | (r >> 3);
+}
+
+inline unsigned short makeColour(unsigned short int r, unsigned short int g, unsigned short int b) {
+ return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+}
+
+#endif
diff --git a/engines/sludge/csludge.h b/engines/sludge/csludge.h
new file mode 100644
index 0000000000..3c73a9d10a
--- /dev/null
+++ b/engines/sludge/csludge.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SLUDGE_CSLUDGE_H
+#define SLUDGE_CSLUDGE_H
+
+enum sludgeCommand {SLU_UNKNOWN,
+ SLU_RETURN, SLU_BRANCH, SLU_BR_ZERO, SLU_SET_GLOBAL,
+ SLU_SET_LOCAL, SLU_LOAD_GLOBAL, SLU_LOAD_LOCAL,
+ SLU_PLUS, SLU_MINUS, SLU_MULT, SLU_DIVIDE,
+ SLU_AND, SLU_OR, SLU_EQUALS, SLU_NOT_EQ, SLU_MODULUS,
+ SLU_LOAD_VALUE, SLU_LOAD_BUILT, SLU_LOAD_FUNC, SLU_CALLIT,
+ SLU_LOAD_STRING, SLU_LOAD_FILE, /*SLU_LOAD_SCENE,*/ SLU_LOAD_OBJTYPE,
+ SLU_NOT, SLU_LOAD_NULL, SLU_STACK_PUSH, SLU_LESSTHAN, SLU_MORETHAN, SLU_NEGATIVE,
+ SLU_UNREG, SLU_LESS_EQUAL, SLU_MORE_EQUAL,
+ SLU_INCREMENT_LOCAL,
+ SLU_DECREMENT_LOCAL,
+ SLU_INCREMENT_GLOBAL,
+ SLU_DECREMENT_GLOBAL, SLU_INDEXSET, SLU_INDEXGET,
+ SLU_INCREMENT_INDEX, SLU_DECREMENT_INDEX, SLU_QUICK_PUSH,
+ numSludgeCommands
+ };
+
+#endif
diff --git a/engines/sludge/cursors.cpp b/engines/sludge/cursors.cpp
new file mode 100644
index 0000000000..6ad3a35906
--- /dev/null
+++ b/engines/sludge/cursors.cpp
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if 0
+#if !defined(HAVE_GLES2)
+#include "GLee.h"
+#else
+#include <GLES2/gl2.h>
+#endif
+#endif
+
+#include "allfiles.h"
+#include "cursors.h"
+#include "colours.h"
+#include "sprites.h"
+#include "sprbanks.h"
+#include "people.h"
+#include "sludger.h"
+
+personaAnimation *mouseCursorAnim;
+int mouseCursorFrameNum = 0;
+int mouseCursorCountUp = 0;
+
+extern inputType input;
+
+void pickAnimCursor(personaAnimation *pp) {
+ deleteAnim(mouseCursorAnim);
+ mouseCursorAnim = pp;
+ mouseCursorFrameNum = 0;
+ mouseCursorCountUp = 0;
+}
+
+void displayCursor() {
+#if 0
+ if (mouseCursorAnim && mouseCursorAnim -> numFrames) {
+
+ int spriteNum = mouseCursorAnim -> frames[mouseCursorFrameNum].frameNum;
+ int flipMe = 0;
+
+ if (spriteNum < 0) {
+ spriteNum = -spriteNum;
+ flipMe = 1;
+ if (spriteNum >= mouseCursorAnim -> theSprites -> bank.total) spriteNum = 0;
+ } else {
+ if (spriteNum >= mouseCursorAnim -> theSprites -> bank.total) flipMe = 2;
+ }
+
+ if (flipMe != 2) {
+ (flipMe ? flipFontSprite : fontSprite)(input.mouseX, input.mouseY,
+ mouseCursorAnim -> theSprites -> bank.sprites[spriteNum],
+ mouseCursorAnim -> theSprites -> bank.myPalette /* ( spritePalette&) NULL*/);
+ }
+
+ if (++ mouseCursorCountUp >= mouseCursorAnim -> frames[mouseCursorFrameNum].howMany) {
+ mouseCursorCountUp = 0;
+ mouseCursorFrameNum ++;
+ mouseCursorFrameNum %= mouseCursorAnim -> numFrames;
+ }
+ }
+#endif
+}
+
+void pasteCursor(int x, int y, personaAnimation *c) {
+ if (c -> numFrames) pasteSpriteToBackDrop(x, y,
+ c -> theSprites -> bank.sprites[c -> frames[0].frameNum],
+ c -> theSprites -> bank.myPalette);
+}
diff --git a/engines/sludge/cursors.h b/engines/sludge/cursors.h
new file mode 100644
index 0000000000..9c803fc7db
--- /dev/null
+++ b/engines/sludge/cursors.h
@@ -0,0 +1,30 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SLUDGE_CURSORS_H
+#define SLUDGE_CURSORS_H
+
+void pickAnimCursor(struct personaAnimation *pp);
+void displayCursor();
+void pasteCursor(int x, int y, struct personaAnimation *c);
+
+#endif
diff --git a/engines/sludge/debug.cpp b/engines/sludge/debug.cpp
new file mode 100644
index 0000000000..222f411b80
--- /dev/null
+++ b/engines/sludge/debug.cpp
@@ -0,0 +1,52 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if 0
+#include <stdarg.h>
+#endif
+
+#include "allfiles.h"
+#include "debug.h"
+#include "language.h"
+
+void debugOut(const char *a, ...) {
+ if (! gameSettings.debugMode) return;
+
+ va_list argptr;
+ va_start(argptr, a);
+
+#if ALLOW_FILE
+#if defined __unix__ && !(defined __APPLE__)
+ vfprintf(stderr, a, argptr);
+#else
+ FILE *fp = fopen("debuggy.txt", "at");
+ if (fp) {
+ vfprintf(fp, a, argptr);
+ fclose(fp);
+ }
+#endif
+#endif
+}
+
+void debugHeader() {
+ debugOut("*** Engine compiled " __DATE__ " at " __TIME__ ".\n");
+}
diff --git a/engines/sludge/debug.h b/engines/sludge/debug.h
new file mode 100644
index 0000000000..94b6c347ff
--- /dev/null
+++ b/engines/sludge/debug.h
@@ -0,0 +1,29 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SLUDGE_DEDBUG_H
+#define SLUDGE_DEGBUG_H
+
+void debugOut(const char *a, ...);
+void debugHeader();
+
+#endif
diff --git a/engines/sludge/eglport/eglport.cpp b/engines/sludge/eglport/eglport.cpp
new file mode 100755
index 0000000000..841438323f
--- /dev/null
+++ b/engines/sludge/eglport/eglport.cpp
@@ -0,0 +1,712 @@
+/**
+ *
+ * EGLPORT.C
+ * Copyright (C) 2011-2013 Scott R. Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "eglport.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+//#define USE_EGL_SDL 1
+//#define USE_GLES1 1
+
+#if defined(USE_EGL_SDL)
+#include "SDL.h"
+#include "SDL_syswm.h"
+SDL_SysWMinfo sysWmInfo; /** Holds our X Display/Window information */
+#endif /* USE_EGL_SDL */
+
+#if defined(PANDORA) /* Pandora VSync Support */
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fb.h>
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+#endif
+int fbdev = -1;
+
+#elif defined(RPI)
+#include "bcm_host.h"
+#endif /* PANDORA */
+
+enum EGL_RENDER_T {
+ RENDER_RAW=0, /** Sets render mode to raw or framebuffer mode. */
+ RENDER_SDL, /** Sets render mode to X11/SDL mode. */
+ RENDER_TOTAL
+};
+
+enum EGL_SETTINGS_T {
+ CFG_MODE=0, /** Render mode for EGL 0=RAW 1=SDL. */
+ CFG_VSYNC, /** Controls system vsync if available. */
+ CFG_FSAA, /** Number of samples for full screen AA. 0 is off, 2/4 samples. */
+ CFG_FPS, /** Calculate and report frame per second. */
+ CFG_RED_SIZE, /** Number of bits of Red in the color buffer. */
+ CFG_GREEN_SIZE, /** Number of bits of Green in the color buffer. */
+ CFG_BLUE_SIZE, /** Number of bits of Blue in the color buffer. */
+ CFG_ALPHA_SIZE, /** Number of bits of Alpha in the color buffer. */
+ CFG_DEPTH_SIZE, /** Number of bits of Z in the depth buffer. */
+ CFG_BUFFER_SIZE, /** The total color component bits in the color buffer. */
+ CFG_STENCIL_SIZE, /** Number of bits of Stencil in the stencil buffer. */
+ CFG_TOTAL /** Total number of settings. */
+};
+
+NativeDisplayType nativeDisplay = 0; /** Reference to the systems native display */
+NativeWindowType nativeWindow = 0; /** Reference to the systems native window */
+EGLint eglSettings[CFG_TOTAL]; /** Stores setting values. */
+EGLDisplay eglDisplay = NULL; /** Reference to the EGL display */
+EGLConfig eglConfig = NULL; /** Reference to the EGL config */
+EGLContext eglContext = NULL; /** Reference to the EGL context */
+EGLSurface eglSurface = NULL; /** Reference to the EGL surface */
+
+#define totalConfigsIn 5 /** Total number of configurations to request */
+EGLint totalConfigsFound = 0; /** Total number of configurations matching attributes */
+EGLConfig eglConfigs[totalConfigsIn]; /** Structure containing references to matching configurations */
+
+uint32_t fpsCount = 0; /** Total number of frames counted */
+uint32_t fpsTime = 0; /** Start time of frame count measurment */
+
+int8_t eglColorbits = 0;
+int8_t eglDepthbits = 0;
+int8_t eglStencilbits = 0;
+
+
+/** Private API */
+void OpenCfg ( const char* file );
+int8_t ConfigureEGL ( EGLConfig config );
+int8_t FindEGLConfigs ( void );
+int8_t CheckEGLErrors ( const char* file, uint16_t line );
+
+int8_t GetNativeDisplay ( void );
+int8_t GetNativeWindow ( uint16_t width, uint16_t height );
+void FreeNativeDisplay ( void );
+void FreeNativeWindow ( void );
+
+void Platform_Open ( void );
+void Platform_Close ( void );
+void Platform_VSync ( void );
+uint32_t Platform_GetTicks ( void );
+
+void EGL_Init( void )
+{
+ //nothing...
+ return;
+}
+/** @brief Release all EGL and system resources
+ */
+void EGL_Close( void )
+{
+ /* Release EGL resources */
+ if (eglDisplay != NULL)
+ {
+ peglMakeCurrent( eglDisplay, NULL, NULL, EGL_NO_CONTEXT );
+ if (eglContext != NULL) {
+ peglDestroyContext( eglDisplay, eglContext );
+ }
+ if (eglSurface != NULL) {
+ peglDestroySurface( eglDisplay, eglSurface );
+ }
+ peglTerminate( eglDisplay );
+ }
+
+ eglSurface = NULL;
+ eglContext = NULL;
+ eglDisplay = NULL;
+
+ eglColorbits = 0;
+ eglDepthbits = 0;
+ eglStencilbits = 0;
+
+ /* Release platform resources */
+ FreeNativeWindow();
+ FreeNativeDisplay();
+ Platform_Close();
+
+ CheckEGLErrors( __FILE__, __LINE__ );
+
+ printf( "EGLport: Closed\n" );
+}
+
+/** @brief Swap the surface buffer onto the display
+ */
+void EGL_SwapBuffers( void )
+{
+ if (eglSettings[CFG_VSYNC] != 0) {
+ Platform_VSync();
+ }
+
+ peglSwapBuffers( eglDisplay, eglSurface );
+
+ if (eglSettings[CFG_FPS] != 0) {
+ fpsCount++;
+
+ if (fpsTime - Platform_GetTicks() >= 1000)
+ {
+ printf( "EGLport: %d fps\n", fpsCount );
+ fpsTime = Platform_GetTicks();
+ fpsCount = 0;
+ }
+ }
+}
+
+/** @brief Obtain the system display and initialize EGL
+ * @param width : desired pixel width of the window (not used by all platforms)
+ * @param height : desired pixel height of the window (not used by all platforms)
+ * @return : 0 if the function passed, else 1
+ */
+int8_t EGL_Open( /*uint16_t width, uint16_t height*/ )
+{
+ EGLint eglMajorVer, eglMinorVer;
+ EGLBoolean result;
+ uint32_t configIndex = 0;
+ const char* output;
+
+ static const EGLint contextAttribs[] =
+ {
+#if defined(USE_GLES2)
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+#endif
+ EGL_NONE
+ };
+
+#if defined(DEBUG)
+ printf( "EGLport Warning: DEBUG is enabled which may effect performance\n" );
+#endif
+
+ /* Check that system is not open */
+ if (eglDisplay != NULL || eglContext != NULL || eglSurface != NULL)
+ {
+ printf( "EGLport ERROR: EGL system is already open!\n" );
+ return 1;
+ }
+
+ /* Check for the cfg file to alternative settings */
+ OpenCfg( "eglport.cfg" );
+
+ /* Setup any platform specific bits */
+ Platform_Open();
+
+ printf( "EGLport: Opening EGL display\n" );
+ if (GetNativeDisplay() != 0)
+ {
+ printf( "EGLport ERROR: Unable to obtain native display!\n" );
+ return 1;
+ }
+
+ eglDisplay = peglGetDisplay( nativeDisplay );
+ if (eglDisplay == EGL_NO_DISPLAY)
+ {
+ CheckEGLErrors( __FILE__, __LINE__ );
+ printf( "EGLport ERROR: Unable to create EGL display.\n" );
+ return 1;
+ }
+
+ printf( "EGLport: Initializing\n" );
+ result = peglInitialize( eglDisplay, &eglMajorVer, &eglMinorVer );
+ if (result != EGL_TRUE )
+ {
+ CheckEGLErrors( __FILE__, __LINE__ );
+ printf( "EGLport ERROR: Unable to initialize EGL display.\n" );
+ return 1;
+ }
+
+ /* Get EGL Library Information */
+ printf( "EGL Implementation Version: Major %d Minor %d\n", eglMajorVer, eglMinorVer );
+ output = peglQueryString( eglDisplay, EGL_VENDOR );
+ printf( "EGL_VENDOR: %s\n", output );
+ output = peglQueryString( eglDisplay, EGL_VERSION );
+ printf( "EGL_VERSION: %s\n", output );
+ output = peglQueryString( eglDisplay, EGL_EXTENSIONS );
+ printf( "EGL_EXTENSIONS: %s\n", output );
+
+ if (FindEGLConfigs() != 0)
+ {
+ printf( "EGLport ERROR: Unable to configure EGL. See previous error.\n" );
+ return 1;
+ }
+
+ printf( "EGLport: Using Config %d\n", configIndex );
+#if defined(EGL_VERSION_1_2)
+ /* Bind GLES and create the context */
+ printf( "EGLport: Binding API\n" );
+ result = peglBindAPI( EGL_OPENGL_ES_API );
+ if ( result == EGL_FALSE )
+ {
+ CheckEGLErrors( __FILE__, __LINE__ );
+ printf( "EGLport ERROR: Could not bind EGL API.\n" );
+ return 1;
+ }
+#endif /* EGL_VERSION_1_2 */
+
+ printf( "EGLport: Creating Context\n" );
+ eglContext = peglCreateContext( eglDisplay, eglConfigs[configIndex], NULL, contextAttribs );
+ if (eglContext == EGL_NO_CONTEXT)
+ {
+ CheckEGLErrors( __FILE__, __LINE__ );
+ printf( "EGLport ERROR: Unable to create GLES context!\n");
+ return 1;
+ }
+
+ printf( "EGLport: Creating window surface\n" );
+ if (GetNativeWindow( 800, 480/*width, height*/ ) != 0)
+ {
+ printf( "EGLport ERROR: Unable to obtain native window!\n" );
+ return 1;
+ }
+
+ eglSurface = peglCreateWindowSurface( eglDisplay, eglConfigs[configIndex], nativeWindow, 0 );
+ if (eglSurface == EGL_NO_SURFACE)
+ {
+ CheckEGLErrors( __FILE__, __LINE__ );
+ printf( "EGLport ERROR: Unable to create EGL surface!\n" );
+ return 1;
+ }
+
+ printf( "EGLport: Making Current\n" );
+ result = peglMakeCurrent( eglDisplay, eglSurface, eglSurface, eglContext );
+ if (result != EGL_TRUE)
+ {
+ CheckEGLErrors( __FILE__, __LINE__ );
+ printf( "EGLport ERROR: Unable to make GLES context current\n" );
+ return 1;
+ }
+
+ {
+ EGLint color, depth, stencil;
+ eglGetConfigAttrib(eglDisplay, eglConfigs[configIndex], EGL_BUFFER_SIZE, &color);
+ eglGetConfigAttrib(eglDisplay, eglConfigs[configIndex], EGL_DEPTH_SIZE, &depth);
+ eglGetConfigAttrib(eglDisplay, eglConfigs[configIndex], EGL_STENCIL_SIZE, &stencil);
+ eglColorbits = (color==16)?5:8; //quick hack
+ eglDepthbits = depth;
+ eglStencilbits = stencil;
+ }
+
+ printf( "EGLport: Setting swap interval\n" );
+ peglSwapInterval( eglDisplay, (eglSettings[CFG_VSYNC] > 0) ? 1 : 0 );
+
+ printf( "EGLport: Complete\n" );
+
+ CheckEGLErrors( __FILE__, __LINE__ );
+
+ return 0;
+}
+
+/** @brief Read settings that configure how to use EGL
+ * @param file : name of the config file
+ */
+void OpenCfg ( const char* file )
+{
+ #define MAX_STRING 20
+ #define MAX_SIZE 100
+ uint8_t i;
+ FILE* fp = NULL;
+ char* location = NULL;
+ char eglStrings[CFG_TOTAL][MAX_STRING];
+ char buffer[MAX_SIZE];
+
+ strncpy( eglStrings[CFG_MODE], "egl_mode=", MAX_STRING );
+ strncpy( eglStrings[CFG_VSYNC], "use_vsync=", MAX_STRING );
+ strncpy( eglStrings[CFG_FSAA], "use_fsaa=", MAX_STRING );
+ strncpy( eglStrings[CFG_RED_SIZE], "size_red=", MAX_STRING );
+ strncpy( eglStrings[CFG_GREEN_SIZE], "size_green=", MAX_STRING );
+ strncpy( eglStrings[CFG_BLUE_SIZE], "size_blue=", MAX_STRING );
+ strncpy( eglStrings[CFG_ALPHA_SIZE], "size_alpha=", MAX_STRING );
+ strncpy( eglStrings[CFG_DEPTH_SIZE], "size_depth=", MAX_STRING );
+ strncpy( eglStrings[CFG_BUFFER_SIZE], "size_buffer=", MAX_STRING );
+ strncpy( eglStrings[CFG_STENCIL_SIZE], "size_stencil=", MAX_STRING );
+
+ /* Set defaults */
+#if defined(USE_EGL_SDL) && !defined(PANDORA)
+ eglSettings[CFG_MODE] = RENDER_SDL;
+#else
+ eglSettings[CFG_MODE] = RENDER_RAW;
+#endif
+ eglSettings[CFG_VSYNC] = 0;
+ eglSettings[CFG_FSAA] = 0;
+ eglSettings[CFG_FPS] = 0;
+ eglSettings[CFG_RED_SIZE] = 5;
+ eglSettings[CFG_GREEN_SIZE] = 6;
+ eglSettings[CFG_BLUE_SIZE] = 5;
+ eglSettings[CFG_ALPHA_SIZE] = 0;
+ eglSettings[CFG_DEPTH_SIZE] = 16;
+ eglSettings[CFG_BUFFER_SIZE] = 16;
+ eglSettings[CFG_STENCIL_SIZE] = 0;
+
+ /* Parse INI file */
+ fp = fopen( file, "r");
+ if (fp != NULL)
+ {
+ while (fgets( buffer, MAX_SIZE, fp ) != NULL)
+ {
+ for (i=0; i<CFG_TOTAL; i++)
+ {
+ location = strstr( buffer, eglStrings[i] );
+ if (location != NULL)
+ {
+ eglSettings[i] = atol( location+strlen( eglStrings[i] ) );
+ printf( "EGLport: %s set to %d.\n", eglStrings[i], eglSettings[i] );
+ break;
+ }
+ }
+ }
+
+ fclose( fp );
+ }
+ else
+ {
+ printf( "EGL NOTICE: Unable to read ini settings from file '%s'. Using defaults\n", file );
+ }
+}
+
+/** @brief Find a EGL configuration tht matches the defined attributes
+ * @return : 0 if the function passed, else 1
+ */
+int8_t FindEGLConfigs( void )
+{
+ EGLBoolean result;
+ int attrib = 0;
+ EGLint ConfigAttribs[23];
+
+ ConfigAttribs[attrib++] = EGL_RED_SIZE; /* 1 */
+ ConfigAttribs[attrib++] = eglSettings[CFG_RED_SIZE]; /* 2 */
+ ConfigAttribs[attrib++] = EGL_GREEN_SIZE; /* 3 */
+ ConfigAttribs[attrib++] = eglSettings[CFG_GREEN_SIZE]; /* 4 */
+ ConfigAttribs[attrib++] = EGL_BLUE_SIZE; /* 5 */
+ ConfigAttribs[attrib++] = eglSettings[CFG_BLUE_SIZE]; /* 6 */
+ ConfigAttribs[attrib++] = EGL_ALPHA_SIZE; /* 7 */
+ ConfigAttribs[attrib++] = eglSettings[CFG_ALPHA_SIZE]; /* 8 */
+ ConfigAttribs[attrib++] = EGL_DEPTH_SIZE; /* 9 */
+ ConfigAttribs[attrib++] = eglSettings[CFG_DEPTH_SIZE]; /* 10 */
+ ConfigAttribs[attrib++] = EGL_BUFFER_SIZE; /* 11 */
+ ConfigAttribs[attrib++] = eglSettings[CFG_BUFFER_SIZE]; /* 12 */
+ ConfigAttribs[attrib++] = EGL_STENCIL_SIZE; /* 13 */
+ ConfigAttribs[attrib++] = eglSettings[CFG_STENCIL_SIZE]; /* 14 */
+ ConfigAttribs[attrib++] = EGL_SURFACE_TYPE; /* 15 */
+ ConfigAttribs[attrib++] = EGL_WINDOW_BIT; /* 16 */
+#if defined(EGL_VERSION_1_2)
+ ConfigAttribs[attrib++] = EGL_RENDERABLE_TYPE; /* 17 */
+#if defined(USE_GLES1)
+ ConfigAttribs[attrib++] = EGL_OPENGL_ES_BIT;
+#elif defined(USE_GLES2)
+ ConfigAttribs[attrib++] = EGL_OPENGL_ES2_BIT; /* 18 */
+#endif /* USE_GLES1 */
+#endif /* EGL_VERSION_1_2 */
+ ConfigAttribs[attrib++] = EGL_SAMPLE_BUFFERS; /* 19 */
+ ConfigAttribs[attrib++] = (eglSettings[CFG_FSAA] > 0) ? 1 : 0; /* 20 */
+ ConfigAttribs[attrib++] = EGL_SAMPLES; /* 21 */
+ ConfigAttribs[attrib++] = eglSettings[CFG_FSAA]; /* 22 */
+ ConfigAttribs[attrib++] = EGL_NONE; /* 23 */
+
+ result = peglChooseConfig( eglDisplay, ConfigAttribs, eglConfigs, totalConfigsIn, &totalConfigsFound );
+ if (result != EGL_TRUE || totalConfigsFound == 0)
+ {
+ CheckEGLErrors( __FILE__, __LINE__ );
+ printf( "EGLport ERROR: Unable to query for available configs, found %d.\n", totalConfigsFound );
+ return 1;
+ }
+ printf( "EGLport: Found %d available configs\n", totalConfigsFound );
+
+ return 0;
+}
+
+/** @brief Error checking function
+ * @param file : string reference that contains the source file that the check is occuring in
+ * @param line : numeric reference that contains the line number that the check is occuring in
+ * @return : 0 if the function passed, else 1
+ */
+int8_t CheckEGLErrors( const char* file, uint16_t line )
+{
+ EGLenum error;
+ const char* errortext;
+ const char* description;
+
+ error = eglGetError();
+
+ if (error != EGL_SUCCESS && error != 0)
+ {
+ switch (error)
+ {
+ case EGL_NOT_INITIALIZED:
+ errortext = "EGL_NOT_INITIALIZED.";
+ description = "EGL is not or could not be initialized, for the specified display.";
+ break;
+ case EGL_BAD_ACCESS:
+ errortext = "EGL_BAD_ACCESS EGL";
+ description = "cannot access a requested resource (for example, a context is bound in another thread).";
+ break;
+ case EGL_BAD_ALLOC:
+ errortext = "EGL_BAD_ALLOC EGL";
+ description = "failed to allocate resources for the requested operation.";
+ break;
+ case EGL_BAD_ATTRIBUTE:
+ errortext = "EGL_BAD_ATTRIBUTE";
+ description = "An unrecognized attribute or attribute value was passed in anattribute list.";
+ break;
+ case EGL_BAD_CONFIG:
+ errortext = "EGL_BAD_CONFIG";
+ description = "An EGLConfig argument does not name a valid EGLConfig.";
+ break;
+ case EGL_BAD_CONTEXT:
+ errortext = "EGL_BAD_CONTEXT";
+ description = "An EGLContext argument does not name a valid EGLContext.";
+ break;
+ case EGL_BAD_CURRENT_SURFACE:
+ errortext = "EGL_BAD_CURRENT_SURFACE";
+ description = "The current surface of the calling thread is a window, pbuffer,or pixmap that is no longer valid.";
+ break;
+ case EGL_BAD_DISPLAY:
+ errortext = "EGL_BAD_DISPLAY";
+ description = "An EGLDisplay argument does not name a valid EGLDisplay.";
+ break;
+ case EGL_BAD_MATCH:
+ errortext = "EGL_BAD_MATCH";
+ description = "Arguments are inconsistent; for example, an otherwise valid context requires buffers (e.g. depth or stencil) not allocated by an otherwise valid surface.";
+ break;
+ case EGL_BAD_NATIVE_PIXMAP:
+ errortext = "EGL_BAD_NATIVE_PIXMAP";
+ description = "An EGLNativePixmapType argument does not refer to a validnative pixmap.";
+ break;
+ case EGL_BAD_NATIVE_WINDOW:
+ errortext = "EGL_BAD_NATIVE_WINDOW";
+ description = "An EGLNativeWindowType argument does not refer to a validnative window.";
+ break;
+ case EGL_BAD_PARAMETER:
+ errortext = "EGL_BAD_PARAMETER";
+ description = "One or more argument values are invalid.";
+ break;
+ case EGL_BAD_SURFACE:
+ errortext = "EGL_BAD_SURFACE";
+ description = "An EGLSurface argument does not name a valid surface (window,pbuffer, or pixmap) configured for rendering";
+ break;
+ case EGL_CONTEXT_LOST:
+ errortext = "EGL_CONTEXT_LOST";
+ description = "A power management event has occurred. The application mustdestroy all contexts and reinitialise client API state and objects to continue rendering.";
+ break;
+ default:
+ errortext = "Unknown EGL Error";
+ description = "";
+ break;
+ }
+
+ printf( "EGLport ERROR: EGL Error detected in file %s at line %d: %s (0x%X)\n Description: %s\n", file, line, errortext, error, description );
+ return 1;
+ }
+
+ return 0;
+}
+
+/** @brief Obtain a reference to the system's native display
+ * @param window : pointer to save the display reference
+ * @return : 0 if the function passed, else 1
+ */
+int8_t GetNativeDisplay( void )
+{
+ if (eglSettings[CFG_MODE] == RENDER_RAW) /* RAW FB mode */
+ {
+ printf( "EGLport: Using EGL_DEFAULT_DISPLAY\n" );
+ nativeDisplay = EGL_DEFAULT_DISPLAY;
+ }
+ else if (eglSettings[CFG_MODE] == RENDER_SDL) /* SDL/X11 mode */
+ {
+#if defined(USE_EGL_SDL)
+ printf( "EGLport: Opening SDL/X11 display\n" );
+ SDL_VERSION(&sysWmInfo.version);
+ SDL_GetWMInfo(&sysWmInfo);
+ nativeDisplay = (EGLNativeDisplayType)sysWmInfo.info.x11.display;
+
+ if (nativeDisplay == 0)
+ {
+ printf( "EGLport ERROR: unable to get display!\n" );
+ return 1;
+ }
+#else
+ printf( "EGLport ERROR: SDL mode was not enabled in this compile!\n" );
+#endif
+ }
+
+ return 0;
+}
+
+/** @brief Obtain a reference to the system's native window
+ * @param width : desired pixel width of the window (not used by all platforms)
+ * @param height : desired pixel height of the window (not used by all platforms)
+ * @return : 0 if the function passed, else 1
+ */
+int8_t GetNativeWindow( uint16_t width, uint16_t height )
+{
+ nativeWindow = 0;
+
+#if defined(WIZ) || defined(CAANOO)
+
+ nativeWindow = (NativeWindowType)malloc(16*1024);
+
+ if(nativeWindow == NULL) {
+ printf( "EGLport ERROR: Memory for window Failed\n" );
+ return 1;
+ }
+
+#elif defined(RPI)
+
+ EGLBoolean result;
+ uint32_t screen_width, screen_height;
+ static EGL_DISPMANX_WINDOW_T nativewindow;
+ DISPMANX_ELEMENT_HANDLE_T dispman_element;
+ DISPMANX_DISPLAY_HANDLE_T dispman_display;
+ DISPMANX_UPDATE_HANDLE_T dispman_update;
+ VC_RECT_T dst_rect;
+ VC_RECT_T src_rect;
+
+ /* create an EGL window surface */
+ result = graphics_get_display_size(0 /* LCD */, &screen_width, &screen_height);
+ if(result < 0) {
+ printf( "EGLport ERROR: RPi graphicget_display_size failed\n" );
+ return 1;
+ }
+
+ dst_rect.x = 0;
+ dst_rect.y = 0;
+ dst_rect.width = screen_width;
+ dst_rect.height = screen_height;
+
+ src_rect.x = 0;
+ src_rect.y = 0;
+ src_rect.width = width << 16;
+ src_rect.height = height << 16;
+
+ dispman_display = vc_dispmanx_display_open( 0 /* LCD */);
+ dispman_update = vc_dispmanx_update_start( 0 );
+ dispman_element = vc_dispmanx_element_add ( dispman_update, dispman_display,
+ 0 /*layer*/, &dst_rect, 0 /*src*/,
+ &src_rect, DISPMANX_PROTECTION_NONE, (VC_DISPMANX_ALPHA_T*)0 /*alpha*/, (DISPMANX_CLAMP_T*)0 /*clamp*/, (DISPMANX_TRANSFORM_T)0 /*transform*/);
+
+ nativewindow.element = dispman_element;
+ nativewindow.width = screen_width;
+ nativewindow.height = screen_height;
+ vc_dispmanx_update_submit_sync( dispman_update );
+
+ nativeWindow = (NativeWindowType)&nativewindow;
+
+#else /* default */
+
+ if (eglSettings[CFG_MODE] == RENDER_RAW) /* RAW FB mode */
+ {
+ nativeWindow = 0;
+ }
+ else if(eglSettings[CFG_MODE] == RENDER_SDL) /* SDL/X11 mode */
+ {
+#if defined(USE_EGL_SDL)
+ /* SDL_GetWMInfo is populated when display was opened */
+ nativeWindow = (NativeWindowType)sysWmInfo.info.x11.window;
+
+ if (nativeWindow == 0)
+ {
+ printf( "EGLport ERROR: unable to get window!\n" );
+ return 1;
+ }
+#else
+ printf( "EGLport ERROR: SDL mode was not enabled in this compile!\n" );
+#endif
+ }
+ else
+ {
+ printf( "EGLport ERROR: Unknown EGL render mode %d!\n", eglSettings[CFG_MODE] );
+ return 1;
+ }
+
+#endif /* WIZ / CAANOO */
+
+ return 0;
+}
+
+/** @brief Release the system's native display
+ */
+void FreeNativeDisplay( void )
+{
+}
+
+/** @brief Release the system's native window
+ */
+void FreeNativeWindow( void )
+{
+#if defined(WIZ) || defined(CAANOO)
+ if (nativeWindow != NULL) {
+ free( nativeWindow );
+ }
+ nativeWindow = NULL;
+#endif /* WIZ / CAANOO */
+}
+
+/** @brief Open any system specific resources
+ */
+void Platform_Open( void )
+{
+#if defined(PANDORA)
+ /* Pandora VSync */
+ fbdev = open( "/dev/fb0", O_RDONLY /* O_RDWR */ );
+ if ( fbdev < 0 ) {
+ printf( "EGLport ERROR: Couldn't open /dev/fb0 for Pandora Vsync\n" );
+ }
+#elif defined(RPI)
+ bcm_host_init();
+#endif /* PANDORA */
+}
+
+/** @brief Release any system specific resources
+ */
+void Platform_Close( void )
+{
+#if defined(PANDORA)
+ /* Pandora VSync */
+ close( fbdev );
+ fbdev = -1;
+#endif /* PANDORA */
+}
+
+/** @brief Check the systems vsync state
+ */
+void Platform_VSync( void )
+{
+#if defined(PANDORA)
+ /* Pandora VSync */
+ if (fbdev >= 0) {
+ int arg = 0;
+ ioctl( fbdev, FBIO_WAITFORVSYNC, &arg );
+ }
+#endif /* PANDORA */
+}
+
+/** @brief Get the system tick time (ms)
+ */
+uint32_t Platform_GetTicks( void )
+{
+ uint32_t ticks = 0;
+#if defined(USE_EGL_SDL)
+ ticks = SDL_GetTicks();
+#else
+ printf( "EGLport ERROR: SDL mode was not enabled in this compile!\n" );
+#endif
+ return ticks;
+}
+
diff --git a/engines/sludge/eglport/eglport.h b/engines/sludge/eglport/eglport.h
new file mode 100644
index 0000000000..20fb979789
--- /dev/null
+++ b/engines/sludge/eglport/eglport.h
@@ -0,0 +1,109 @@
+/**
+ *
+ * EGLPORT.H
+ * Copyright (C) 2011-2013 Scott R. Smith
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef EGLPORT_H
+#define EGLPORT_H
+
+#include <stdint.h>
+#include "EGL/egl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Defines (in every case choose only one) */
+/** Common: */
+/** DEBUG : enable additional error monitoring per EGL function call */
+/** Native display and window system for use with EGL */
+/** USE_EGL_SDL : used for access to a SDL X11 window */
+/** Platform: settings that are specific to that device */
+/** PANDORA (USE_GLES1 or USE_GLES2) */
+/** WIZ (USE_GLES1) */
+/** CAANOO (USE_GLES1) */
+/** RPI (USE_GLES1 or USE_GLES2) */
+/** GLES Version */
+/** USE_GLES1 : EGL for use with OpenGL-ES 1.X contexts */
+/** USE_GLES2 : EGL for use with OpenGL-ES 2.0 contexts */
+
+/** Public API */
+void EGL_Init ( void );
+void EGL_Close ( void );
+int8_t EGL_Open ( /*uint16_t width, uint16_t height*/ );
+void EGL_SwapBuffers ( void );
+
+extern int8_t eglColorbits;
+extern int8_t eglDepthbits;
+extern int8_t eglStencilbits;
+
+/** Simple Examples */
+/** Raw mode:
+ EGL_Open( window_width, window_height );
+ do while(!quit) {
+ ... run app
+ EGL_SwapBuffers();
+ }
+ EGL_Close();
+*/
+/** X11/SDL mode:
+ SDL_Init( SDL_INIT_VIDEO );
+ SDL_Surface* screen = SDL_SetVideoMode(640, 480, 16, SDL_SWSURFACE|SDL_FULLSCREEN);
+ EGL_Open( window_width, window_height );
+ do while(!quit) {
+ ... run app
+ EGL_SwapBuffers();
+ }
+ EGL_Close();
+ SDL_Quit();
+*/
+
+#if defined(DEBUG)
+#define GET_EGLERROR(FUNCTION) \
+ FUNCTION; \
+ { \
+ CheckEGLErrors(__FILE__, __LINE__); \
+ }
+#else
+#define GET_EGLERROR(FUNCTION) FUNCTION;
+#endif
+
+#define peglQueryString(A,B) GET_EGLERROR(eglQueryString(A,B))
+#define peglDestroyContext(A,B) GET_EGLERROR(eglDestroyContext(A,B))
+#define peglDestroySurface(A,B) GET_EGLERROR(eglDestroySurface(A,B))
+#define peglTerminate(A) GET_EGLERROR(eglTerminate(A))
+#define peglSwapBuffers(A,B) GET_EGLERROR(eglSwapBuffers(A,B))
+#define peglGetDisplay(A) GET_EGLERROR(eglGetDisplay(A))
+#define peglBindAPI(A) GET_EGLERROR(eglBindAPI(A))
+#define peglCreateContext(A,B,C,D) GET_EGLERROR(eglCreateContext(A,B,C,D))
+#define peglCreateWindowSurface(A,B,C,D) GET_EGLERROR(eglCreateWindowSurface(A,B,C,D))
+#define peglInitialize(A,B,C) GET_EGLERROR(eglInitialize(A,B,C))
+#define peglMakeCurrent(A,B,C,D) GET_EGLERROR(eglMakeCurrent(A,B,C,D))
+#define peglChooseConfig(A,B,C,D,E) GET_EGLERROR(eglChooseConfig(A,B,C,D,E))
+#define peglSwapInterval(A,B) GET_EGLERROR(eglSwapInterval(A,B))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EGLPORT_H */
diff --git a/engines/sludge/errors.h b/engines/sludge/errors.h
new file mode 100644
index 0000000000..0c4716c3e2
--- /dev/null
+++ b/engines/sludge/errors.h
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SLUDGE_ERRRORS_H
+#define SLUDGE_ERRRORS_H
+
+#define _NO_MEMORY_GENERAL_ "\n\nTry closing down any programs you don't really need running (or freeing up a bit of disk space, which will give you more virtual memory - that should help too)."
+
+//---------------------------------------
+// Fatal errors
+//---------------------------------------
+
+#define ERROR_VERSION_TOO_LOW_1 "This SLUDGE file requires a more recent version of the SLUDGE engine"
+#define ERROR_VERSION_TOO_LOW_2 "(it was created for v%i.%i).\n\nVisit http://opensludge.github.io/ to download the most recent version."
+#define ERROR_VERSION_TOO_HIGH_1 "This SLUDGE file was created for an older version of the SLUDGE engine"
+#define ERROR_VERSION_TOO_HIGH_2 "(v%i.%i).\n\nPlease contact the author of this game to obtain a version compatible with your SLUDGE engine (v" TEXT_VERSION ")."
+#define ERROR_BAD_HEADER "Bad header information... this isn't a valid SLUDGE game"
+#define ERROR_HACKER "What have you been up to? Think we're a hacker, do we? Nice try."
+
+#define ERROR_GAME_LOAD_NO "This isn't a SLUDGE saved game!\n"
+#define ERROR_GAME_LOAD_WRONG "Can't load this saved game! It was either created by...\n\n (a) a different SLUDGE game to the one which you're playing, or...\n (b) a different (newer or older) version of the same game.\n\nFilename"
+#define ERROR_GAME_SAVE_FROZEN "Can't save games while I'm frozen"
+#define ERROR_GAME_LOAD_CORRUPT "This saved game appears to be corrupted"
+
+#define ERROR_NON_EMPTY_STACK "Returning from function with non-empty stack"
+#define ERROR_UNKNOWN_MCODE "Unknown SLUDGE machine code"
+#define ERROR_CALL_NONFUNCTION "Call of non-function"
+#define ERROR_INCDEC_UNKNOWN "Tried to increment/decrement index of an undefined variable"
+#define ERROR_INDEX_EMPTY "Tried to index an empty stack"
+#define ERROR_INDEX_NONSTACK "Tried to index a non-stack variable"
+#define ERROR_NOSTACK "Corrupt file - no stack"
+#define ERROR_UNKNOWN_CODE "Unimplemented internal SLUDGE command code."
+#define ERROR_OUT_OF_MEMORY "Out of memory!" _NO_MEMORY_GENERAL_
+
+#define ERROR_MUSIC_MEMORY_LOW "Your computer doesn't have enough memory available to load a music resource that needs playing." _NO_MEMORY_GENERAL_
+#define ERROR_SOUND_MEMORY_LOW "Your computer doesn't have enough memory available to load a sound resource that needs playing." _NO_MEMORY_GENERAL_
+#define ERROR_MUSIC_UNKNOWN "I can't understand a piece of music which I've been told to play!\n\n" \
+ "Maybe it's stored in a format that SLUDGE doesn't know about... " \
+ "make sure you've got a recent version of the SLUDGE engine from http://opensludge.github.io/. " \
+ "Failing that, maybe the resource in question isn't a valid music format at all... in which case, contact the game's author and tell them what's happened."
+#define ERROR_SOUND_UNKNOWN "I can't understand a sample which I've been told to play!\nMake sure you've got the latest SLUDGE engine from http://opensludge.github.io/. Failing that, maybe the resource in question isn't a valid sound at all... in which case, contact the game's author and tell them what's happened."
+#define ERROR_MUSIC_ODDNESS "I can't load a music resource I've been told to play. Sorry."
+#define ERROR_SOUND_ODDNESS "I can't load a sound resource I've been told to play. Sorry."
+#define ERROR_MOVIE_ODDNESS "I can't load a music resource I've been told to play. Sorry."
+
+//---------------------------------------
+// Startup warnings
+//---------------------------------------
+
+#define WARNING_BASS_WRONG_VERSION "Incompatible version of BASS.DLL found!"
+#define WARNING_BASS_FAIL "Can't initialise sound engine."
+
+#endif
diff --git a/engines/sludge/fileset.cpp b/engines/sludge/fileset.cpp
new file mode 100644
index 0000000000..8d5eb52a94
--- /dev/null
+++ b/engines/sludge/fileset.cpp
@@ -0,0 +1,237 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#if 0
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+// For unicode conversion
+#include <iconv.h>
+#endif
+
+#include "debug.h"
+#include "stringy.h"
+
+#include "allfiles.h"
+#include "moreio.h"
+#include "newfatal.h"
+#include "CommonCode/version.h"
+
+
+bool sliceBusy = true;
+#if ALLOW_FILE
+FILE *bigDataFile = NULL;
+#endif
+uint32_t startOfDataIndex, startOfTextIndex,
+ startOfSubIndex, startOfObjectIndex;
+
+bool openSubSlice(int num) {
+// FILE * dbug = fopen ("debuggy.txt", "at");
+
+// fprintf (dbug, "\nTrying to open sub %i\n", num);
+
+ if (sliceBusy) {
+ fatal("Can't read from data file", "I'm already reading something");
+ return false;
+ }
+// fprintf (dbug, "Going to position %li\n", startOfSubIndex + (num << 2));
+ fseek(bigDataFile, startOfSubIndex + (num << 2), 0);
+ fseek(bigDataFile, get4bytes(bigDataFile), 0);
+// fprintf (dbug, "Told to skip forward to %li\n", ftell (bigDataFile));
+// fclose (dbug);
+
+ return sliceBusy = true;
+}
+
+bool openObjectSlice(int num) {
+// FILE * dbug = fopen ("debuggy.txt", "at");
+
+// fprintf (dbug, "\nTrying to open object %i\n", num);
+
+ if (sliceBusy) {
+ fatal("Can't read from data file", "I'm already reading something");
+ return false;
+ }
+
+// fprintf (dbug, "Going to position %li\n", startOfObjectIndex + (num << 2));
+ fseek(bigDataFile, startOfObjectIndex + (num << 2), 0);
+ fseek(bigDataFile, get4bytes(bigDataFile), 0);
+// fprintf (dbug, "Told to skip forward to %li\n", ftell (bigDataFile));
+// fclose (dbug);
+ return sliceBusy = true;
+}
+
+unsigned int openFileFromNum(int num) {
+// FILE * dbug = fopen ("debuggy.txt", "at");
+
+ if (sliceBusy) {
+ fatal("Can't read from data file", "I'm already reading something");
+ return 0;
+ }
+
+// fprintf (dbug, "\nTrying to open file %i\n", num);
+// fprintf (dbug, "Jumping to %li (for index) \n", startOfDataIndex + (num << 2));
+ fseek(bigDataFile, startOfDataIndex + (num << 2), 0);
+ fseek(bigDataFile, get4bytes(bigDataFile), 1);
+// fprintf (dbug, "Jumping to %li (for data) \n", ftell (bigDataFile));
+ sliceBusy = true;
+// fclose (dbug);
+
+ return get4bytes(bigDataFile);
+}
+
+
+// Converts a string from Windows CP-1252 to UTF-8.
+// This is needed for old games.
+char *convertString(char *s) {
+ static char *buf = NULL;
+
+ if (! buf) {
+ buf = new char [65536];
+ if (! checkNew(buf)) return NULL;
+ }
+
+ char **tmp1 = (char **) &s;
+ char **tmp2 = (char **) &buf;
+ char *sOrig = s;
+ char *bufOrig = buf;
+#if defined __unix__ && !(defined __APPLE__)
+ iconv_t convert = iconv_open("UTF-8", "ISO8859-2");
+#else
+ iconv_t convert = iconv_open("UTF-8", "CP1250");
+#endif
+
+ if (convert == (iconv_t) - 1) {
+ switch (errno) {
+ case EINVAL:
+ fprintf(stderr, "Error: Encoding not supported by iconv.\n");
+ break;
+ default:
+ fprintf(stderr, "Error: Could not open iconv conversion descriptor.\n");
+ }
+ }
+
+ size_t len1 = strlen(s) + 1;
+ size_t len2 = 65535;
+ size_t iconv_value =
+#ifdef _WIN32
+ iconv(convert, (const char **) tmp1, &len1, tmp2, &len2);
+#else
+ iconv(convert, (char **) tmp1, &len1, tmp2, &len2);
+#endif
+
+ if (iconv_value == (size_t) - 1) {
+ switch (errno) {
+ /* See "man 3 iconv" for an explanation. */
+ case EILSEQ:
+ fprintf(stderr, "Invalid multibyte sequence.\n");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Incomplete multibyte sequence.\n");
+ break;
+ case E2BIG:
+ fprintf(stderr, "No more room.\n");
+ break;
+ default:
+ fprintf(stderr, "Error: %s.\n", strerror(errno));
+ }
+ fatal("Conversion to Unicode failed. This can be fixed by recompiling the game in a current version of the SLUDGE Development Kit, but this error should never happen. Congratulations, you've found a bug in the SLUDGE engine! Please let us know about it.");
+ }
+ iconv_close(convert);
+
+
+ delete [] sOrig;
+ return copyString(buf = bufOrig);
+}
+
+char *getNumberedString(int value) {
+
+ if (sliceBusy) {
+ fatal("Can't read from data file", "I'm already reading something");
+ return NULL;
+ }
+
+ fseek(bigDataFile, (value << 2) + startOfTextIndex, 0);
+ value = get4bytes(bigDataFile);
+ fseek(bigDataFile, value, 0);
+
+ char *s = readString(bigDataFile);
+
+ if (gameVersion < VERSION(2, 2)) {
+ // This is an older game - We need to convert the string to UTF-8
+ s = convertString(s);
+ }
+
+ return s;
+}
+
+bool startAccess() {
+ int wasBusy = sliceBusy;
+ sliceBusy = true;
+ return wasBusy;
+}
+void finishAccess() {
+ sliceBusy = false;
+}
+
+int32_t startIndex;
+
+void setFileIndices(FILE *fp, int numLanguages, unsigned int skipBefore) {
+ if (fp) {
+ // Keep hold of the file handle, and let things get at it
+ bigDataFile = fp;
+ startIndex = ftell(fp);
+ } else {
+ // No file pointer - this means that we reuse the bigDataFile
+ fp = bigDataFile;
+ fseek(fp, startIndex, 0);
+ }
+ sliceBusy = false;
+
+ if (skipBefore > numLanguages) {
+ warning("Not a valid language ID! Using default instead.");
+ skipBefore = 0;
+ }
+
+ // STRINGS
+ int skipAfter = numLanguages - skipBefore;
+ while (skipBefore) {
+ fseek(fp, get4bytes(fp), 0);
+ skipBefore --;
+ }
+ startOfTextIndex = ftell(fp) + 4;
+
+ fseek(fp, get4bytes(fp), 0);
+
+ while (skipAfter) {
+ fseek(fp, get4bytes(fp), 0);
+ skipAfter --;
+ }
+
+ startOfSubIndex = ftell(fp) + 4;
+ fseek(fp, get4bytes(fp), 1);
+
+ startOfObjectIndex = ftell(fp) + 4;
+ fseek(fp, get4bytes(fp), 1);
+
+ // Remember that the data section starts here
+ startOfDataIndex = ftell(fp);
+}
diff --git a/engines/sludge/fileset.h b/engines/sludge/fileset.h
new file mode 100644
index 0000000000..39a66912f1
--- /dev/null
+++ b/engines/sludge/fileset.h
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_FILESET_H
+#define SLUDGE_FILESET_H
+
+#if ALLOW_FILE
+extern FILE *bigDataFile;
+
+void setFileIndices(FILE *fp, int, unsigned int);
+#endif
+
+unsigned int openFileFromNum(int num);
+bool openSubSlice(int num);
+bool openObjectSlice(int num);
+char *getNumberedString(int value);
+
+bool startAccess();
+void finishAccess();
+
+#endif
diff --git a/engines/sludge/fixScaleSprite.frag b/engines/sludge/fixScaleSprite.frag
new file mode 100644
index 0000000000..ed9f6cfaf8
--- /dev/null
+++ b/engines/sludge/fixScaleSprite.frag
@@ -0,0 +1,29 @@
+uniform sampler2D tex0;
+uniform sampler2D tex1;
+uniform sampler2D tex2;
+uniform bool useLightTexture;
+
+varying vec2 varCoord0;
+varying vec2 varCoord1;
+varying vec2 varCoord2;
+
+varying vec4 color;
+varying vec4 secondaryColor;
+
+void main()
+{
+ vec4 texture = texture2D (tex0, varCoord0);
+ vec4 texture2 = texture2D (tex2, varCoord2);
+ vec3 col;
+ if (useLightTexture) {
+ vec4 texture1 = texture2D (tex1, varCoord1);
+ col = texture1.rgb * texture.rgb;
+ } else {
+ col = color.rgb * texture.rgb;
+ }
+ col += vec3(secondaryColor);
+ vec4 color = vec4 (col, color.a * texture.a);
+ col = mix (texture2.rgb, color.rgb, color.a);
+ gl_FragColor = vec4 (col, max(texture.a, texture2.a));
+}
+
diff --git a/engines/sludge/fixScaleSprite.vert b/engines/sludge/fixScaleSprite.vert
new file mode 100644
index 0000000000..d9f4e5177a
--- /dev/null
+++ b/engines/sludge/fixScaleSprite.vert
@@ -0,0 +1,25 @@
+attribute vec4 myVertex;
+attribute vec2 myUV0;
+attribute vec2 myUV1;
+attribute vec2 myUV2;
+
+uniform mat4 myPMVMatrix;
+uniform vec4 myColor;
+uniform vec4 mySecondaryColor;
+
+varying vec2 varCoord0;
+varying vec2 varCoord1;
+varying vec2 varCoord2;
+
+varying vec4 color;
+varying vec4 secondaryColor;
+
+void main() {
+ varCoord0 = myUV0.st;
+ varCoord1 = myUV1.st;
+ varCoord2 = myUV2.st;
+ gl_Position = myPMVMatrix * myVertex;
+
+ color = myColor;
+ secondaryColor = mySecondaryColor;
+}
diff --git a/engines/sludge/floor.cpp b/engines/sludge/floor.cpp
new file mode 100644
index 0000000000..826632a76a
--- /dev/null
+++ b/engines/sludge/floor.cpp
@@ -0,0 +1,290 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+#include "newfatal.h"
+#include "fileset.h"
+#include "moreio.h"
+#include "floor.h"
+#include "line.h"
+
+flor *currentFloor = NULL;
+
+bool pointInFloorPolygon(floorPolygon &floorPoly, int x, int y) {
+ int i = 0, j, c = 0;
+ float xp_i, yp_i;
+ float xp_j, yp_j;
+
+ for (j = floorPoly.numVertices - 1; i < floorPoly.numVertices;
+ j = i ++) {
+
+ xp_i = currentFloor -> vertex[floorPoly.vertexID[i]].x;
+ yp_i = currentFloor -> vertex[floorPoly.vertexID[i]].y;
+ xp_j = currentFloor -> vertex[floorPoly.vertexID[j]].x;
+ yp_j = currentFloor -> vertex[floorPoly.vertexID[j]].y;
+
+ if ((((yp_i <= y) && (y < yp_j)) ||
+ ((yp_j <= y) && (y < yp_i))) &&
+ (x < (xp_j - xp_i) * (y - yp_i) / (yp_j - yp_i) + xp_i)) {
+
+ c = !c;
+ }
+ }
+ return c;
+}
+
+bool getMatchingCorners(floorPolygon &a, floorPolygon &b, int &cornerA, int &cornerB) {
+ int sharedVertices = 0;
+ int i, j;
+
+ for (i = 0; i < a.numVertices; i ++) {
+ for (j = 0; j < b.numVertices; j ++) {
+ if (a.vertexID[i] == b.vertexID[j]) {
+ if (sharedVertices ++) {
+ cornerB = a.vertexID[i];
+ return true;
+ } else {
+ cornerA = a.vertexID[i];
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool polysShareSide(floorPolygon &a, floorPolygon &b) {
+ int sharedVertices = 0;
+ int i, j;
+
+ for (i = 0; i < a.numVertices; i ++) {
+ for (j = 0; j < b.numVertices; j ++) {
+ if (a.vertexID[i] == b.vertexID[j]) {
+ if (sharedVertices ++) return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void noFloor() {
+ currentFloor -> numPolygons = 0;
+ currentFloor -> polygon = NULL;
+ currentFloor -> vertex = NULL;
+ currentFloor -> matrix = NULL;
+}
+
+bool initFloor() {
+ currentFloor = new flor;
+ if (! checkNew(currentFloor)) return false;
+ noFloor();
+ return true;
+}
+
+void killFloor() {
+ for (int i = 0; i < currentFloor -> numPolygons; i ++) {
+ delete currentFloor -> polygon[i].vertexID;
+ delete currentFloor -> matrix[i];
+ }
+ delete currentFloor -> polygon;
+ currentFloor -> polygon = NULL;
+ delete currentFloor -> vertex;
+ currentFloor -> vertex = NULL;
+ delete currentFloor -> matrix;
+ currentFloor -> matrix = NULL;
+}
+
+void setFloorNull() {
+ killFloor();
+ noFloor();
+}
+
+bool setFloor(int fileNum) {
+
+ int i, j;
+
+ killFloor();
+
+ setResourceForFatal(fileNum);
+#if ALLOW_FILE
+ if (! openFileFromNum(fileNum)) return false;
+
+ // Find out how many polygons there are and reserve memory
+
+ currentFloor -> originalNum = fileNum;
+ currentFloor -> numPolygons = fgetc(bigDataFile);
+ currentFloor -> polygon = new floorPolygon[currentFloor -> numPolygons];
+ if (! checkNew(currentFloor -> polygon)) return false;
+
+ // Read in each polygon
+
+ for (i = 0; i < currentFloor -> numPolygons; i ++) {
+
+ // Find out how many vertex IDs there are and reserve memory
+
+ currentFloor -> polygon[i].numVertices = fgetc(bigDataFile);
+ currentFloor -> polygon[i].vertexID = new int[currentFloor -> polygon[i].numVertices];
+ if (! checkNew(currentFloor -> polygon[i].vertexID)) return false;
+
+ // Read in each vertex ID
+
+ for (j = 0; j < currentFloor -> polygon[i].numVertices; j ++) {
+ currentFloor -> polygon[i].vertexID[j] = get2bytes(bigDataFile);
+ }
+ }
+
+ // Find out how many vertices there are and reserve memory
+
+ i = get2bytes(bigDataFile);
+ currentFloor -> vertex = new POINT[i];
+ if (! checkNew(currentFloor -> vertex)) return false;
+
+ for (j = 0; j < i; j ++) {
+
+ currentFloor -> vertex[j].x = get2bytes(bigDataFile);
+ currentFloor -> vertex[j].y = get2bytes(bigDataFile);
+ }
+
+ finishAccess();
+#endif
+ // Now build the movement martix
+
+ currentFloor -> matrix = new int *[currentFloor -> numPolygons];
+ int * * distanceMatrix = new int *[currentFloor -> numPolygons];
+
+ if (! checkNew(currentFloor -> matrix)) return false;
+
+ for (i = 0; i < currentFloor -> numPolygons; i ++) {
+ currentFloor -> matrix[i] = new int [currentFloor -> numPolygons];
+ distanceMatrix [i] = new int [currentFloor -> numPolygons];
+ if (! checkNew(currentFloor -> matrix[i])) return false;
+ for (j = 0; j < currentFloor -> numPolygons; j ++) {
+ currentFloor -> matrix[i][j] = -1;
+ distanceMatrix [i][j] = 10000;
+ }
+ }
+
+ for (i = 0; i < currentFloor -> numPolygons; i ++) {
+ for (j = 0; j < currentFloor -> numPolygons; j ++) {
+ if (i != j) {
+ if (polysShareSide(currentFloor -> polygon[i], currentFloor -> polygon[j])) {
+ currentFloor -> matrix[i][j] = j;
+ distanceMatrix [i][j] = 1;
+ }
+ } else {
+ currentFloor -> matrix[i][j] = -2;
+ distanceMatrix [i][j] = 0;
+ }
+ }
+ }
+
+ bool madeChange;
+ int lookForDistance = 0;
+
+ do {
+ lookForDistance ++;
+// debugMatrix ();
+ madeChange = false;
+ for (i = 0; i < currentFloor -> numPolygons; i ++) {
+ for (j = 0; j < currentFloor -> numPolygons; j ++) {
+ if (currentFloor -> matrix[i][j] == -1) {
+
+ // OK, so we don't know how to get from i to j...
+ for (int d = 0; d < currentFloor -> numPolygons; d ++) {
+ if (d != i && d != j) {
+ if (currentFloor -> matrix[i][d] == d &&
+ currentFloor -> matrix[d][j] >= 0 &&
+ distanceMatrix [d][j] <= lookForDistance) {
+
+ currentFloor -> matrix[i][j] = d;
+ distanceMatrix [i][j] = lookForDistance + 1;
+ madeChange = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ } while (madeChange);
+
+ for (i = 0; i < currentFloor -> numPolygons; i ++) {
+ delete distanceMatrix [i];
+ }
+
+ delete distanceMatrix;
+ distanceMatrix = NULL;
+
+ setResourceForFatal(-1);
+
+ return true;
+}
+
+void drawFloor() {
+ int i, j, nV;
+ for (i = 0; i < currentFloor -> numPolygons; i ++) {
+// drawLine (i, 0, i + 5, 100);
+ nV = currentFloor -> polygon[i].numVertices;
+ if (nV > 1) {
+ for (j = 1; j < nV; j ++) {
+ drawLine(currentFloor -> vertex[currentFloor -> polygon[i].vertexID[j - 1]].x,
+ currentFloor -> vertex[currentFloor -> polygon[i].vertexID[j - 1]].y,
+ currentFloor -> vertex[currentFloor -> polygon[i].vertexID[j]].x,
+ currentFloor -> vertex[currentFloor -> polygon[i].vertexID[j]].y);
+ }
+ drawLine(currentFloor -> vertex[currentFloor -> polygon[i].vertexID[0]].x,
+ currentFloor -> vertex[currentFloor -> polygon[i].vertexID[0]].y,
+ currentFloor -> vertex[currentFloor -> polygon[i].vertexID[nV - 1]].x,
+ currentFloor -> vertex[currentFloor -> polygon[i].vertexID[nV - 1]].y);
+ }
+ }
+}
+
+int inFloor(int x, int y) {
+ int i, r = -1;
+
+ for (i = 0; i < currentFloor -> numPolygons; i ++)
+ if (pointInFloorPolygon(currentFloor -> polygon[i], x, y))
+ r = i;
+
+ return r;
+}
+
+bool closestPointOnLine(int &closestX, int &closestY, int x1, int y1, int x2, int y2, int xP, int yP) {
+ int xDiff = x2 - x1;
+ int yDiff = y2 - y1;
+
+ double m = xDiff * (xP - x1) + yDiff * (yP - y1);
+ m /= (xDiff * xDiff) + (yDiff * yDiff);
+
+ if (m < 0) {
+ closestX = x1;
+ closestY = y1;
+ } else if (m > 1) {
+ closestX = x2;
+ closestY = y2;
+ } else {
+ closestX = x1 + m * xDiff;
+ closestY = y1 + m * yDiff;
+ return true;
+ }
+ return false;
+}
diff --git a/engines/sludge/floor.h b/engines/sludge/floor.h
new file mode 100644
index 0000000000..5c0d050b0c
--- /dev/null
+++ b/engines/sludge/floor.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_FLOOR_H
+#define SLUDGE_FLOOR_H
+
+#ifdef _WIN32
+#include "windef.h"
+#endif
+
+struct floorPolygon {
+ int numVertices;
+ int *vertexID;
+};
+
+#ifndef _WIN32
+struct POINT {
+ int x;
+ int y;
+};
+#endif
+
+struct flor {
+ int originalNum;
+ POINT *vertex;
+ int numPolygons;
+ floorPolygon *polygon;
+ int * *matrix;
+};
+
+bool initFloor();
+void setFloorNull();
+bool setFloor(int fileNum);
+void drawFloor();
+int inFloor(int x, int y);
+bool getMatchingCorners(floorPolygon &, floorPolygon &, int &, int &);
+bool closestPointOnLine(int &closestX, int &closestY, int x1, int y1, int x2, int y2, int xP, int yP);
+
+#endif
diff --git a/engines/sludge/fonttext.cpp b/engines/sludge/fonttext.cpp
new file mode 100644
index 0000000000..835c2a6f32
--- /dev/null
+++ b/engines/sludge/fonttext.cpp
@@ -0,0 +1,206 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "platform-dependent.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "CommonCode/utf8.h"
+
+#include "debug.h"
+#include "allfiles.h"
+#include "stringy.h"
+
+#include "sprites.h"
+#include "colours.h"
+#include "fonttext.h"
+#include "newfatal.h"
+#include "moreio.h"
+
+spriteBank theFont;
+int fontHeight = 0, numFontColours, loadedFontNum;
+char *fontOrderString = NULL;
+short fontSpace = -1;
+
+uint32_t *fontTable = NULL;
+unsigned int fontTableSize = 0;
+
+#define fontInTable(x) ((x<fontTableSize) ? fontTable[(uint32_t) x] : 0)
+
+extern float cameraZoom;
+
+bool isInFont(char *theText) {
+ if (! fontTableSize) return 0;
+ if (! theText[0]) return 0;
+
+ // We don't want to compare strings. Only single characters allowed!
+ if (u8_strlen(theText) > 1) return false;
+
+ int i = 0;
+ uint32_t c = u8_nextchar(theText, &i);
+
+ return u8_strchr(fontOrderString, c, &i);
+}
+
+int stringLength(char *theText) {
+ return u8_strlen(theText);
+}
+
+int stringWidth(char *theText) {
+ int a = 0;
+ uint32_t c;
+ int xOff = 0;
+
+ if (! fontTableSize) return 0;
+
+ while (theText[a]) {
+ c = u8_nextchar(theText, &a);
+ xOff += theFont.sprites[fontInTable(c)].width + fontSpace;
+ }
+
+ return xOff;
+}
+
+void pasteString(char *theText, int xOff, int y, spritePalette &thePal) {
+ sprite *mySprite;
+ int a = 0;
+ uint32_t c;
+
+ if (! fontTableSize) return;
+
+ xOff += (int)((float)(fontSpace >> 1) / cameraZoom);
+ while (theText[a]) {
+ c = u8_nextchar(theText, &a);
+ mySprite = & theFont.sprites[fontInTable(c)];
+ fontSprite(xOff, y, * mySprite, thePal);
+ xOff += (int)((double)(mySprite -> width + fontSpace) / cameraZoom);
+ }
+}
+
+void pasteStringToBackdrop(char *theText, int xOff, int y, spritePalette &thePal) {
+ sprite *mySprite;
+ int a = 0;
+ uint32_t c;
+
+ if (! fontTableSize) return;
+
+ xOff += fontSpace >> 1;
+ while (theText[a]) {
+ c = u8_nextchar(theText, &a);
+ mySprite = & theFont.sprites[fontInTable(c)];
+ pasteSpriteToBackDrop(xOff, y, * mySprite, thePal);
+ xOff += mySprite -> width + fontSpace;
+ }
+}
+
+void burnStringToBackdrop(char *theText, int xOff, int y, spritePalette &thePal) {
+ sprite *mySprite;
+ int a = 0;
+ uint32_t c;
+
+ if (! fontTableSize) return;
+
+ xOff += fontSpace >> 1;
+ while (theText[a]) {
+ c = u8_nextchar(theText, &a);
+ mySprite = & theFont.sprites[fontInTable(c)];
+ burnSpriteToBackDrop(xOff, y, * mySprite, thePal);
+ xOff += mySprite -> width + fontSpace;
+ }
+}
+
+void fixFont(spritePalette &spal) {
+#if 0
+ delete [] spal.tex_names;
+ delete [] spal.burnTex_names;
+ delete [] spal.tex_h;
+ delete [] spal.tex_w;
+
+ spal.numTextures = theFont.myPalette.numTextures;
+
+ spal.tex_names = new GLuint [spal.numTextures];
+ if (! checkNew(spal.tex_names)) return;
+ spal.burnTex_names = new GLuint [spal.numTextures];
+ if (! checkNew(spal.burnTex_names)) return;
+ spal.tex_w = new int [spal.numTextures];
+ if (! checkNew(spal.tex_w)) return;
+ spal.tex_h = new int [spal.numTextures];
+ if (! checkNew(spal.tex_h)) return;
+
+ for (int i = 0; i < theFont.myPalette.numTextures; i++) {
+ spal.tex_names[i] = theFont.myPalette.tex_names[i];
+ spal.burnTex_names[i] = theFont.myPalette.burnTex_names[i];
+ spal.tex_w[i] = theFont.myPalette.tex_w[i];
+ spal.tex_h[i] = theFont.myPalette.tex_h[i];
+ }
+#endif
+}
+
+void setFontColour(spritePalette &sP, byte r, byte g, byte b) {
+ sP.originalRed = r;
+ sP.originalGreen = g;
+ sP.originalBlue = b;
+}
+
+bool loadFont(int filenum, const char *charOrder, int h) {
+ int a = 0;
+ uint32_t c;
+
+ delete [] fontOrderString;
+ fontOrderString = copyString(charOrder);
+
+ forgetSpriteBank(theFont);
+
+ loadedFontNum = filenum;
+
+
+ fontTableSize = 0;
+ while (charOrder[a]) {
+ c = u8_nextchar(charOrder, &a);
+ if (c > fontTableSize) fontTableSize = c;
+ }
+ fontTableSize++;
+
+ delete [] fontTable;
+ fontTable = new uint32_t [fontTableSize];
+ if (! checkNew(fontTable)) return false;
+
+ for (a = 0; a < fontTableSize; a ++) {
+ fontTable[a] = 0;
+ }
+ a = 0;
+ int i = 0;
+ while (charOrder[a]) {
+ c = u8_nextchar(charOrder, &a);
+ fontTable[c] = i;
+ i++;
+ }
+
+ if (! loadSpriteBank(filenum, theFont, true)) {
+ fatal("Can't load font");
+ return false;
+ }
+
+ numFontColours = theFont.myPalette.total;
+ fontHeight = h;
+ return true;
+}
diff --git a/engines/sludge/fonttext.h b/engines/sludge/fonttext.h
new file mode 100644
index 0000000000..f33876f6f9
--- /dev/null
+++ b/engines/sludge/fonttext.h
@@ -0,0 +1,35 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_FONTTEXT_H
+#define SLUDGE_FONTTEXT_H
+
+bool loadFont(int filenum, const char *charOrder, int);
+void pasteString(char *theText, int, int, spritePalette &);
+void fixFont(spritePalette &spal);
+void setFontColour(spritePalette &sP, byte r, byte g, byte b);
+int stringWidth(char *theText);
+int stringLength(char *theText);
+void pasteStringToBackdrop(char *theText, int xOff, int y, spritePalette &thePal);
+void burnStringToBackdrop(char *theText, int xOff, int y, spritePalette &thePal);
+bool isInFont(char *theText);
+
+#endif
diff --git a/engines/sludge/freeze.cpp b/engines/sludge/freeze.cpp
new file mode 100644
index 0000000000..f245ad0501
--- /dev/null
+++ b/engines/sludge/freeze.cpp
@@ -0,0 +1,364 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+#include "debug.h"
+#include "graphics.h"
+#include "newfatal.h"
+#include "sprites.h"
+#include "sprbanks.h"
+#include "people.h"
+#include "sludger.h"
+#include "objtypes.h"
+#include "region.h"
+#include "backdrop.h"
+#include "talk.h"
+#include "fonttext.h"
+#include "statusba.h"
+#include "freeze.h"
+#include "zbuffer.h"
+
+extern onScreenPerson *allPeople;
+extern screenRegion *allScreenRegions;
+extern screenRegion *overRegion;
+extern speechStruct *speech;
+extern inputType input;
+#if 0
+extern GLuint backdropTextureName;
+#endif
+extern parallaxLayer *parallaxStuff;
+extern int lightMapNumber, zBufferNumber;
+extern eventHandlers *currentEvents;
+extern personaAnimation *mouseCursorAnim;
+extern int mouseCursorFrameNum;
+extern int cameraX, cameraY;
+extern unsigned int sceneWidth, sceneHeight;
+extern float cameraZoom;
+extern zBufferData zBuffer;
+extern bool backdropExists;
+frozenStuffStruct *frozenStuff = NULL;
+extern unsigned int sceneWidth, sceneHeight;
+
+void shufflePeople();
+#if 0
+GLuint freezeTextureName = 0;
+#endif
+
+void freezeGraphics() {
+#if 0
+ glViewport(0, 0, realWinWidth, realWinHeight);
+
+ glGenTextures(1, &freezeTextureName);
+#endif
+ int w = winWidth;
+ int h = winHeight;
+ if (! NPOT_textures) {
+ w = getNextPOT(winWidth);
+ h = getNextPOT(winHeight);
+ }
+#if 0
+ glBindTexture(GL_TEXTURE_2D, freezeTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, freezeTextureName);
+#endif
+ // Temporarily disable AA
+ int antiAlias = gameSettings.antiAlias;
+ gameSettings.antiAlias = 0;
+
+
+ int x = 0;
+ while (x < winWidth) {
+ int y = 0;
+
+ if (winWidth - x < realWinWidth) {
+ w = winWidth - x;
+ } else {
+ w = realWinWidth;
+ }
+
+ while (y < winHeight) {
+
+ if (winHeight - y < realWinHeight) {
+ h = winHeight - y;
+ } else {
+ h = realWinHeight;
+ }
+#if 0
+ const GLfloat bPMVMatrix[] = {
+ 2.0f / realWinWidth * cameraZoom, .0, .0, .0,
+ .0, 2.0f / realWinHeight * cameraZoom, .0, .0,
+ .0, .0, 1.0f, .0,
+ -2.0f * (x / realWinWidth) - 1.0f, -2.0f * (y / realWinHeight) - 1.0f, .0, 1.0f
+
+ };
+ for (int i = 0; i < 16; i++) {
+ aPMVMatrix[i] = bPMVMatrix[i];
+ }
+ // Render scene
+ glDepthMask(GL_TRUE);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen
+ glDepthMask(GL_FALSE);
+
+ drawBackDrop(); // Draw the room
+ drawZBuffer(cameraX, cameraY, false);
+
+ glEnable(GL_DEPTH_TEST);
+
+ drawPeople(); // Then add any moving characters...
+
+ glDisable(GL_DEPTH_TEST);
+
+
+ // Copy Our ViewPort To The Texture
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 0, 0, w, h, freezeTextureName);
+#endif
+ y += h;
+ }
+ x += w;
+ }
+
+ gameSettings.antiAlias = antiAlias;
+
+#if 0
+ glViewport(viewportOffsetX, viewportOffsetY, viewportWidth, viewportHeight);
+ setPixelCoords(false);
+#endif
+}
+
+bool freeze() {
+ debugOut("calling freeze()\n");
+ frozenStuffStruct *newFreezer = new frozenStuffStruct;
+ if (! checkNew(newFreezer)) return false;
+
+ // Grab a copy of the current scene
+ freezeGraphics();
+#if 0
+ newFreezer -> backdropTextureName = backdropTextureName;
+#endif
+ int picWidth = sceneWidth;
+ int picHeight = sceneHeight;
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(picWidth);
+ picHeight = getNextPOT(picHeight);
+ }
+#if 0
+ newFreezer -> backdropTexture = new GLubyte [picHeight * picWidth * 4];
+ if (! checkNew(newFreezer -> backdropTexture)) return false;
+
+ saveTexture(backdropTextureName, newFreezer->backdropTexture);
+
+ backdropTextureName = 0;
+#endif
+ newFreezer -> sceneWidth = sceneWidth;
+ newFreezer -> sceneHeight = sceneHeight;
+ newFreezer -> cameraX = cameraX;
+ newFreezer -> cameraY = cameraY;
+ newFreezer -> cameraZoom = cameraZoom;
+#if 0
+ newFreezer -> lightMapTexture = lightMap.data;
+ newFreezer -> lightMapTextureName = lightMap.name;
+ newFreezer -> lightMapNumber = lightMapNumber;
+ lightMap.data = NULL;
+ lightMap.name = 0;
+ newFreezer -> parallaxStuff = parallaxStuff;
+ parallaxStuff = NULL;
+ newFreezer -> zBufferImage = zBuffer.tex;
+ newFreezer -> zBufferNumber = zBuffer.originalNum;
+ newFreezer -> zPanels = zBuffer.numPanels;
+ zBuffer.tex = NULL;
+#endif
+ // resizeBackdrop kills parallax stuff, light map, z-buffer...
+ if (! resizeBackdrop(winWidth, winHeight)) return fatal("Can't create new temporary backdrop buffer");
+
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(sceneWidth);
+ picHeight = getNextPOT(sceneHeight);
+#if 0
+ backdropTexW = (double) sceneWidth / picWidth;
+ backdropTexH = (double) sceneHeight / picHeight;
+#endif
+ }
+
+#if 0
+ // Copy the old scene to the new backdrop
+ deleteTextures(1, &backdropTextureName);
+ backdropTextureName = freezeTextureName;
+ backdropExists = true;
+
+ // Free texture memory used by old stuff
+ parallaxStuff = newFreezer -> parallaxStuff;
+ while (parallaxStuff) {
+ deleteTextures(1, &parallaxStuff -> textureName);
+ parallaxStuff = parallaxStuff -> next;
+ }
+ if (newFreezer -> zBufferImage) {
+ deleteTextures(1, &zBuffer.texName);
+ }
+ if (newFreezer -> lightMapTextureName) {
+ deleteTextures(1, &newFreezer -> lightMapTextureName);
+ }
+ if (newFreezer -> backdropTextureName) {
+ deleteTextures(1, &newFreezer -> backdropTextureName);
+ }
+#endif
+ newFreezer -> allPeople = allPeople;
+ allPeople = NULL;
+
+ statusStuff *newStatusStuff = new statusStuff;
+ if (! checkNew(newStatusStuff)) return false;
+ newFreezer -> frozenStatus = copyStatusBarStuff(newStatusStuff);
+
+ newFreezer -> allScreenRegions = allScreenRegions;
+ allScreenRegions = NULL;
+ overRegion = NULL;
+
+ newFreezer -> mouseCursorAnim = mouseCursorAnim;
+ newFreezer -> mouseCursorFrameNum = mouseCursorFrameNum;
+ mouseCursorAnim = makeNullAnim();
+ mouseCursorFrameNum = 0;
+
+ newFreezer -> speech = speech;
+ initSpeech();
+
+ newFreezer -> currentEvents = currentEvents;
+ currentEvents = new eventHandlers;
+ if (! checkNew(currentEvents)) return false;
+ memset(currentEvents, 0, sizeof(eventHandlers));
+
+ newFreezer -> next = frozenStuff;
+ frozenStuff = newFreezer;
+
+ return true;
+}
+
+int howFrozen() {
+ int a = 0;
+ frozenStuffStruct *f = frozenStuff;
+ while (f) {
+ a ++;
+ f = f -> next;
+ }
+ return a;
+}
+
+#if 0
+extern GLubyte *backdropTexture;
+#endif
+
+void unfreeze(bool killImage) {
+ frozenStuffStruct *killMe = frozenStuff;
+
+ if (! frozenStuff) return;
+
+ sceneWidth = frozenStuff -> sceneWidth;
+ sceneHeight = frozenStuff -> sceneHeight;
+
+ cameraX = frozenStuff -> cameraX;
+ cameraY = frozenStuff -> cameraY;
+ input.mouseX = (int)(input.mouseX * cameraZoom);
+ input.mouseY = (int)(input.mouseY * cameraZoom);
+ cameraZoom = frozenStuff -> cameraZoom;
+ input.mouseX = (int)(input.mouseX / cameraZoom);
+ input.mouseY = (int)(input.mouseY / cameraZoom);
+ setPixelCoords(false);
+
+ killAllPeople();
+ allPeople = frozenStuff -> allPeople;
+
+ killAllRegions();
+ allScreenRegions = frozenStuff -> allScreenRegions;
+
+ killLightMap();
+#if 0
+ lightMap.data = frozenStuff -> lightMapTexture;
+ lightMap.name = frozenStuff -> lightMapTextureName;
+ lightMapNumber = frozenStuff -> lightMapNumber;
+ if (lightMapNumber) {
+ lightMap.name = 0;
+ loadLightMap(lightMapNumber);
+ }
+
+ killZBuffer();
+ zBuffer.tex = frozenStuff -> zBufferImage;
+ zBuffer.originalNum = frozenStuff -> zBufferNumber;
+ zBuffer.numPanels = frozenStuff -> zPanels;
+ if (zBuffer.numPanels) {
+ zBuffer.texName = 0;
+ setZBuffer(zBuffer.originalNum);
+ }
+
+ killParallax();
+ parallaxStuff = frozenStuff -> parallaxStuff;
+ reloadParallaxTextures();
+
+ if (killImage) killBackDrop();
+ backdropTextureName = frozenStuff -> backdropTextureName;
+ if (backdropTexture) delete[] backdropTexture;
+ backdropTexture = frozenStuff -> backdropTexture;
+ backdropExists = true;
+ if (backdropTextureName) {
+ backdropTextureName = 0;
+ glGenTextures(1, &backdropTextureName);
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+
+ int picWidth = sceneWidth;
+ int picHeight = sceneHeight;
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(picWidth);
+ picHeight = getNextPOT(picHeight);
+ }
+ // Restore the backdrop
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, frozenStuff -> backdropTexture, backdropTextureName);
+ }
+#endif
+ deleteAnim(mouseCursorAnim);
+ mouseCursorAnim = frozenStuff -> mouseCursorAnim;
+ mouseCursorFrameNum = frozenStuff -> mouseCursorFrameNum;
+
+ restoreBarStuff(frozenStuff -> frozenStatus);
+
+ delete currentEvents;
+ currentEvents = frozenStuff -> currentEvents;
+ killAllSpeech();
+ delete speech;
+
+ speech = frozenStuff -> speech;
+ frozenStuff = frozenStuff -> next;
+
+ overRegion = NULL;
+ delete killMe;
+ killMe = NULL;
+
+}
diff --git a/engines/sludge/freeze.h b/engines/sludge/freeze.h
new file mode 100644
index 0000000000..1682d3b4b3
--- /dev/null
+++ b/engines/sludge/freeze.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_FREEZE_H
+#define SLUDGE_FREEZE_H
+
+
+struct frozenStuffStruct {
+ onScreenPerson *allPeople;
+ screenRegion *allScreenRegions;
+#if 0
+ GLubyte *backdropTexture;
+ GLuint backdropTextureName;
+ GLuint lightMapTextureName;
+ GLubyte *lightMapTexture;
+ GLubyte *zBufferImage;
+#endif
+ int zPanels;
+ parallaxLayer *parallaxStuff;
+ int lightMapNumber, zBufferNumber;
+ speechStruct *speech;
+ statusStuff *frozenStatus;
+ eventHandlers *currentEvents;
+ personaAnimation *mouseCursorAnim;
+ int mouseCursorFrameNum;
+ int cameraX, cameraY, sceneWidth, sceneHeight;
+ float cameraZoom;
+
+ frozenStuffStruct *next;
+};
+
+
+bool freeze();
+void unfreeze(bool killImage = true);
+int howFrozen();
+
+#endif
diff --git a/engines/sludge/graphics.cpp b/engines/sludge/graphics.cpp
new file mode 100644
index 0000000000..8bdc917515
--- /dev/null
+++ b/engines/sludge/graphics.cpp
@@ -0,0 +1,956 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#if 0
+#include <stdarg.h>
+
+#include <SDL/SDL.h>
+#endif
+
+#include "allfiles.h"
+#include "debug.h"
+#include "platform-dependent.h"
+#include "CommonCode/specialsettings.h"
+#include "graphics.h"
+#include "language.h"
+#include "newfatal.h"
+#include "sprbanks.h"
+#include "zbuffer.h"
+#include "backdrop.h"
+#include "shaders.h"
+#include "movie.h"
+#include "stringy.h"
+
+#include "language.h" // for settings
+
+#if 0
+#if !defined(HAVE_GLES2)
+#ifdef _WIN32
+#include <GL\glu.h> // handy for gluErrorString
+#elif defined __APPLE__
+#include <OpenGL/glu.h>
+#else
+#include <GL/glu.h>
+#endif
+#endif
+#endif
+
+unsigned int winWidth, winHeight;
+int viewportHeight, viewportWidth;
+int viewportOffsetX = 0, viewportOffsetY = 0;
+
+extern float cameraZoom;
+
+bool NPOT_textures = true;
+
+extern int specialSettings;
+
+void setMovieViewport();
+
+#if 0
+extern GLuint backdropTextureName;
+extern GLuint snapshotTextureName;
+#endif
+
+extern unsigned int sceneWidth, sceneHeight;
+extern zBufferData zBuffer;
+extern int lightMapNumber;
+#if 0
+extern GLuint yTextureName;
+extern GLuint uTextureName;
+extern GLuint vTextureName;
+//extern GLubyte * ytex, * utex, * vtex;
+
+shaders shader;
+GLfloat aPMVMatrix[16];
+
+void sludgeDisplay();
+
+GLfloat primaryColor[4];
+GLfloat secondaryColor[4];
+#endif
+
+struct textureList *firstTexture = NULL;
+
+textureList *addTexture() {
+ textureList *newTexture = new textureList;
+ newTexture -> next = firstTexture;
+ firstTexture = newTexture;
+ return newTexture;
+}
+
+#if 0
+void deleteTextures(GLsizei n, const GLuint *textures) {
+ if (firstTexture == NULL) {
+ //debugOut("Deleting texture while list is already empty.\n");
+ } else {
+ for (int i = 0; i < n; i++) {
+ bool found = false;
+ textureList *list = firstTexture;
+ if (list->name == textures[i]) {
+ found = true;
+ firstTexture = list->next;
+ delete list;
+ continue;
+ }
+
+ while (list->next) {
+ if (list->next->name == textures[i]) {
+ found = true;
+ textureList *deleteMe = list->next;
+ list->next = list->next->next;
+ delete deleteMe;
+ break;
+ }
+ list = list->next;
+ }
+ //if (!found)
+ // debugOut("Deleting texture that was not in list.\n");
+ }
+ }
+
+ glDeleteTextures(n, textures);
+
+}
+
+void getTextureDimensions(GLuint name, GLint *width, GLint *height) {
+ textureList *list = firstTexture;
+ while (list) {
+ if (list->name == name) {
+ *width = list->width;
+ *height = list->height;
+#if !defined(HAVE_GLES2)
+ //For the following test it is assumed that glBindTexture is always
+ //called for the right texture before getTextureDimensions.
+ GLint tw, th;
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tw);
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &th);
+ if (tw != *width || th != *height) {
+ debugOut("Warning: Texture dimensions don't match: They are %ix%i, but SLUDGEs bookkeeping says %ix%i.\n", tw, th, *width, *height);
+ }
+#endif
+ return;
+ }
+ list = list->next;
+ }
+ fatal("Texture not found in list.\n");
+}
+
+void storeTextureDimensions(GLuint name, GLsizei width, GLsizei height, const char *file, int line) {
+ if (! NPOT_textures && !(((height & (height - 1)) == 0) || ((width & (width - 1)) == 0))) {
+ debugOut("I was told to create a texture with dimensions %ix%i in %s @ line %d although NPOT textures are disabled.\n", width, height, file, line);
+ //height = getNextPOT(height);
+ //width = getNextPOT(width);
+ }
+
+ textureList *list = firstTexture;
+ while (list) {
+ if (list->name == name) {
+ //debugOut("Texture dimensions are overwritten.\n");
+ break;
+ }
+ list = list->next;
+ }
+ if (list == NULL) {
+ list = addTexture();
+ }
+ list->name = name;
+ list->width = width;
+ list->height = height;
+
+}
+#endif
+
+#ifdef HAVE_GLES2
+void glesCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
+ // Work around for broken glCopy(Sub)TexImage2D...
+ void *tmp = malloc(width * height * 4);
+ glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, tmp);
+ glTexSubImage2D(target, level, xoffset, yoffset, width, height, GL_RGBA, GL_UNSIGNED_BYTE, tmp);
+ free(tmp);
+}
+void glesCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
+ // Work around for broken glCopy(Sub)TexImage2D...
+ void *tmp = malloc(width * height * 4);
+ glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, tmp);
+ glTexImage2D(target, level, GL_RGBA, width, height, border, GL_RGBA, GL_UNSIGNED_BYTE, tmp);
+ free(tmp);
+}
+#endif
+
+#if 0
+void dcopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border, GLuint name, const char *file, int line) {
+
+ glBindTexture(GL_TEXTURE_2D, name);
+#ifdef HAVE_GLES2_
+ glesCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+#else
+ glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+#endif
+}
+
+void dcopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, GLuint name, const char *file, int line) {
+ glBindTexture(GL_TEXTURE_2D, name);
+#ifdef HAVE_GLES2_
+ glesCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+#else
+ glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+#endif
+}
+
+void dtexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height,
+ GLint border, GLenum format, GLenum type, const GLvoid *data, GLuint name, const char *file, int line) {
+ storeTextureDimensions(name, width, height, file, line);
+ glBindTexture(GL_TEXTURE_2D, name);
+ glTexImage2D(target, level, internalformat, width, height, border, format, type, data);
+}
+
+void dtexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+ GLenum format, GLenum type, const GLvoid *data, GLuint name, const char *file, int line) {
+ storeTextureDimensions(name, width, height, file, line);
+ glBindTexture(GL_TEXTURE_2D, name);
+ glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, data);
+}
+
+void setPrimaryColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
+ primaryColor[0] = r;
+ primaryColor[1] = g;
+ primaryColor[2] = b;
+ primaryColor[3] = a;
+}
+
+void setSecondaryColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
+ secondaryColor[0] = r;
+ secondaryColor[1] = g;
+ secondaryColor[2] = b;
+ secondaryColor[3] = a;
+}
+
+void drawQuad(GLint program, const GLfloat *vertices, int numTexCoords, ...) {
+ int i, vertexLoc, texCoordLocs[numTexCoords];
+ const GLfloat *texCoords[numTexCoords];
+
+ va_list vl;
+ va_start(vl, numTexCoords);
+ for (i = 0; i < numTexCoords; i++) {
+ texCoords[i] = va_arg(vl, const GLfloat *);
+ }
+ va_end(vl);
+
+ glUniform4f(glGetUniformLocation(program, "myColor"), primaryColor[0], primaryColor[1], primaryColor[2], primaryColor[3]);
+ if (program == shader.smartScaler || program == shader.paste) {
+ glUniform4f(glGetUniformLocation(program, "mySecondaryColor"), secondaryColor[0], secondaryColor[1], secondaryColor[2], secondaryColor[3]);
+ }
+
+ vertexLoc = glGetAttribLocation(program, "myVertex");
+ texCoordLocs[0] = glGetAttribLocation(program, "myUV0");
+ if (numTexCoords > 1) texCoordLocs[1] = glGetAttribLocation(program, "myUV1");
+ if (numTexCoords > 2) texCoordLocs[2] = glGetAttribLocation(program, "myUV2");
+ if (numTexCoords > 3) texCoordLocs[3] = glGetAttribLocation(program, "myUV3");
+
+ glEnableVertexAttribArray(vertexLoc);
+ glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, GL_FALSE, 0, vertices);
+
+ for (i = 0; i < numTexCoords; i++) {
+ if (texCoords[i]) {
+ glEnableVertexAttribArray(texCoordLocs[i]);
+ glVertexAttribPointer(texCoordLocs[i], 2, GL_FLOAT, GL_FALSE, 0, texCoords[i]);
+ }
+ }
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ for (i = 0; i < numTexCoords; i++) {
+ if (texCoords[i]) {
+ glDisableVertexAttribArray(texCoordLocs[i]);
+ }
+ }
+ glDisableVertexAttribArray(vertexLoc);
+
+}
+
+
+void setPMVMatrix(GLint program) {
+ glUniformMatrix4fv(glGetUniformLocation(program, "myPMVMatrix"), 1, GL_FALSE, aPMVMatrix);
+}
+#endif
+// This is for swapping settings between rendering to texture or to the screen
+void setPixelCoords(bool pixels) {
+ static int current = -1;
+// if (current == pixels) return;
+ current = pixels;
+#if 0
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+
+ if (pixels) {
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ const GLfloat bPMVMatrix[] = {
+ 2.0f / viewportWidth, .0, .0, .0,
+ .0, 2.0f / viewportHeight, .0, .0,
+ .0, .0, 1.0f, .0,
+ -1.0, -1.0f, .0, 1.0f
+
+ };
+ for (int i = 0; i < 16; i++) {
+ aPMVMatrix[i] = bPMVMatrix[i];
+ }
+ } else {
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+
+ GLfloat w = (GLfloat) winWidth / cameraZoom;
+ GLfloat h = (GLfloat) winHeight / cameraZoom;
+
+ const GLfloat bPMVMatrix[] = {
+ 2.0f / w, .0, .0, .0,
+ .0, -2.0f / h, .0, .0,
+ .0, .0, 1.0f, .0,
+ -1.0, 1.0f, .0, 1.0f
+
+ };
+ for (int i = 0; i < 16; i++) {
+ aPMVMatrix[i] = bPMVMatrix[i];
+ }
+ }
+#endif
+}
+
+int desktopW = 0, desktopH = 0;
+bool runningFullscreen = false;
+
+
+#if defined(HAVE_GLES2)
+void saveTexture(GLuint tex, GLubyte *data) {
+ // use an FBO to easily grab the texture...
+ static GLuint fbo = 0;
+ GLuint old_fbo;
+ GLint tw, th;
+ GLint old_vp[4];
+ if (fbo == 0) {
+ glGenFramebuffers(1, &fbo);
+ }
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&old_fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
+ getTextureDimensions(tex, &tw, &th);
+ glGetIntegerv(GL_VIEWPORT, old_vp);
+ glViewport(0, 0, tw, th);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glReadPixels(0, 0, tw, th, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ glViewport(old_vp[0], old_vp[1], old_vp[2], old_vp[3]);
+ glBindFramebuffer(GL_FRAMEBUFFER, old_fbo);
+}
+#elif defined _WIN32
+// Replacement for glGetTexImage, because some ATI drivers are buggy.
+void saveTexture(GLuint tex, GLubyte *data) {
+ setPixelCoords(true);
+
+ glBindTexture(GL_TEXTURE_2D, tex);
+
+ GLint tw, th;
+ getTextureDimensions(tex, &tw, &th);
+
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ int xoffset = 0;
+ while (xoffset < tw) {
+ int w = (tw - xoffset < viewportWidth) ? tw - xoffset : viewportWidth;
+
+ int yoffset = 0;
+ while (yoffset < th) {
+ int h = (th - yoffset < viewportHeight) ? th - yoffset : viewportHeight;
+
+ glClear(GL_COLOR_BUFFER_BIT); // Clear The Screen
+
+ const GLfloat vertices[] = {
+ (GLfloat) - xoffset, (GLfloat) - yoffset, 0.f,
+ (GLfloat)tw - xoffset, (GLfloat) - yoffset, 0.f,
+ (GLfloat) - xoffset, (GLfloat) - yoffset + th, 0.f,
+ (GLfloat)tw - xoffset, (GLfloat) - yoffset + th, 0.f
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f
+ };
+
+ glUseProgram(shader.texture);
+ setPMVMatrix(shader.texture);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+ glUseProgram(0);
+
+ for (int i = 0; i < h; i++) {
+ glReadPixels(viewportOffsetX, viewportOffsetY + i, w, 1, GL_RGBA, GL_UNSIGNED_BYTE, data + xoffset * 4 + (yoffset + i) * 4 * tw);
+ }
+
+ yoffset += viewportHeight;
+ }
+
+ xoffset += viewportWidth;
+ }
+ //glReadPixels(viewportOffsetX, viewportOffsetY, tw, th, GL_RGBA, GL_UNSIGNED_BYTE, data);
+
+ setPixelCoords(false);
+
+}
+#else
+#if 0
+void saveTexture(GLuint tex, GLubyte *data) {
+
+ glBindTexture(GL_TEXTURE_2D, tex);
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+}
+#endif
+#endif
+
+// This is for setting windowed or fullscreen graphics.
+// Used for switching, and for initial window creation.
+void setGraphicsWindow(bool fullscreen, bool restoreGraphics, bool resize) {
+#if defined(PANDORA)
+ fullscreen = true;
+#endif
+#if 0
+ GLubyte *snapTexture = NULL;
+
+ Uint32 videoflags = 0;
+
+ if (! desktopW) {
+
+ // Get video hardware information
+ const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
+ desktopW = videoInfo->current_w;
+ desktopH = videoInfo->current_h;
+
+ } else if (restoreGraphics && fullscreen == runningFullscreen & ! resize) return;
+
+ runningFullscreen = fullscreen;
+
+ if (restoreGraphics) {
+ /*
+ * Save the textures
+ */
+ if (backdropTextureName) {
+ if (backdropTexture) delete backdropTexture;
+ int picWidth = sceneWidth;
+ int picHeight = sceneHeight;
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(picWidth);
+ picHeight = getNextPOT(picHeight);
+ }
+ backdropTexture = new GLubyte [picHeight * picWidth * 4];
+ if (! checkNew(backdropTexture)) return;
+
+ saveTexture(backdropTextureName, backdropTexture);
+ }
+ if (snapshotTextureName) {
+ int picWidth = winWidth;
+ int picHeight = winHeight;
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(picWidth);
+ picHeight = getNextPOT(picHeight);
+ }
+ snapTexture = new GLubyte [picHeight * picWidth * 4];
+ if (! checkNew(snapTexture)) return;
+
+ saveTexture(snapshotTextureName, snapTexture);
+ }
+ }
+
+ /*
+ * Set the graphics mode
+ */
+ float winAspect = (float) winWidth / winHeight;
+
+ if (fullscreen) {
+ specialSettings &= ~SPECIAL_INVISIBLE;
+#if !defined(HAVE_GLES2)
+ videoflags = SDL_OPENGL | SDL_FULLSCREEN;
+#else
+ videoflags = SDL_SWSURFACE | SDL_FULLSCREEN;
+#endif
+
+ if (gameSettings.fixedPixels) {
+ viewportWidth = realWinWidth = winWidth;
+ viewportHeight = realWinHeight = winHeight;
+ viewportOffsetY = 0;
+ viewportOffsetX = 0;
+ } else {
+ realWinWidth = desktopW;
+ realWinHeight = desktopH;
+
+ float realAspect = (float) realWinWidth / realWinHeight;
+
+ if (realAspect > winAspect) {
+ viewportHeight = realWinHeight;
+ viewportWidth = (int)(realWinHeight * winAspect);
+ viewportOffsetY = 0;
+ viewportOffsetX = (realWinWidth - viewportWidth) / 2;
+ } else {
+ viewportWidth = realWinWidth;
+ viewportHeight = (int)((float) realWinWidth / winAspect);
+ viewportOffsetY = (realWinHeight - viewportHeight) / 2;
+ viewportOffsetX = 0;
+ }
+ }
+
+ } else {
+#if !defined(HAVE_GLES2)
+ videoflags = SDL_OPENGL/* | SDL_RESIZABLE*/;
+#else
+ videoflags = SDL_SWSURFACE;
+#endif
+
+ if (resize) {
+ float realAspect = (float) desktopW / desktopH;
+
+ if (realAspect > winAspect) {
+ realWinWidth = (int)(realWinHeight * winAspect);
+ } else {
+ realWinHeight = (int)(realWinWidth / winAspect);
+ }
+
+ realAspect = (float) realWinWidth / realWinHeight;
+
+ if (realAspect > winAspect) {
+ viewportHeight = realWinHeight;
+ viewportWidth = (int)(realWinHeight * winAspect);
+ viewportOffsetY = 0;
+ viewportOffsetX = (realWinWidth - viewportWidth) / 2;
+ } else {
+ viewportWidth = realWinWidth;
+ viewportHeight = (int)((float) realWinWidth / winAspect);
+ viewportOffsetY = (realWinHeight - viewportHeight) / 2;
+ viewportOffsetX = 0;
+ }
+ } else {
+
+ if (gameSettings.fixedPixels) {
+ viewportWidth = realWinWidth = winWidth;
+ viewportHeight = realWinHeight = winHeight;
+ viewportOffsetY = 0;
+ viewportOffsetX = 0;
+ } else {
+ realWinHeight = desktopH * 3 / 4;
+ realWinWidth = (int)(realWinHeight * winAspect);
+
+ if (realWinWidth > desktopW) {
+ realWinWidth = desktopW;
+ realWinHeight = (int)((float) realWinWidth / winAspect);
+ }
+
+ viewportHeight = realWinHeight;
+ viewportWidth = realWinWidth;
+ viewportOffsetY = 0;
+ viewportOffsetX = 0;
+ }
+ }
+ }
+
+ debugHeader();
+
+ if (SDL_SetVideoMode(realWinWidth, realWinHeight, 32, videoflags) == 0) {
+ msgBox("Startup Error: Couldn't set video mode.", SDL_GetError());
+ SDL_Quit();
+ exit(2);
+ }
+ debugOut("Video mode %d %d set successfully.\n", realWinWidth, realWinHeight);
+
+#if defined(HAVE_GLES2)
+ if (EGL_Open()) {
+ msgBox("Startup Error", "Couldn't initialize EGL.");
+ SDL_Quit();
+ exit(1);
+ }
+ EGL_Init();
+#endif
+
+ GLint uniform;
+ const char *Vertex;
+ const char *Fragment;
+
+ Vertex = shaderFileRead("scale.vert");
+
+#if !defined(HAVE_GLES2)
+ Fragment = shaderFileRead("scale.frag");
+#else
+ /* const GLubyte *str;
+ int glDerivativesAvailable;
+ str = glGetString (GL_EXTENSIONS);
+ glDerivativesAvailable = (strstr((const char *)str, "GL_OES_standard_derivatives") != NULL);
+ if (!glDerivativesAvailable) {
+ debugOut("Extension \"GL_OES_standard_derivatives\" not available. Advanced anti-aliasing is not possible. Using linear anti-aliasing instead.");
+ gameSettings.antiAlias = -1;
+ */
+ Fragment = shaderFileRead("scale_noaa.frag");
+// }
+
+ Fragment = joinStrings("precision mediump float;\n", Fragment);
+#endif
+
+ if (! Vertex || ! Fragment) {
+ fatal("Error loading \"scale\" shader program!", "Try re-installing the game. (scale.frag, scale_noaa.frag or scale.vert was not found.)");
+ gameSettings.antiAlias = -1;
+ shader.smartScaler = 0;
+ } else {
+
+ shader.smartScaler = buildShaders(Vertex, Fragment);
+
+ if (! shader.smartScaler) {
+ fatal("Error building \"scale\" shader program!");
+ gameSettings.antiAlias = -1;
+ shader.smartScaler = 0;
+ } else {
+ debugOut("Built shader program: %d (smartScaler)\n", shader.smartScaler);
+
+ glUseProgram(shader.smartScaler);
+ uniform = glGetUniformLocation(shader.smartScaler, "Texture");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+ uniform = glGetUniformLocation(shader.smartScaler, "lightTexture");
+ if (uniform >= 0) glUniform1i(uniform, 1);
+ uniform = glGetUniformLocation(shader.smartScaler, "useLightTexture");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+ uniform = glGetUniformLocation(shader.smartScaler, "antialias");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+ uniform = glGetUniformLocation(shader.smartScaler, "scale");
+
+ float scale = (float)realWinWidth / (float)winWidth * 0.25;
+ if (scale > 1.0) scale = 1.0;
+ if (uniform >= 0) glUniform1f(uniform, scale);
+
+ }
+ }
+
+ Vertex = shaderFileRead("fixScaleSprite.vert");
+ Fragment = shaderFileRead("fixScaleSprite.frag");
+
+#if defined(HAVE_GLES2)
+ Fragment = joinStrings("precision mediump float;\n", Fragment);
+#endif
+
+ if (! Vertex || ! Fragment) {
+ fatal("Error loading \"fixScaleSprite\" shader program!", "Try re-installing the game. (fixScaleSprite.frag or fixScaleSprite.vert was not found.)");
+ shader.paste = 0;
+ } else {
+
+ shader.paste = buildShaders(Vertex, Fragment);
+ if (! shader.paste) {
+ fatal("Error building \"fixScaleSprite\" shader program!");
+ } else {
+ debugOut("Built shader program: %d (fixScaleSprite)\n", shader.paste);
+
+ glUseProgram(shader.paste);
+ uniform = glGetUniformLocation(shader.paste, "tex0");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+ uniform = glGetUniformLocation(shader.paste, "tex1");
+ if (uniform >= 0) glUniform1i(uniform, 1);
+ uniform = glGetUniformLocation(shader.paste, "tex2");
+ if (uniform >= 0) glUniform1i(uniform, 2);
+ uniform = glGetUniformLocation(shader.paste, "useLightTexture");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+
+ }
+ }
+
+ Vertex = shaderFileRead("yuv.vert");
+ Fragment = shaderFileRead("yuv.frag");
+
+#if defined(HAVE_GLES2)
+ Fragment = joinStrings("precision mediump float;\n", Fragment);
+#endif
+
+ if (! Vertex || ! Fragment) {
+ fatal("Error loading \"yuv\" shader program!", "Try re-installing the game. (yuv.frag or yuv.vert was not found.)");
+ shader.yuv = 0;
+ } else {
+
+ shader.yuv = buildShaders(Vertex, Fragment);
+ if (! shader.yuv) {
+ fatal("Error building \"yuv\" shader program!");
+ } else {
+ debugOut("Built shader program: %d (yuv)\n", shader.yuv);
+
+ glUseProgram(shader.yuv);
+ uniform = glGetUniformLocation(shader.yuv, "Ytex");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+ uniform = glGetUniformLocation(shader.yuv, "Utex");
+ if (uniform >= 0) glUniform1i(uniform, 1);
+ uniform = glGetUniformLocation(shader.yuv, "Vtex");
+ if (uniform >= 0) glUniform1i(uniform, 2);
+
+ }
+ }
+
+ Vertex = shaderFileRead("texture.vert");
+ Fragment = shaderFileRead("texture.frag");
+
+#if defined(HAVE_GLES2)
+ Fragment = joinStrings("precision mediump float;\n", Fragment);
+#endif
+
+ if (! Vertex || ! Fragment) {
+ fatal("Error loading \"texture\" shader program!", "Try re-installing the game. (texture.frag or texture.vert was not found.)");
+ shader.texture = 0;
+ } else {
+
+ shader.texture = buildShaders(Vertex, Fragment);
+ if (! shader.texture) {
+ fatal("Error building \"texture\" shader program!");
+ } else {
+ debugOut("Built shader program: %d (texture)\n", shader.texture);
+
+ glUseProgram(shader.texture);
+ uniform = glGetUniformLocation(shader.texture, "sampler2d");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+ uniform = glGetUniformLocation(shader.texture, "zBuffer");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+ uniform = glGetUniformLocation(shader.texture, "zBufferLayer");
+ if (uniform >= 0) glUniform1f(uniform, 0.);
+ uniform = glGetUniformLocation(shader.texture, "modulateColor");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+ }
+ }
+
+ Vertex = shaderFileRead("color.vert");
+ Fragment = shaderFileRead("color.frag");
+
+#if defined(HAVE_GLES2)
+ Fragment = joinStrings("precision mediump float;\n", Fragment);
+#endif
+
+ if (! Vertex || ! Fragment) {
+ fatal("Error loading \"color\" shader program!", "Try re-installing the game. (color.frag or color.vert was not found.)");
+ shader.color = 0;
+ } else {
+
+ shader.color = buildShaders(Vertex, Fragment);
+ if (! shader.color) {
+ fatal("Error building \"color\" shader program!");
+ } else {
+ debugOut("Built shader program: %d (color)\n", shader.color);
+ glUseProgram(shader.color);
+ }
+ }
+ glUseProgram(0);
+
+ glViewport(viewportOffsetX, viewportOffsetY, viewportWidth, viewportHeight);
+
+ /*
+ * Set up OpenGL for 2D rendering.
+ */
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ setPixelCoords(false);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ if (restoreGraphics) {
+ /*
+ * Restore the textures
+ */
+ if (backdropTextureName) {
+ if (!glIsTexture(backdropTextureName)) {
+ glGenTextures(1, &backdropTextureName);
+ }
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+ // Restore the backdrop
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sceneWidth, sceneHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, backdropTexture, backdropTextureName);
+
+ }
+ if (snapshotTextureName) {
+ if (!glIsTexture(snapshotTextureName)) {
+ glGenTextures(1, &snapshotTextureName);
+ }
+ glBindTexture(GL_TEXTURE_2D, snapshotTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ // Restore the backdrop
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, winWidth, winHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, snapTexture, snapshotTextureName);
+ delete snapTexture;
+ }
+
+ if (yTextureName) {
+ if (!glIsTexture(yTextureName)) {
+ glGenTextures(1, &yTextureName);
+ glGenTextures(1, &uTextureName);
+ glGenTextures(1, &vTextureName);
+ }
+ }
+
+
+ reloadSpriteTextures();
+ reloadParallaxTextures();
+ zBuffer.texName = 0;
+ if (zBuffer.numPanels) {
+ setZBuffer(zBuffer.originalNum);
+ }
+ lightMap.name = 0;
+ if (lightMapNumber) {
+ loadLightMap(lightMapNumber);
+ }
+
+ sludgeDisplay();
+ }
+
+ if (movieIsPlaying)
+ setMovieViewport();
+#endif
+}
+
+void setupOpenGLStuff() {
+
+ /*
+ * Time to setup our requested window attributes for our OpenGL window.
+ * We want *at least* 8 bits of red, green and blue. We also want at least a 16-bit
+ * depth buffer.
+ *
+ * The last thing we do is request a double buffered window. '1' turns on double
+ * buffering, '0' turns it off.
+ */
+#if 0
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+#endif
+ setGraphicsWindow(gameSettings.userFullScreen, false);
+
+#if !defined(HAVE_GLES2)
+#if 0
+ /* Check for graphics capabilities... */
+ if (GLEE_VERSION_2_0) {
+ // Yes! Textures can be any size!
+ NPOT_textures = true;
+ debugOut("OpenGL 2.0! All is good.\n");
+ } else {
+ if (GLEE_VERSION_1_5) {
+ debugOut("OpenGL 1.5!\n");
+ } else if (GLEE_VERSION_1_4) {
+ debugOut("OpenGL 1.4!\n");
+ } else if (GLEE_VERSION_1_3) {
+ debugOut("OpenGL 1.3!\n");
+ } else if (GLEE_VERSION_1_2) {
+ debugOut("OpenGL 1.2!\n");
+ }
+
+ if (GLEE_ARB_texture_non_power_of_two) {
+ // Yes! Textures can be any size!
+ NPOT_textures = true;
+ } else {
+ // Workaround needed for lesser graphics cards. Let's hope this works...
+ NPOT_textures = false;
+ debugOut("Warning: Old graphics card! GLEE_ARB_texture_non_power_of_two not supported.\n");
+ }
+
+ if (GLEE_ARB_shading_language_100) {
+ debugOut("ARB_shading_language_100 supported.\n");
+ } else {
+ debugOut("Warning: Old graphics card! ARB_shading_language_100 not supported. Try updating your drivers.\n");
+ }
+ if (GLEE_ARB_shader_objects) {
+ debugOut("ARB_shader_objects supported.\n");
+ } else {
+ fatal("Error: Old graphics card! ARB_shader_objects not supported.\n");
+ }
+ if (GLEE_ARB_vertex_shader) {
+ debugOut("ARB_vertex_shader supported.\n");
+ } else {
+ fatal("Error: Old graphics card! ARB_vertex_shader not supported.\n");
+ }
+ if (GLEE_ARB_fragment_shader) {
+ debugOut("ARB_fragment_shader supported.\n");
+ } else {
+ fatal("Error: Old graphics card! ARB_fragment_shader not supported.\n");
+ }
+ }
+#else
+ NPOT_textures = false;
+#endif
+#endif
+ int n;
+#if 0
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, (GLint *) &n);
+#endif
+ debugOut("Max texture image units: %d\n", n);
+
+}
+
+
+// I found this function on a coding forum on the 'net.
+// Looks a bit weird, but it should work.
+int getNextPOT(int n) {
+ --n;
+ n |= n >> 16;
+ n |= n >> 8;
+ n |= n >> 4;
+ n |= n >> 2;
+ n |= n >> 1;
+ ++n;
+ return n;
+}
+
+int printOglError(const char *file, int line) {
+ /* Returns 1 if an OpenGL error occurred, 0 otherwise. */
+ int retCode = 0;
+#if 0
+ GLenum glErr;
+
+ glErr = glGetError();
+ while (glErr != GL_NO_ERROR) {
+#if !defined(HAVE_GLES2)
+ debugOut("glError in file %s @ line %d: %s\n", file, line, gluErrorString(glErr));
+#else
+ debugOut("glError in file %s @ line %d: error code %i\n", file, line, glErr);
+#endif
+ retCode = 1;
+ glErr = glGetError();
+ }
+#endif
+ return retCode;
+}
diff --git a/engines/sludge/graphics.h b/engines/sludge/graphics.h
new file mode 100644
index 0000000000..ca95307f51
--- /dev/null
+++ b/engines/sludge/graphics.h
@@ -0,0 +1,118 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_GRAPHICS_H
+#define SLUDGE_GRAPHICS_H
+
+#if 0
+#if !defined(HAVE_GLES2)
+#include "GLee.h"
+#else
+#include <GLES2/gl2.h>
+#include "eglport/eglport.h"
+#endif
+#endif
+
+
+struct texture {
+#if 0
+ GLubyte *data;
+ GLuint name;
+#endif
+ int w, h;
+ double texW, texH;
+};
+
+#if 0
+struct shaders {
+ GLuint paste;
+ GLuint smartScaler;
+ GLuint yuv;
+ GLuint texture;
+ GLuint color;
+};
+#endif
+
+struct textureList {
+#if 0
+ GLuint name;
+ GLsizei width;
+ GLsizei height;
+#endif
+ struct textureList *next;
+};
+
+#if 0
+// From Backdrop.cpp, but they're here anyway
+extern GLubyte *backdropTexture;
+extern GLfloat backdropTexW, backdropTexH;
+#endif
+
+extern unsigned int winWidth, winHeight;
+extern int viewportHeight, viewportWidth;
+extern int viewportOffsetX, viewportOffsetY;
+extern int realWinWidth, realWinHeight;
+
+extern bool NPOT_textures;
+
+#if 0
+extern shaders shader;
+extern GLfloat aPMVMatrix[];
+
+void setPrimaryColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
+void setSecondaryColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
+
+void drawQuad(GLint program, const GLfloat *vertices, int numTexCoords, ...);
+
+void setPMVMatrix(GLint program);
+#endif
+
+void setPixelCoords(bool pixels);
+void setGraphicsWindow(bool fullscreen, bool restoreGraphics = true, bool resize = false);
+
+void setupOpenGLStuff();
+
+int getNextPOT(int n);
+
+#if 0
+void saveTexture(GLuint tex, GLubyte *data);
+
+void dcopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border, GLuint name, const char *file, int line);
+void dcopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, GLuint name, const char *file, int line);
+void dtexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *data, GLuint name, const char *file, int line);
+void dtexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data, GLuint name, const char *file, int line);
+
+#define copyTexImage2D(target, level, internalformat, x, y, width, height, border, name) dcopyTexImage2D(target, level, internalformat, x, y, width,height, border, name, __FILE__, __LINE__)
+
+#define copyTexSubImage2D(target, level, xoffset,yoffset, x, y, width, height, name) dcopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height, name, __FILE__, __LINE__)
+
+#define texImage2D(target, level, internalformat, width, height, border, format, type, data,name) dtexImage2D( target, level, internalformat, width, height, border, format, type, data, name, __FILE__, __LINE__)
+
+#define texSubImage2D( target, level, xoffset, yoffset, width, height, format, type, data,name) dtexSubImage2D( target, level, xoffset, yoffset, width, height, format, type, data, name, __FILE__, __LINE__)
+
+void deleteTextures(GLsizei n, const GLuint *textures);
+
+void getTextureDimensions(GLuint name, GLint *width, GLint *height);
+
+int printOglError(const char *file, int line);
+#define printOpenGLError() printOglError(__FILE__, __LINE__)
+#endif
+#endif
diff --git a/engines/sludge/helpers.cpp b/engines/sludge/helpers.cpp
new file mode 100644
index 0000000000..f25f878b5d
--- /dev/null
+++ b/engines/sludge/helpers.cpp
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+#include "helpers.h"
+
+bool fileExists(const char *file) {
+ bool retval = false;
+#if ALLOW_FILE
+ FILE *tester;
+ tester = fopen(file, "rb");
+ if (tester) {
+ retval = true;
+ fclose(tester);
+ }
+#endif
+ return retval;
+}
diff --git a/engines/sludge/helpers.h b/engines/sludge/helpers.h
new file mode 100644
index 0000000000..82d8e40b23
--- /dev/null
+++ b/engines/sludge/helpers.h
@@ -0,0 +1,27 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_HELPERS_H
+#define SLUDGE_HELPERS_H
+
+bool fileExists(const char *file);
+
+#endif
diff --git a/engines/sludge/language.cpp b/engines/sludge/language.cpp
new file mode 100644
index 0000000000..3a956615e4
--- /dev/null
+++ b/engines/sludge/language.cpp
@@ -0,0 +1,210 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include "stringy.h"
+#include "newfatal.h"
+#include "moreio.h"
+#include "language.h"
+#include "CommonCode/version.h"
+#include "platform-dependent.h"
+
+int *languageTable;
+char **languageName;
+settingsStruct gameSettings;
+
+unsigned int stringToInt(char *s) {
+ int i = 0;
+ bool negative = false;
+ for (;;) {
+ if (*s >= '0' && *s <= '9') {
+ i *= 10;
+ i += *s - '0';
+ s ++;
+ } else if (*s == '-') {
+ negative = ! negative;
+ s++;
+ } else {
+ if (negative)
+ return -i;
+ return i;
+ }
+ }
+}
+
+
+char *getPrefsFilename(char *filename) {
+ // Yes, this trashes the original string, but
+ // we also free it at the end (warning!)...
+
+ int n, i;
+
+ n = strlen(filename);
+
+ if (n > 4 && filename[n - 4] == '.') {
+ filename[n - 4] = 0;
+ }
+
+ char *f = filename;
+ for (i = 0; i < n; i++) {
+#ifdef _WIN32
+ if (filename[i] == '\\')
+#else
+ if (filename[i] == '/')
+#endif
+ f = filename + i + 1;
+ }
+
+ char *joined = joinStrings(f, ".ini");
+
+ delete filename;
+ filename = NULL;
+ return joined;
+}
+
+void readIniFile(char *filename) {
+#if ALLOW_FILE
+ char *langName = getPrefsFilename(copyString(filename));
+
+ FILE *fp = fopen(langName, "rb");
+
+ gameSettings.languageID = 0;
+ gameSettings.userFullScreen = defaultUserFullScreen();
+ gameSettings.refreshRate = 0;
+ gameSettings.antiAlias = 1;
+ gameSettings.fixedPixels = false;
+ gameSettings.noStartWindow = false;
+ gameSettings.debugMode = false;
+
+ delete langName;
+ langName = NULL;
+
+ if (fp) {
+ char lineSoFar[257] = "";
+ char secondSoFar[257] = "";
+ unsigned char here = 0;
+ char readChar = ' ';
+ bool keepGoing = true;
+ bool doingSecond = false;
+
+ do {
+ readChar = fgetc(fp);
+ if (feof(fp)) {
+ readChar = '\n';
+ keepGoing = false;
+ }
+ switch (readChar) {
+ case '\n':
+ case '\r':
+ if (doingSecond) {
+ if (strcmp(lineSoFar, "LANGUAGE") == 0) {
+ gameSettings.languageID = stringToInt(secondSoFar);
+ } else if (strcmp(lineSoFar, "WINDOW") == 0) {
+ gameSettings.userFullScreen = ! stringToInt(secondSoFar);
+ } else if (strcmp(lineSoFar, "REFRESH") == 0) {
+ gameSettings.refreshRate = stringToInt(secondSoFar);
+ } else if (strcmp(lineSoFar, "ANTIALIAS") == 0) {
+ gameSettings.antiAlias = stringToInt(secondSoFar);
+ } else if (strcmp(lineSoFar, "FIXEDPIXELS") == 0) {
+ gameSettings.fixedPixels = stringToInt(secondSoFar);
+ } else if (strcmp(lineSoFar, "NOSTARTWINDOW") == 0) {
+ gameSettings.noStartWindow = stringToInt(secondSoFar);
+ } else if (strcmp(lineSoFar, "DEBUGMODE") == 0) {
+ gameSettings.debugMode = stringToInt(secondSoFar);
+ }
+ }
+ here = 0;
+ doingSecond = false;
+ lineSoFar[0] = 0;
+ secondSoFar[0] = 0;
+ break;
+
+ case '=':
+ doingSecond = true;
+ here = 0;
+ break;
+
+ default:
+ if (doingSecond) {
+ secondSoFar[here ++] = readChar;
+ secondSoFar[here] = 0;
+ } else {
+ lineSoFar[here ++] = readChar;
+ lineSoFar[here] = 0;
+ }
+ break;
+ }
+ } while (keepGoing);
+
+ fclose(fp);
+ }
+#endif
+}
+
+void saveIniFile(char *filename) {
+#if ALLOW_FILE
+ char *langName = getPrefsFilename(copyString(filename));
+ FILE *fp = fopen(langName, "wt");
+ delete langName;
+
+ fprintf(fp, "LANGUAGE=%d\n", gameSettings.languageID);
+ fprintf(fp, "WINDOW=%d\n", ! gameSettings.userFullScreen);
+ fprintf(fp, "ANTIALIAS=%d\n", gameSettings.antiAlias);
+ fprintf(fp, "FIXEDPIXELS=%d\n", gameSettings.fixedPixels);
+ fprintf(fp, "NOSTARTWINDOW=%d\n", gameSettings.noStartWindow);
+ fprintf(fp, "DEBUGMODE=%d\n", gameSettings.debugMode);
+
+ fclose(fp);
+#endif
+}
+
+#if ALLOW_FILE
+void makeLanguageTable(FILE *table) {
+ languageTable = new int[gameSettings.numLanguages + 1];
+ if (! checkNew(languageTable)) return;
+
+ languageName = new char *[gameSettings.numLanguages + 1];
+ if (! checkNew(languageName)) return;
+
+ for (unsigned int i = 0; i <= gameSettings.numLanguages; i ++) {
+ languageTable[i] = i ? get2bytes(table) : 0;
+ printf("languageTable %i: %i\n", i, languageTable[i]);
+ languageName[i] = 0;
+ if (gameVersion >= VERSION(2, 0)) {
+ if (gameSettings.numLanguages) {
+ languageName[i] = readString(table);
+ printf("languageName %i: %s\n", i, languageName[i]);
+ }
+ }
+ }
+}
+#endif
+
+int getLanguageForFileB() {
+ int indexNum = -1;
+
+ for (unsigned int i = 0; i <= gameSettings.numLanguages; i ++) {
+ if (languageTable[i] == gameSettings.languageID) indexNum = i;
+ }
+
+ return indexNum;
+}
diff --git a/engines/sludge/language.h b/engines/sludge/language.h
new file mode 100644
index 0000000000..fff3ef91da
--- /dev/null
+++ b/engines/sludge/language.h
@@ -0,0 +1,46 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef LANGUAGE_H
+#define LANGUAGE_H
+
+struct settingsStruct {
+ unsigned int languageID;
+ unsigned int numLanguages;
+ bool userFullScreen;
+ unsigned int refreshRate;
+ int antiAlias;
+ bool fixedPixels;
+ bool noStartWindow;
+ bool debugMode;
+};
+
+extern settingsStruct gameSettings;
+
+void readIniFile(char *filename);
+void saveIniFile(char *filename);
+int getLanguageForFileB();
+
+#if ALLOW_FILE
+void makeLanguageTable(FILE *table);
+#endif
+
+#endif
diff --git a/engines/sludge/libvorbis/COPYING b/engines/sludge/libvorbis/COPYING
new file mode 100644
index 0000000000..28de72a970
--- /dev/null
+++ b/engines/sludge/libvorbis/COPYING
@@ -0,0 +1,28 @@
+Copyright (c) 2002-2008 Xiph.org Foundation
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiph.org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/engines/sludge/libvorbis/vorbis_misc.h b/engines/sludge/libvorbis/vorbis_misc.h
new file mode 100644
index 0000000000..85fe3074ac
--- /dev/null
+++ b/engines/sludge/libvorbis/vorbis_misc.h
@@ -0,0 +1,57 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: miscellaneous prototypes
+ last mod: $Id: misc.h 16227 2009-07-08 06:58:46Z xiphmont $
+
+ ********************************************************************/
+
+#ifndef _V_RANDOM_H_
+#define _V_RANDOM_H_
+#include "vorbis/codec.h"
+
+extern void *_vorbis_block_alloc(vorbis_block *vb,long bytes);
+extern void _vorbis_block_ripcord(vorbis_block *vb);
+
+#ifdef ANALYSIS
+extern int analysis_noisy;
+extern void _analysis_output(char *base,int i,float *v,int n,int bark,int dB,
+ ogg_int64_t off);
+extern void _analysis_output_always(char *base,int i,float *v,int n,int bark,int dB,
+ ogg_int64_t off);
+#endif
+
+#ifdef DEBUG_MALLOC
+
+#define _VDBG_GRAPHFILE "malloc.m"
+#undef _VDBG_GRAPHFILE
+extern void *_VDBG_malloc(void *ptr,long bytes,char *file,long line);
+extern void _VDBG_free(void *ptr,char *file,long line);
+
+#ifndef MISC_C
+#undef _ogg_malloc
+#undef _ogg_calloc
+#undef _ogg_realloc
+#undef _ogg_free
+
+#define _ogg_malloc(x) _VDBG_malloc(NULL,(x),__FILE__,__LINE__)
+#define _ogg_calloc(x,y) _VDBG_malloc(NULL,(x)*(y),__FILE__,__LINE__)
+#define _ogg_realloc(x,y) _VDBG_malloc((x),(y),__FILE__,__LINE__)
+#define _ogg_free(x) _VDBG_free((x),__FILE__,__LINE__)
+#endif
+#endif
+
+#endif
+
+
+
+
diff --git a/engines/sludge/libvorbis/vorbis_os.h b/engines/sludge/libvorbis/vorbis_os.h
new file mode 100644
index 0000000000..d12f082ebb
--- /dev/null
+++ b/engines/sludge/libvorbis/vorbis_os.h
@@ -0,0 +1,182 @@
+#ifndef _OS_H
+#define _OS_H
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: #ifdef jail to whip a few platforms into the UNIX ideal.
+ last mod: $Id: os.h 16227 2009-07-08 06:58:46Z xiphmont $
+
+ ********************************************************************/
+
+#include <math.h>
+#include <ogg/os_types.h>
+
+#include "vorbis_misc.h"
+
+#ifndef _V_IFDEFJAIL_H_
+# define _V_IFDEFJAIL_H_
+
+# ifdef __GNUC__
+# define STIN static __inline__
+# elif _WIN32
+# define STIN static __inline
+# else
+# define STIN static
+# endif
+
+#ifdef DJGPP
+# define rint(x) (floor((x)+0.5f))
+#endif
+
+#ifndef M_PI
+# define M_PI (3.1415926536f)
+#endif
+
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+# include <malloc.h>
+# define rint(x) (floor((x)+0.5f))
+# define NO_FLOAT_MATH_LIB
+# define FAST_HYPOT(a, b) sqrt((a)*(a) + (b)*(b))
+#endif
+
+#if defined(__SYMBIAN32__) && defined(__WINS__)
+void *_alloca(size_t size);
+# define alloca _alloca
+#endif
+
+#ifndef FAST_HYPOT
+# define FAST_HYPOT hypot
+#endif
+
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#endif
+
+#ifdef USE_MEMORY_H
+# include <memory.h>
+#endif
+
+#ifndef min
+# define min(x,y) ((x)>(y)?(y):(x))
+#endif
+
+#ifndef max
+# define max(x,y) ((x)<(y)?(y):(x))
+#endif
+
+
+/* Special i386 GCC implementation */
+#if defined(__i386__) && defined(__GNUC__) && !defined(__BEOS__)
+# define VORBIS_FPU_CONTROL
+/* both GCC and MSVC are kinda stupid about rounding/casting to int.
+ Because of encapsulation constraints (GCC can't see inside the asm
+ block and so we end up doing stupid things like a store/load that
+ is collectively a noop), we do it this way */
+
+/* we must set up the fpu before this works!! */
+
+typedef ogg_int16_t vorbis_fpu_control;
+
+static inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){
+ ogg_int16_t ret;
+ ogg_int16_t temp;
+ __asm__ __volatile__("fnstcw %0\n\t"
+ "movw %0,%%dx\n\t"
+ "andw $62463,%%dx\n\t"
+ "movw %%dx,%1\n\t"
+ "fldcw %1\n\t":"=m"(ret):"m"(temp): "dx");
+ *fpu=ret;
+}
+
+static inline void vorbis_fpu_restore(vorbis_fpu_control fpu){
+ __asm__ __volatile__("fldcw %0":: "m"(fpu));
+}
+
+/* assumes the FPU is in round mode! */
+static inline int vorbis_ftoi(double f){ /* yes, double! Otherwise,
+ we get extra fst/fld to
+ truncate precision */
+ int i;
+ __asm__("fistl %0": "=m"(i) : "t"(f));
+ return(i);
+}
+#endif /* Special i386 GCC implementation */
+
+
+/* MSVC inline assembly. 32 bit only; inline ASM isn't implemented in the
+ * 64 bit compiler */
+#if defined(_MSC_VER) && !defined(_WIN64) && !defined(_WIN32_WCE)
+# define VORBIS_FPU_CONTROL
+
+typedef ogg_int16_t vorbis_fpu_control;
+
+static __inline int vorbis_ftoi(double f){
+ int i;
+ __asm{
+ fld f
+ fistp i
+ }
+ return i;
+}
+
+static __inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){
+}
+
+static __inline void vorbis_fpu_restore(vorbis_fpu_control fpu){
+}
+
+#endif /* Special MSVC 32 bit implementation */
+
+
+/* Optimized code path for x86_64 builds. Uses SSE2 intrinsics. This can be
+ done safely because all x86_64 CPUs supports SSE2. */
+#if (defined(_MSC_VER) && defined(_WIN64)) || (defined(__GNUC__) && defined (__x86_64__))
+# define VORBIS_FPU_CONTROL
+
+typedef ogg_int16_t vorbis_fpu_control;
+
+#include <emmintrin.h>
+static __inline int vorbis_ftoi(double f){
+ return _mm_cvtsd_si32(_mm_load_sd(&f));
+}
+
+static __inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){
+}
+
+static __inline void vorbis_fpu_restore(vorbis_fpu_control fpu){
+}
+
+#endif /* Special MSVC x64 implementation */
+
+
+/* If no special implementation was found for the current compiler / platform,
+ use the default implementation here: */
+#ifndef VORBIS_FPU_CONTROL
+
+typedef int vorbis_fpu_control;
+
+static int vorbis_ftoi(double f){
+ /* Note: MSVC and GCC (at least on some systems) round towards zero, thus,
+ the floor() call is required to ensure correct roudning of
+ negative numbers */
+ return (int)floor(f+.5);
+}
+
+/* We don't have special code for this compiler/arch, so do it the slow way */
+# define vorbis_fpu_setround(vorbis_fpu_control) {}
+# define vorbis_fpu_restore(vorbis_fpu_control) {}
+
+#endif /* default implementation */
+
+#endif /* _OS_H */
diff --git a/engines/sludge/libwebm/AUTHORS.TXT b/engines/sludge/libwebm/AUTHORS.TXT
new file mode 100644
index 0000000000..f5f8c11948
--- /dev/null
+++ b/engines/sludge/libwebm/AUTHORS.TXT
@@ -0,0 +1,5 @@
+# Names should be added to this file like so:
+# Name or Organization <email address>
+
+Google Inc.
+Rikard Peterson <info@trumgottist.com>
diff --git a/engines/sludge/libwebm/LICENSE.TXT b/engines/sludge/libwebm/LICENSE.TXT
new file mode 100644
index 0000000000..7a6f99547d
--- /dev/null
+++ b/engines/sludge/libwebm/LICENSE.TXT
@@ -0,0 +1,30 @@
+Copyright (c) 2010, Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the name of Google nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/engines/sludge/libwebm/PATENTS.TXT b/engines/sludge/libwebm/PATENTS.TXT
new file mode 100644
index 0000000000..4414d83850
--- /dev/null
+++ b/engines/sludge/libwebm/PATENTS.TXT
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the WebM Project.
+
+Google hereby grants to you a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer, and otherwise run, modify and propagate the contents of this
+implementation of VP8, where such license applies only to those patent
+claims, both currently owned by Google and acquired in the future,
+licensable by Google that are necessarily infringed by this
+implementation of VP8. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of VP8 or any code incorporated within this
+implementation of VP8 constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of VP8
+shall terminate as of the date such litigation is filed.
diff --git a/engines/sludge/libwebm/mkvparser.cpp b/engines/sludge/libwebm/mkvparser.cpp
new file mode 100644
index 0000000000..8a25af70d0
--- /dev/null
+++ b/engines/sludge/libwebm/mkvparser.cpp
@@ -0,0 +1,7327 @@
+// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#include "mkvparser.hpp"
+#include <cassert>
+#include <cstring>
+#include <new>
+#include <climits>
+
+mkvparser::IMkvReader::~IMkvReader()
+{
+}
+
+void mkvparser::GetVersion(int& major, int& minor, int& build, int& revision)
+{
+ major = 1;
+ minor = 0;
+ build = 0;
+ revision = 17;
+}
+
+long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ int status;
+
+//#ifdef _DEBUG
+// long long total, available;
+// status = pReader->Length(&total, &available);
+// assert(status >= 0);
+// assert((total < 0) || (available <= total));
+// assert(pos < available);
+// assert((available - pos) >= 1); //assume here max u-int len is 8
+//#endif
+
+ len = 1;
+
+ unsigned char b;
+
+ status = pReader->Read(pos, 1, &b);
+
+ if (status < 0) //error or underflow
+ return status;
+
+ if (status > 0) //interpreted as "underflow"
+ return E_BUFFER_NOT_FULL;
+
+ if (b == 0) //we can't handle u-int values larger than 8 bytes
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char m = 0x80;
+
+ while (!(b & m))
+ {
+ m >>= 1;
+ ++len;
+ }
+
+//#ifdef _DEBUG
+// assert((available - pos) >= len);
+//#endif
+
+ long long result = b & (~m);
+ ++pos;
+
+ for (int i = 1; i < len; ++i)
+ {
+ status = pReader->Read(pos, 1, &b);
+
+ if (status < 0)
+ {
+ len = 1;
+ return status;
+ }
+
+ if (status > 0)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result <<= 8;
+ result |= b;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+long long mkvparser::GetUIntLength(
+ IMkvReader* pReader,
+ long long pos,
+ long& len)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ int status = pReader->Length(&total, &available);
+ assert(status >= 0);
+ assert((total < 0) || (available <= total));
+
+ len = 1;
+
+ if (pos >= available)
+ return pos; //too few bytes available
+
+ unsigned char b;
+
+ status = pReader->Read(pos, 1, &b);
+
+ if (status < 0)
+ return status;
+
+ assert(status == 0);
+
+ if (b == 0) //we can't handle u-int values larger than 8 bytes
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char m = 0x80;
+
+ while (!(b & m))
+ {
+ m >>= 1;
+ ++len;
+ }
+
+ return 0; //success
+}
+
+long long mkvparser::SyncReadUInt(
+ IMkvReader* pReader,
+ long long pos,
+ long long stop,
+ long& len)
+{
+ assert(pReader);
+
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char b;
+
+ long hr = pReader->Read(pos, 1, &b);
+
+ if (hr < 0)
+ return hr;
+
+ if (hr != 0L)
+ return E_BUFFER_NOT_FULL;
+
+ if (b == 0) //we can't handle u-int values larger than 8 bytes
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char m = 0x80;
+ len = 1;
+
+ while (!(b & m))
+ {
+ m >>= 1;
+ ++len;
+ }
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ long long result = b & (~m);
+ ++pos;
+
+ for (int i = 1; i < len; ++i)
+ {
+ hr = pReader->Read(pos, 1, &b);
+
+ if (hr < 0)
+ return hr;
+
+ if (hr != 0L)
+ return E_BUFFER_NOT_FULL;
+
+ result <<= 8;
+ result |= b;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+
+long long mkvparser::UnserializeUInt(
+ IMkvReader* pReader,
+ long long pos,
+ long long size)
+{
+ assert(pReader);
+ assert(pos >= 0);
+ assert(size > 0);
+ assert(size <= 8);
+
+ long long result = 0;
+
+ for (long long i = 0; i < size; ++i)
+ {
+ unsigned char b;
+
+ const long status = pReader->Read(pos, 1, &b);
+
+ if (status < 0)
+ return status;
+
+ result <<= 8;
+ result |= b;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+
+float mkvparser::Unserialize4Float(
+ IMkvReader* pReader,
+ long long pos)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+#ifdef _DEBUG
+ {
+ long long total, available;
+
+ const long status = pReader->Length(&total, &available);
+ assert(status >= 0);
+ assert((total < 0) || (available <= total));
+ assert((pos + 4) <= available);
+ }
+#endif
+
+#if 0
+ float result;
+
+ unsigned char* const p = (unsigned char*)&result;
+ unsigned char* q = p + 4;
+
+ for (;;)
+ {
+ hr = pReader->Read(pos, 1, --q);
+ assert(hr == 0L);
+
+ if (q == p)
+ break;
+
+ ++pos;
+ }
+#else
+ union
+ {
+ float result;
+ unsigned long buf;
+ };
+
+ buf = 0;
+
+ for (int i = 0;;)
+ {
+ unsigned char b;
+
+ const int status = pReader->Read(pos++, 1, &b);
+
+ if (status < 0) //error
+ return static_cast<float>(status);
+
+ buf |= b;
+
+ if (++i >= 4)
+ break;
+
+ buf <<= 8;
+ }
+#endif
+
+ return result;
+}
+
+
+double mkvparser::Unserialize8Double(
+ IMkvReader* pReader,
+ long long pos)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+#if 0
+ double result;
+
+ unsigned char* const p = (unsigned char*)&result;
+ unsigned char* q = p + 8;
+
+ for (;;)
+ {
+ const long hr = pReader->Read(pos, 1, --q);
+ assert(hr == 0L);
+
+ if (q == p)
+ break;
+
+ ++pos;
+ }
+#else
+ union
+ {
+ double result;
+ long long buf;
+ };
+
+ buf = 0;
+
+ for (int i = 0;;)
+ {
+ unsigned char b;
+
+ const int status = pReader->Read(pos++, 1, &b);
+
+ if (status < 0) //error
+ return static_cast<double>(status);
+
+ buf |= b;
+
+ if (++i >= 8)
+ break;
+
+ buf <<= 8;
+ }
+#endif
+
+ return result;
+}
+
+signed char mkvparser::Unserialize1SInt(
+ IMkvReader* pReader,
+ long long pos)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+#ifdef _DEBUG
+ {
+ long long total, available;
+
+ const long status = pReader->Length(&total, &available);
+ assert(status == 0);
+ assert((total < 0) || (available <= total));
+ assert(pos < available);
+ }
+#endif
+
+ signed char result;
+ unsigned char& b = reinterpret_cast<unsigned char&>(result);
+
+ const int status = pReader->Read(pos, 1, &b);
+ assert(status == 0); //TODO: must be handled somehow
+
+ return result;
+}
+
+short mkvparser::Unserialize2SInt(
+ IMkvReader* pReader,
+ long long pos)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+#ifdef _DEBUG
+ {
+ long long total, available;
+
+ const long status = pReader->Length(&total, &available);
+ assert(status >= 0);
+ assert((total < 0) || (available <= total));
+ assert((pos + 2) <= available);
+ }
+#endif
+
+#if 0
+ short result;
+
+ unsigned char* const p = (unsigned char*)&result;
+ unsigned char* q = p + 2;
+
+ for (;;)
+ {
+ hr = pReader->Read(pos, 1, --q);
+ assert(hr == 0L);
+
+ if (q == p)
+ break;
+
+ ++pos;
+ }
+#else
+ short result = 0;
+
+ for (int i = 0;;)
+ {
+ unsigned char b;
+
+ const int status = pReader->Read(pos++, 1, &b);
+ assert(status == 0); //TODO: must be handled somehow
+
+ result |= b;
+
+ if (++i >= 2)
+ break;
+
+ result <<= 8;
+ }
+#endif
+
+ return result;
+}
+
+
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id_,
+ long long& val)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ const long status = pReader->Length(&total, &available);
+ assert(status >= 0);
+ assert((total < 0) || (available <= total));
+
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ if ((unsigned long)id != id_)
+ return false;
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0);
+ assert(size <= 8);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ pos += len; //consume length of size of payload
+
+ val = UnserializeUInt(pReader, pos, size);
+ assert(val >= 0);
+
+ pos += size; //consume size of payload
+
+ return true;
+}
+
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id_,
+ char*& val)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long status = pReader->Length(&total, &available);
+ assert(status >= 0);
+ assert((total < 0) || (available <= total));
+
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ if ((unsigned long)id != id_)
+ return false;
+
+ pos += len; //consume id
+
+ const long long size_ = ReadUInt(pReader, pos, len);
+ assert(size_ >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ pos += len; //consume length of size of payload
+ assert((pos + size_) <= available);
+
+ const size_t size = static_cast<size_t>(size_);
+ val = new char[size+1];
+
+ for (size_t i = 0; i < size; ++i)
+ {
+ char c;
+
+ status = pReader->Read(pos + i, 1, (unsigned char*)&c);
+ assert(status == 0); //TODO
+
+ val[i] = c;
+
+ if (c == '\0')
+ break;
+ }
+
+ val[size] = '\0';
+ pos += size_; //consume size of payload
+
+ return true;
+}
+
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id_,
+ unsigned char*& buf,
+ size_t& buflen)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long status = pReader->Length(&total, &available);
+ assert(status >= 0);
+ assert((total < 0) || (available <= total));
+
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ if ((unsigned long)id != id_)
+ return false;
+
+ pos += len; //consume id
+
+ const long long size_ = ReadUInt(pReader, pos, len);
+ assert(size_ >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ pos += len; //consume length of size of payload
+ assert((pos + size_) <= available);
+
+ const long buflen_ = static_cast<long>(size_);
+
+ buf = new (std::nothrow) unsigned char[buflen_];
+ assert(buf); //TODO
+
+ status = pReader->Read(pos, buflen_, buf);
+ assert(status == 0); //TODO
+
+ buflen = buflen_;
+
+ pos += size_; //consume size of payload
+ return true;
+}
+
+
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id_,
+ double& val)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ const long status = pReader->Length(&total, &available);
+ assert(status >= 0);
+ assert((total < 0) || (available <= total));
+
+ long idlen;
+
+ const long long id = ReadUInt(pReader, pos, idlen);
+ assert(id >= 0); //TODO
+
+ if ((unsigned long)id != id_)
+ return false;
+
+ long sizelen;
+ const long long size = ReadUInt(pReader, pos + idlen, sizelen);
+
+ switch (size)
+ {
+ case 4:
+ case 8:
+ break;
+ default:
+ return false;
+ }
+
+ pos += idlen + sizelen; //consume id and size fields
+ assert((pos + size) <= available);
+
+ if (size == 4)
+ val = Unserialize4Float(pReader, pos);
+ else
+ {
+ assert(size == 8);
+ val = Unserialize8Double(pReader, pos);
+ }
+
+ pos += size; //consume size of payload
+
+ return true;
+}
+
+
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id_,
+ short& val)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ const long status = pReader->Length(&total, &available);
+ assert(status >= 0);
+ assert((total < 0) || (available <= total));
+
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert((pos + len) <= available);
+
+ if ((unsigned long)id != id_)
+ return false;
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size <= 2);
+ assert((pos + len) <= available);
+
+ pos += len; //consume length of size of payload
+ assert((pos + size) <= available);
+
+ //TODO:
+ // Generalize this to work for any size signed int
+ if (size == 1)
+ val = Unserialize1SInt(pReader, pos);
+ else
+ val = Unserialize2SInt(pReader, pos);
+
+ pos += size; //consume size of payload
+
+ return true;
+}
+
+
+namespace mkvparser
+{
+
+EBMLHeader::EBMLHeader() :
+ m_docType(NULL)
+{
+ Init();
+}
+
+EBMLHeader::~EBMLHeader()
+{
+ delete[] m_docType;
+}
+
+void EBMLHeader::Init()
+{
+ m_version = 1;
+ m_readVersion = 1;
+ m_maxIdLength = 4;
+ m_maxSizeLength = 8;
+
+ if (m_docType)
+ {
+ delete[] m_docType;
+ m_docType = NULL;
+ }
+
+ m_docTypeVersion = 1;
+ m_docTypeReadVersion = 1;
+}
+
+long long EBMLHeader::Parse(
+ IMkvReader* pReader,
+ long long& pos)
+{
+ assert(pReader);
+
+ long long total, available;
+
+ long status = pReader->Length(&total, &available);
+
+ if (status < 0) //error
+ return status;
+
+ pos = 0;
+ long long end = (available >= 1024) ? 1024 : available;
+
+ for (;;)
+ {
+ unsigned char b = 0;
+
+ while (pos < end)
+ {
+ status = pReader->Read(pos, 1, &b);
+
+ if (status < 0) //error
+ return status;
+
+ if (b == 0x1A)
+ break;
+
+ ++pos;
+ }
+
+ if (b != 0x1A)
+ {
+ if (pos >= 1024)
+ return E_FILE_FORMAT_INVALID; //don't bother looking anymore
+
+ if ((total >= 0) && ((total - available) < 5))
+ return E_FILE_FORMAT_INVALID;
+
+ return available + 5; //5 = 4-byte ID + 1st byte of size
+ }
+
+ if ((total >= 0) && ((total - pos) < 5))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((available - pos) < 5)
+ return pos + 5; //try again later
+
+ long len;
+
+ const long long result = ReadUInt(pReader, pos, len);
+
+ if (result < 0) //error
+ return result;
+
+ if (result == 0x0A45DFA3) //EBML Header ID
+ {
+ pos += len; //consume ID
+ break;
+ }
+
+ ++pos; //throw away just the 0x1A byte, and try again
+ }
+
+ //pos designates start of size field
+
+ //get length of size field
+
+ long len;
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return result;
+
+ if (result > 0) //need more data
+ return result;
+
+ assert(len > 0);
+ assert(len <= 8);
+
+ if ((total >= 0) && ((total - pos) < len))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((available - pos) < len)
+ return pos + len; //try again later
+
+ //get the EBML header size
+
+ result = ReadUInt(pReader, pos, len);
+
+ if (result < 0) //error
+ return result;
+
+ pos += len; //consume size field
+
+ //pos now designates start of payload
+
+ if ((total >= 0) && ((total - pos) < result))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((available - pos) < result)
+ return pos + result;
+
+ end = pos + result;
+
+ Init();
+
+ while (pos < end)
+ {
+ if (Match(pReader, pos, 0x0286, m_version))
+ ;
+ else if (Match(pReader, pos, 0x02F7, m_readVersion))
+ ;
+ else if (Match(pReader, pos, 0x02F2, m_maxIdLength))
+ ;
+ else if (Match(pReader, pos, 0x02F3, m_maxSizeLength))
+ ;
+ else if (Match(pReader, pos, 0x0282, m_docType))
+ ;
+ else if (Match(pReader, pos, 0x0287, m_docTypeVersion))
+ ;
+ else if (Match(pReader, pos, 0x0285, m_docTypeReadVersion))
+ ;
+ else
+ {
+ result = ReadUInt(pReader, pos, len);
+ assert(result > 0);
+ assert(len > 0);
+ assert(len <= 8);
+
+ pos += len;
+ assert(pos < end);
+
+ result = ReadUInt(pReader, pos, len);
+ assert(result >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+
+ pos += len + result;
+ assert(pos <= end);
+ }
+ }
+
+ assert(pos == end);
+ return 0;
+}
+
+
+Segment::Segment(
+ IMkvReader* pReader,
+ long long start,
+ long long size) :
+ m_pReader(pReader),
+ m_start(start),
+ m_size(size),
+ m_pos(start),
+ m_pSeekHead(NULL),
+ m_pInfo(NULL),
+ m_pTracks(NULL),
+ m_pCues(NULL),
+ m_clusters(NULL),
+ m_clusterCount(0),
+ m_clusterPreloadCount(0),
+ m_clusterSize(0)
+{
+}
+
+
+Segment::~Segment()
+{
+ const long count = m_clusterCount + m_clusterPreloadCount;
+
+ Cluster** i = m_clusters;
+ Cluster** j = m_clusters + count;
+
+ while (i != j)
+ {
+ Cluster* const p = *i++;
+ assert(p);
+
+ delete p;
+ }
+
+ delete[] m_clusters;
+
+ delete m_pTracks;
+ delete m_pInfo;
+ delete m_pCues;
+ delete m_pSeekHead;
+}
+
+
+long long Segment::CreateInstance(
+ IMkvReader* pReader,
+ long long pos,
+ Segment*& pSegment)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ pSegment = NULL;
+
+ long long total, available;
+
+ const long status = pReader->Length(&total, &available);
+
+// fprintf(stderr, "status: %d total: %d available: %d\n", status, total, available);
+
+ if (status < 0) //error
+ return status;
+
+ if (available < 0)
+ return -1;
+
+ if ((total >= 0) && (available > total))
+ return -1;
+
+ const long long end = (total >= 0) ? total : available;
+ //TODO: this might need to be liberalized
+
+ //I would assume that in practice this loop would execute
+ //exactly once, but we allow for other elements (e.g. Void)
+ //to immediately follow the EBML header. This is fine for
+ //the source filter case (since the entire file is available),
+ //but in the splitter case over a network we should probably
+ //just give up early. We could for example decide only to
+ //execute this loop a maximum of, say, 10 times.
+ //TODO:
+ //There is an implied "give up early" by only parsing up
+ //to the available limit. We do do that, but only if the
+ //total file size is unknown. We could decide to always
+ //use what's available as our limit (irrespective of whether
+ //we happen to know the total file length). This would have
+ //as its sense "parse this much of the file before giving up",
+ //which a slightly different sense from "try to parse up to
+ //10 EMBL elements before giving up".
+
+ while (pos < end)
+ {
+ //Read ID
+
+ long len;
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result) //error, or too few available bytes
+ return result;
+
+ if ((pos + len) > end)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+
+ if (id < 0) //error
+ return id;
+
+ pos += len; //consume ID
+
+ //Read Size
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result) //error, or too few available bytes
+ return result;
+
+ if ((pos + len) > end)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) //error
+ return size;
+
+ pos += len; //consume length of size of element
+
+ //Pos now points to start of payload
+
+ //Handle "unknown size" for live streaming of webm files.
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (id == 0x08538067) //Segment ID
+ {
+ if (size == unknown_size)
+ size = -1;
+
+ else if (total < 0)
+ size = -1;
+
+ else if ((pos + size) > end)
+ return E_FILE_FORMAT_INVALID;
+
+ pSegment = new (std::nothrow) Segment(pReader, pos, size);
+
+ if (pSegment == 0)
+ return -1; //generic error
+
+ return 0; //success
+ }
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + size) > end)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += size; //consume payload
+ }
+
+ return E_FILE_FORMAT_INVALID; //there is no segment
+ //TODO: this might need to be liberalized. See comments above.
+}
+
+
+long long Segment::ParseHeaders()
+{
+ //Outermost (level 0) segment object has been constructed,
+ //and pos designates start of payload. We need to find the
+ //inner (level 1) elements.
+ long long total, available;
+
+ const int status = m_pReader->Length(&total, &available);
+ assert(status == 0);
+ assert((total < 0) || (available <= total));
+
+ const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+ assert((segment_stop < 0) || (total < 0) || (segment_stop <= total));
+ assert((segment_stop < 0) || (m_pos <= segment_stop));
+
+ for (;;)
+ {
+ if ((total >= 0) && (m_pos >= total))
+ break;
+
+ if ((segment_stop >= 0) && (m_pos >= segment_stop))
+ break;
+
+ long long pos = m_pos;
+ const long long element_start = pos;
+
+ if ((pos + 1) > available)
+ return (pos + 1);
+
+ long len;
+ long long result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return result;
+
+ if (result > 0) //underflow (weird)
+ return (pos + 1);
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ const long long idpos = pos;
+ const long long id = ReadUInt(m_pReader, idpos, len);
+
+ if (id < 0) //error
+ return id;
+
+ if (id == 0x0F43B675) //Cluster ID
+ break;
+
+ pos += len; //consume ID
+
+ if ((pos + 1) > available)
+ return (pos + 1);
+
+ //Read Size
+ result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return result;
+
+ if (result > 0) //underflow (weird)
+ return (pos + 1);
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+
+ if (size < 0) //error
+ return size;
+
+ pos += len; //consume length of size of element
+
+ const long long element_size = size + pos - element_start;
+
+ //Pos now points to start of payload
+
+ if ((segment_stop >= 0) && ((pos + size) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ //We read EBML elements either in total or nothing at all.
+
+ if ((pos + size) > available)
+ return pos + size;
+
+ if (id == 0x0549A966) //Segment Info ID
+ {
+ assert(m_pInfo == NULL);
+
+ m_pInfo = new SegmentInfo(this,
+ pos,
+ size,
+ element_start,
+ element_size);
+ assert(m_pInfo); //TODO
+ }
+ else if (id == 0x0654AE6B) //Tracks ID
+ {
+ assert(m_pTracks == NULL);
+
+ m_pTracks = new Tracks(this,
+ pos,
+ size,
+ element_start,
+ element_size);
+ assert(m_pTracks); //TODO
+ }
+ else if (id == 0x0C53BB6B) //Cues ID
+ {
+ if (m_pCues == NULL)
+ {
+ m_pCues = new Cues(this,
+ pos,
+ size,
+ element_start,
+ element_size);
+ assert(m_pCues); //TODO
+ }
+ }
+ else if (id == 0x014D9B74) //SeekHead ID
+ {
+#if 0
+ if (available >= total)
+ ParseSeekHead(pos, size);
+#else
+ if (m_pSeekHead == NULL)
+ {
+ m_pSeekHead = new SeekHead(this,
+ pos,
+ size,
+ element_start,
+ element_size);
+
+ assert(m_pSeekHead); //TODO
+ }
+#endif
+ }
+
+ m_pos = pos + size; //consume payload
+ }
+
+ assert((segment_stop < 0) || (m_pos <= segment_stop));
+
+ if (m_pInfo == NULL) //TODO: liberalize this behavior
+ return E_FILE_FORMAT_INVALID;
+
+ if (m_pTracks == NULL)
+ return E_FILE_FORMAT_INVALID;
+
+ return 0; //success
+}
+
+
+#if 0
+long Segment::FindNextCluster(long long& pos, size& len) const
+{
+ //Outermost (level 0) segment object has been constructed,
+ //and pos designates start of payload. We need to find the
+ //inner (level 1) elements.
+ long long total, available;
+
+ const int status = m_pReader->Length(&total, &available);
+ assert(status == 0);
+ assert(total >= 0);
+ assert(available <= total);
+
+ const long long stop = m_start + m_size;
+ assert(stop <= total);
+ assert(m_pos <= stop);
+
+ pos = m_pos;
+
+ while (pos < stop)
+ {
+ long long result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0)
+ return static_cast<long>(result);
+
+ if (result > 0)
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return E_BUFFER_NOT_FULL;
+
+ const long long idpos = pos;
+ const long long id = ReadUInt(m_pReader, idpos, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ pos += len; //consume ID
+
+ //Read Size
+ result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0)
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ pos += len; //consume length of size of element
+
+ //Pos now points to start of payload
+
+ if ((pos + size) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + size) > available)
+ return E_BUFFER_NOT_FULL;
+
+ if (id == 0x0F43B675) //Cluster ID
+ {
+ len = static_cast<long>(size);
+ return 0; //success
+ }
+
+ pos += size; //consume payload
+ }
+
+ return E_FILE_FORMAT_INVALID;
+}
+#endif
+
+
+#if 0
+long Segment::ParseCluster(long long& off, long long& new_pos) const
+{
+ off = -1;
+ new_pos = -1;
+
+ const long long stop = m_start + m_size;
+ assert(m_pos <= stop);
+
+ long long pos = m_pos;
+
+ while (pos < stop)
+ {
+ long len;
+ const long long idpos = pos;
+
+ const long long id = SyncReadUInt(m_pReader, pos, stop, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ if (id == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume id
+ assert(pos < stop);
+
+ const long long size = SyncReadUInt(m_pReader, pos, stop, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ pos += len; //consume size
+ assert(pos <= stop);
+
+ if (size == 0) //weird
+ continue;
+
+ //pos now points to start of payload
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+
+ if (id == 0x0F43B675) //Cluster ID
+ {
+ const long long off_ = idpos - m_start;
+
+ if (Cluster::HasBlockEntries(this, off_))
+ {
+ off = off_; // >= 0 means we found a cluster
+ break;
+ }
+ }
+ }
+
+ assert(pos <= stop);
+
+ //Indicate to caller how much of file has been consumed. This is
+ //used later in AddCluster to adjust the current parse position
+ //(the value cached in the segment object itself) to the
+ //file position value just past the cluster we parsed.
+
+ if (off < 0) //we did not found any more clusters
+ {
+ new_pos = stop; //pos >= 0 here means EOF (cluster is NULL)
+ return 0; //TODO: confirm this return value
+ }
+
+ //We found a cluster. Now read something, to ensure that it is
+ //fully loaded in the network cache.
+
+ if (pos >= stop) //we parsed the entire segment
+ {
+ //We did find a cluster, but it was very last element in the segment.
+ //Our preference is that the loop above runs 1 1/2 times:
+ //the first pass finds the cluster, and the second pass
+ //finds the element the follows the cluster. In this case, however,
+ //we reached the end of the file without finding another element,
+ //so we didn't actually read anything yet associated with "end of the
+ //cluster". And we must perform an actual read, in order
+ //to guarantee that all of the data that belongs to this
+ //cluster has been loaded into the network cache. So instead
+ //of reading the next element that follows the cluster, we
+ //read the last byte of the cluster (which is also the last
+ //byte in the file).
+
+ //Read the last byte of the file. (Reading 0 bytes at pos
+ //might work too -- it would depend on how the reader is
+ //implemented. Here we take the more conservative approach,
+ //since this makes fewer assumptions about the network
+ //reader abstraction.)
+
+ unsigned char b;
+
+ const int result = m_pReader->Read(pos - 1, 1, &b);
+ assert(result == 0);
+
+ new_pos = stop;
+ }
+ else
+ {
+ long len;
+ const long long idpos = pos;
+
+ const long long id = SyncReadUInt(m_pReader, pos, stop, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ if (id == 0)
+ return E_BUFFER_NOT_FULL;
+
+ pos += len; //consume id
+ assert(pos < stop);
+
+ const long long size = SyncReadUInt(m_pReader, pos, stop, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ new_pos = idpos;
+ }
+
+ return 0;
+}
+
+
+bool Segment::AddCluster(long long off, long long pos)
+{
+ assert(pos >= m_start);
+
+ const long long stop = m_start + m_size;
+ assert(pos <= stop);
+
+ if (off >= 0)
+ {
+ Cluster* const pCluster = Cluster::Parse(this,
+ m_clusterCount,
+ off,
+ 0,
+ 0);
+ assert(pCluster);
+
+ AppendCluster(pCluster);
+ assert(m_clusters);
+ assert(m_clusterSize > pCluster->m_index);
+ assert(m_clusters[pCluster->m_index] == pCluster);
+ }
+
+ m_pos = pos; //m_pos >= stop is now we know we have all clusters
+ return (pos >= stop);
+}
+#endif
+
+
+long Segment::LoadCluster(
+ long long& pos,
+ long& len)
+{
+ long long total, avail;
+
+ long status = m_pReader->Length(&total, &avail);
+
+ if (status < 0) //error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+ for (;;)
+ {
+ if ((total >= 0) && (m_pos >= total))
+ return 1; //no more clusters
+
+ if ((segment_stop >= 0) && (m_pos >= segment_stop))
+ return 1; //no more clusters
+
+ pos = m_pos;
+
+ //Read ID
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long idpos = pos;
+ const long long id = ReadUInt(m_pReader, idpos, len);
+
+ if (id < 0) //error (or underflow)
+ return static_cast<long>(id);
+
+ pos += len; //consume ID
+
+ //Read Size
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID; //TODO: allow this
+
+ pos += len; //consume length of size of element
+
+ const long long element_size = size + pos - idpos;
+
+ if (size == 0) //weird
+ {
+ m_pos = pos;
+ continue;
+ }
+
+ //Pos now points to start of payload
+
+ if ((segment_stop >= 0) && ((pos + size) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+#if 0
+ len = static_cast<long>(size);
+
+ if ((pos + size) > avail)
+ return E_BUFFER_NOT_FULL;
+#endif
+
+ if (id == 0x0C53BB6B) //Cues ID
+ {
+ if (m_pCues == NULL)
+ {
+ m_pCues = new Cues(this,
+ pos,
+ size,
+ idpos,
+ element_size);
+ assert(m_pCues); //TODO
+ }
+
+ m_pos = pos + size; //consume payload
+ continue;
+ }
+
+ if (id != 0x0F43B675) //Cluster ID
+ {
+ m_pos = pos + size; //consume payload
+ continue;
+ }
+
+ const long idx = m_clusterCount;
+ const long long idoff = idpos - m_start;
+
+ long long pos_;
+ long len_;
+
+ status = Cluster::HasBlockEntries(this, idoff, pos_, len_);
+
+ if (status < 0) //error, or underflow
+ {
+ pos = pos_;
+ len = len_;
+
+ return status;
+ }
+
+ if (m_clusterPreloadCount > 0)
+ {
+ assert(idx < m_clusterSize);
+
+ Cluster* const pCluster = m_clusters[idx];
+ assert(pCluster);
+ assert(pCluster->m_index < 0);
+
+ //const long long off_ = pCluster->m_pos;
+ //assert(off_);
+ //const long long off = off_ * ((off_ >= 0) ? 1 : -1);
+ //assert(idoff <= off);
+
+ const long long off = pCluster->GetPosition();
+ assert(off >= 0);
+
+ if (idoff == off) //preloaded already
+ {
+ if (status == 0) //no block entries
+ return E_FILE_FORMAT_INVALID;
+
+ pCluster->m_index = idx; //move from preloaded to loaded
+ ++m_clusterCount;
+ --m_clusterPreloadCount;
+
+ m_pos = pos + size; //consume payload
+ assert((segment_stop < 0) || (m_pos <= segment_stop));
+#if 0
+ status = pCluster->Load(pos, len); //set size and timecode
+ assert(status == 0); //TODO
+#endif
+ return 0; //success
+ }
+ }
+
+ m_pos = pos + size; //consume payload
+ assert((segment_stop < 0) || (m_pos <= segment_stop));
+
+ if (status == 0) //no block entries
+ continue;
+
+ Cluster* const pCluster = Cluster::Create(this,
+ idx,
+ idoff,
+ element_size);
+ assert(pCluster);
+
+ AppendCluster(pCluster);
+ assert(m_clusters);
+ assert(idx < m_clusterSize);
+ assert(m_clusters[idx] == pCluster);
+#if 0
+ status = pCluster->Load(pos, len);
+ assert(status == 0); //TODO
+#endif
+ return 0;
+ }
+}
+
+
+void Segment::AppendCluster(Cluster* pCluster)
+{
+ assert(pCluster);
+ assert(pCluster->m_index >= 0);
+
+ const long count = m_clusterCount + m_clusterPreloadCount;
+
+ long& size = m_clusterSize;
+ assert(size >= count);
+
+ const long idx = pCluster->m_index;
+ assert(idx == m_clusterCount);
+
+ if (count >= size)
+ {
+ long n;
+
+ if (size > 0)
+ n = 2 * size;
+ else if (m_pInfo == 0)
+ n = 2048;
+ else
+ {
+ const long long ns = m_pInfo->GetDuration();
+
+ if (ns <= 0)
+ n = 2048;
+ else
+ {
+ const long long sec = (ns + 999999999LL) / 1000000000LL;
+ n = static_cast<long>(sec);
+ }
+ }
+
+ Cluster** const qq = new Cluster*[n];
+ Cluster** q = qq;
+
+ Cluster** p = m_clusters;
+ Cluster** const pp = p + count;
+
+ while (p != pp)
+ *q++ = *p++;
+
+ delete[] m_clusters;
+
+ m_clusters = qq;
+ size = n;
+ }
+
+ if (m_clusterPreloadCount > 0)
+ {
+ assert(m_clusters);
+
+ Cluster** const p = m_clusters + m_clusterCount;
+ assert(*p);
+ assert((*p)->m_index < 0);
+
+ Cluster** q = p + m_clusterPreloadCount;
+ assert(q < (m_clusters + size));
+
+ for (;;)
+ {
+ Cluster** const qq = q - 1;
+ assert((*qq)->m_index < 0);
+
+ *q = *qq;
+ q = qq;
+
+ if (q == p)
+ break;
+ }
+ }
+
+ m_clusters[idx] = pCluster;
+ ++m_clusterCount;
+}
+
+
+void Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx)
+{
+ assert(pCluster);
+ assert(pCluster->m_index < 0);
+ assert(idx >= m_clusterCount);
+
+ const long count = m_clusterCount + m_clusterPreloadCount;
+
+ long& size = m_clusterSize;
+ assert(size >= count);
+
+ if (count >= size)
+ {
+ long n;
+
+ if (size > 0)
+ n = 2 * size;
+ else if (m_pInfo == 0)
+ n = 2048;
+ else
+ {
+ const long long ns = m_pInfo->GetDuration();
+
+ if (ns <= 0)
+ n = 2048;
+ else
+ {
+ const long long sec = (ns + 999999999LL) / 1000000000LL;
+ n = static_cast<long>(sec);
+ }
+ }
+
+ Cluster** const qq = new Cluster*[n];
+ Cluster** q = qq;
+
+ Cluster** p = m_clusters;
+ Cluster** const pp = p + count;
+
+ while (p != pp)
+ *q++ = *p++;
+
+ delete[] m_clusters;
+
+ m_clusters = qq;
+ size = n;
+ }
+
+ assert(m_clusters);
+
+ Cluster** const p = m_clusters + idx;
+
+ Cluster** q = m_clusters + count;
+ assert(q >= p);
+ assert(q < (m_clusters + size));
+
+ while (q > p)
+ {
+ Cluster** const qq = q - 1;
+ assert((*qq)->m_index < 0);
+
+ *q = *qq;
+ q = qq;
+ }
+
+ m_clusters[idx] = pCluster;
+ ++m_clusterPreloadCount;
+}
+
+
+long Segment::Load()
+{
+ assert(m_clusters == NULL);
+ assert(m_clusterSize == 0);
+ assert(m_clusterCount == 0);
+ //assert(m_size >= 0);
+
+ //Outermost (level 0) segment object has been constructed,
+ //and pos designates start of payload. We need to find the
+ //inner (level 1) elements.
+
+ long long total, avail;
+
+ long status = m_pReader->Length(&total, &avail);
+
+ if (status < 0) //error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+ for (;;)
+ {
+ long long pos = m_pos;
+
+ if ((total >= 0) && (pos >= total))
+ break;
+
+ if ((segment_stop >= 0) && (pos >= segment_stop))
+ break;
+
+ const long long element_start = pos;
+
+ long len;
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ const long long idpos = pos;
+ const long long id = ReadUInt(m_pReader, idpos, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ pos += len; //consume ID
+
+ //Read Size
+ result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume length of size of element
+
+ //Pos now points to start of payload
+
+ const long long element_size = (pos - element_start) + size;
+
+ if ((segment_stop >= 0) && ((pos + size) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == 0x0F43B675) //Cluster ID
+ {
+ const long idx = m_clusterCount;
+ const long long off = idpos - m_start;
+
+ long long pos_;
+ long len_;
+
+ status = Cluster::HasBlockEntries(this, off, pos_, len_);
+
+ if (status < 0) //weird: error or underflow
+ return status;
+
+ if (status > 0) //have block entries
+ {
+ Cluster* const pCluster = Cluster::Create(this,
+ idx,
+ off,
+ element_size);
+ assert(pCluster);
+
+ AppendCluster(pCluster);
+ assert(m_clusters);
+ assert(m_clusterSize > idx);
+ assert(m_clusters[idx] == pCluster);
+ }
+ }
+ else if (id == 0x0C53BB6B) //Cues ID
+ {
+ assert(m_pCues == NULL);
+
+ m_pCues = new Cues(this, pos, size, element_start, element_size);
+ assert(m_pCues); //TODO
+ }
+ else if (id == 0x0549A966) //SegmentInfo ID
+ {
+ assert(m_pInfo == NULL);
+
+ m_pInfo = new SegmentInfo(this,
+ pos,
+ size,
+ element_start,
+ element_size);
+ assert(m_pInfo);
+ }
+ else if (id == 0x0654AE6B) //Tracks ID
+ {
+ assert(m_pTracks == NULL);
+
+ m_pTracks = new Tracks(this,
+ pos,
+ size,
+ element_start,
+ element_size);
+ assert(m_pTracks); //TODO
+ }
+
+ m_pos = pos + size; //consume payload
+ }
+
+ if (m_pInfo == NULL)
+ return E_FILE_FORMAT_INVALID; //TODO: ignore this case?
+
+ if (m_pTracks == NULL)
+ return E_FILE_FORMAT_INVALID;
+
+ if (m_clusters == NULL) //TODO: ignore this case?
+ return E_FILE_FORMAT_INVALID;
+
+ return 0;
+}
+
+
+#if 0
+void Segment::ParseSeekHead(long long start, long long size_)
+{
+ long long pos = start;
+ const long long stop = start + size_;
+
+ while (pos < stop)
+ {
+ long len;
+
+ const long long id = ReadUInt(m_pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+ assert(size >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume Size field
+ assert((pos + size) <= stop);
+
+ if (id == 0x0DBB) //SeekEntry ID
+ ParseSeekEntry(pos, size);
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(pos == stop);
+}
+#else
+SeekHead::SeekHead(
+ Segment* pSegment,
+ long long start,
+ long long size_,
+ long long element_start,
+ long long element_size) :
+ m_pSegment(pSegment),
+ m_start(start),
+ m_size(size_),
+ m_element_start(element_start),
+ m_element_size(element_size),
+ m_entries(0),
+ m_count(0)
+{
+ long long pos = start;
+ const long long stop = start + size_;
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ //first count the seek head entries
+
+ int count = 0;
+
+ while (pos < stop)
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume Size field
+ assert((pos + size) <= stop);
+
+ if (id == 0x0DBB) //SeekEntry ID
+ ++count;
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(pos == stop);
+
+ if (count <= 0)
+ return; //nothing else for us to do
+
+ m_entries = new (std::nothrow) Entry[count];
+ assert(m_entries); //TODO
+
+ //now parse the entries
+
+ Entry* pEntry = m_entries;
+ pos = start;
+
+ while (pos < stop)
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume Size field
+ assert((pos + size) <= stop);
+
+ if (id == 0x0DBB) //SeekEntry ID
+ ParseEntry(pReader, pos, size, pEntry);
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(pos == stop);
+
+ const ptrdiff_t count_ = ptrdiff_t(pEntry - m_entries);
+ assert(count_ >= 0);
+ assert(count_ <= count);
+
+ m_count = static_cast<int>(count_);
+}
+
+SeekHead::~SeekHead()
+{
+ delete[] m_entries;
+}
+
+int SeekHead::GetCount() const
+{
+ return m_count;
+}
+
+const SeekHead::Entry* SeekHead::GetEntry(int idx) const
+{
+ if (idx < 0)
+ return 0;
+
+ if (idx >= m_count)
+ return 0;
+
+ return m_entries + idx;
+}
+#endif
+
+
+#if 0
+void Segment::ParseCues(long long off)
+{
+ if (m_pCues)
+ return;
+
+ //odbgstream os;
+ //os << "Segment::ParseCues (begin)" << endl;
+
+ long long pos = m_start + off;
+ const long long element_start = pos;
+ const long long stop = m_start + m_size;
+
+ long len;
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0);
+ assert((pos + len) <= stop);
+
+ const long long idpos = pos;
+
+ const long long id = ReadUInt(m_pReader, idpos, len);
+ assert(id == 0x0C53BB6B); //Cues ID
+
+ pos += len; //consume ID
+ assert(pos < stop);
+
+ //Read Size
+
+ result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0);
+ assert((pos + len) <= stop);
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+ assert(size >= 0);
+
+ pos += len; //consume length of size of element
+ assert((pos + size) <= stop);
+
+ const long long element_size = size + pos - element_start;
+
+ //Pos now points to start of payload
+
+ m_pCues = new Cues(this, pos, size, element_start, element_size);
+ assert(m_pCues); //TODO
+
+ //os << "Segment::ParseCues (end)" << endl;
+}
+#else
+long Segment::ParseCues(
+ long long off,
+ long long& pos,
+ long& len)
+{
+ if (m_pCues)
+ return 0; //success
+
+ if (off < 0)
+ return -1;
+
+ long long total, avail;
+
+ const int status = m_pReader->Length(&total, &avail);
+
+ if (status < 0) //error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ pos = m_start + off;
+
+ const long long element_start = pos;
+ const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //underflow (weird)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long idpos = pos;
+
+ const long long id = ReadUInt(m_pReader, idpos, len);
+
+ if (id != 0x0C53BB6B) //Cues ID
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume ID
+ assert((segment_stop < 0) || (pos <= segment_stop));
+
+ //Read Size
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //underflow (weird)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ if (size == 0) //weird, although technically not illegal
+ return 1; //done
+
+ pos += len; //consume length of size of element
+ assert((segment_stop < 0) || (pos <= segment_stop));
+
+ //Pos now points to start of payload
+
+ const long long element_stop = pos + size;
+
+ if ((segment_stop >= 0) && (element_stop > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ len = static_cast<long>(size);
+
+ if (element_stop > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long element_size = element_stop - element_start;
+
+ m_pCues = new (std::nothrow) Cues(
+ this,
+ pos,
+ size,
+ element_start,
+ element_size);
+ assert(m_pCues); //TODO
+
+ return 0; //success
+}
+#endif
+
+
+#if 0
+void Segment::ParseSeekEntry(
+ long long start,
+ long long size_)
+{
+ long long pos = start;
+
+ const long long stop = start + size_;
+
+ long len;
+
+ const long long seekIdId = ReadUInt(m_pReader, pos, len);
+ //seekIdId;
+ assert(seekIdId == 0x13AB); //SeekID ID
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long seekIdSize = ReadUInt(m_pReader, pos, len);
+ assert(seekIdSize >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume size
+
+ const long long seekId = ReadUInt(m_pReader, pos, len); //payload
+ assert(seekId >= 0);
+ assert(len == seekIdSize);
+ assert((pos + len) <= stop);
+
+ pos += seekIdSize; //consume payload
+
+ const long long seekPosId = ReadUInt(m_pReader, pos, len);
+ //seekPosId;
+ assert(seekPosId == 0x13AC); //SeekPos ID
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long seekPosSize = ReadUInt(m_pReader, pos, len);
+ assert(seekPosSize >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume size
+ assert((pos + seekPosSize) <= stop);
+
+ const long long seekOff = UnserializeUInt(m_pReader, pos, seekPosSize);
+ assert(seekOff >= 0);
+ assert(seekOff < m_size);
+
+ pos += seekPosSize; //consume payload
+ assert(pos == stop);
+
+ const long long seekPos = m_start + seekOff;
+ assert(seekPos < (m_start + m_size));
+
+ if (seekId == 0x0C53BB6B) //Cues ID
+ ParseCues(seekOff);
+}
+#else
+void SeekHead::ParseEntry(
+ IMkvReader* pReader,
+ long long start,
+ long long size_,
+ Entry*& pEntry)
+{
+ long long pos = start;
+ const long long stop = start + size_;
+
+ long len;
+
+ //parse the container for the level-1 element ID
+
+ const long long seekIdId = ReadUInt(pReader, pos, len);
+ //seekIdId;
+
+ if (seekIdId != 0x13AB) //SeekID ID
+ return;
+
+ if ((pos + len) > stop)
+ return;
+
+ pos += len; //consume SeekID id
+
+ const long long seekIdSize = ReadUInt(pReader, pos, len);
+
+ if (seekIdSize <= 0)
+ return;
+
+ if ((pos + len) > stop)
+ return;
+
+ pos += len; //consume size of field
+
+ if ((pos + seekIdSize) > stop)
+ return;
+
+ //TODO: it's not clear whether this is correct
+ //It seems as if the payload here is "binary" which
+ //means the value of the ID should be unserialized,
+ //not parsed as an uint.
+ //
+ pEntry->id = ReadUInt(pReader, pos, len); //payload
+
+ if (pEntry->id <= 0)
+ return;
+
+ if (len != seekIdSize)
+ return;
+
+ pos += seekIdSize; //consume SeekID payload
+
+ const long long seekPosId = ReadUInt(pReader, pos, len);
+
+ if (seekPosId != 0x13AC) //SeekPos ID
+ return;
+
+ if ((pos + len) > stop)
+ return;
+
+ pos += len; //consume id
+
+ const long long seekPosSize = ReadUInt(pReader, pos, len);
+
+ if (seekPosSize <= 0)
+ return;
+
+ if ((pos + len) > stop)
+ return;
+
+ pos += len; //consume size
+
+ if ((pos + seekPosSize) > stop)
+ return;
+
+ pEntry->pos = UnserializeUInt(pReader, pos, seekPosSize);
+
+ if (pEntry->pos < 0)
+ return;
+
+ pos += seekPosSize; //consume payload
+
+ if (pos != stop)
+ return;
+
+ ++pEntry; //success
+}
+#endif
+
+
+Cues::Cues(
+ Segment* pSegment,
+ long long start_,
+ long long size_,
+ long long element_start,
+ long long element_size) :
+ m_pSegment(pSegment),
+ m_start(start_),
+ m_size(size_),
+ m_cue_points(NULL),
+ m_count(0),
+ m_preload_count(0),
+ m_pos(start_),
+ m_element_start(element_start),
+ m_element_size(element_size)
+{
+}
+
+
+Cues::~Cues()
+{
+ const size_t n = m_count + m_preload_count;
+
+ CuePoint** p = m_cue_points;
+ CuePoint** const q = p + n;
+
+ while (p != q)
+ {
+ CuePoint* const pCP = *p++;
+ assert(pCP);
+
+ delete pCP;
+ }
+
+ delete[] m_cue_points;
+}
+
+
+long Cues::GetCount() const
+{
+ if (m_cue_points == NULL)
+ return -1;
+
+ return m_count; //TODO: really ignore preload count?
+}
+
+
+bool Cues::DoneParsing() const
+{
+ const long long stop = m_start + m_size;
+ return (m_pos >= stop);
+}
+
+
+void Cues::Init() const
+{
+ if (m_cue_points)
+ return;
+
+ assert(m_count == 0);
+ assert(m_preload_count == 0);
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ const long long stop = m_start + m_size;
+ long long pos = m_start;
+
+ size_t cue_points_size = 0;
+
+ while (pos < stop)
+ {
+ const long long idpos = pos;
+
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume Size field
+ assert((pos + size) <= stop);
+
+ if (id == 0x3B) //CuePoint ID
+ PreloadCuePoint(cue_points_size, idpos);
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+}
+
+
+void Cues::PreloadCuePoint(
+ size_t& cue_points_size,
+ long long pos) const
+{
+ assert(m_count == 0);
+
+ if (m_preload_count >= cue_points_size)
+ {
+ size_t n;
+
+ if (cue_points_size > 0)
+ n = static_cast<size_t>(2 * cue_points_size);
+ else
+ {
+ const SegmentInfo* const pInfo = m_pSegment->GetInfo();
+
+ if (pInfo == NULL)
+ n = 2048;
+ else
+ {
+ const long long ns = pInfo->GetDuration();
+
+ if (ns <= 0)
+ n = 2048;
+ else
+ {
+ const long long sec = (ns + 999999999LL) / 1000000000LL;
+ n = static_cast<size_t>(sec);
+ }
+ }
+ }
+
+ CuePoint** const qq = new CuePoint*[n];
+ CuePoint** q = qq; //beginning of target
+
+ CuePoint** p = m_cue_points; //beginning of source
+ CuePoint** const pp = p + m_preload_count; //end of source
+
+ while (p != pp)
+ *q++ = *p++;
+
+ delete[] m_cue_points;
+
+ m_cue_points = qq;
+ cue_points_size = n;
+ }
+
+ CuePoint* const pCP = new CuePoint(m_preload_count, pos);
+ m_cue_points[m_preload_count++] = pCP;
+}
+
+
+bool Cues::LoadCuePoint() const
+{
+ //odbgstream os;
+ //os << "Cues::LoadCuePoint" << endl;
+
+ const long long stop = m_start + m_size;
+
+ if (m_pos >= stop)
+ return false; //nothing else to do
+
+ Init();
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ while (m_pos < stop)
+ {
+ const long long idpos = m_pos;
+
+ long len;
+
+ const long long id = ReadUInt(pReader, m_pos, len);
+ assert(id >= 0); //TODO
+ assert((m_pos + len) <= stop);
+
+ m_pos += len; //consume ID
+
+ const long long size = ReadUInt(pReader, m_pos, len);
+ assert(size >= 0);
+ assert((m_pos + len) <= stop);
+
+ m_pos += len; //consume Size field
+ assert((m_pos + size) <= stop);
+
+ if (id != 0x3B) //CuePoint ID
+ {
+ m_pos += size; //consume payload
+ assert(m_pos <= stop);
+
+ continue;
+ }
+
+ assert(m_preload_count > 0);
+
+ CuePoint* const pCP = m_cue_points[m_count];
+ assert(pCP);
+ assert((pCP->GetTimeCode() >= 0) || (-pCP->GetTimeCode() == idpos));
+
+ pCP->Load(pReader);
+ ++m_count;
+ --m_preload_count;
+
+ m_pos += size; //consume payload
+ assert(m_pos <= stop);
+
+ break;
+ }
+
+ return (m_pos < stop);
+}
+
+
+bool Cues::Find(
+ long long time_ns,
+ const Track* pTrack,
+ const CuePoint*& pCP,
+ const CuePoint::TrackPosition*& pTP) const
+{
+ assert(time_ns >= 0);
+ assert(pTrack);
+
+#if 0
+ LoadCuePoint(); //establish invariant
+
+ assert(m_cue_points);
+ assert(m_count > 0);
+
+ CuePoint** const ii = m_cue_points;
+ CuePoint** i = ii;
+
+ CuePoint** const jj = ii + m_count + m_preload_count;
+ CuePoint** j = jj;
+
+ pCP = *i;
+ assert(pCP);
+
+ if (time_ns <= pCP->GetTime(m_pSegment))
+ {
+ pTP = pCP->Find(pTrack);
+ return (pTP != NULL);
+ }
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ while (i < j)
+ {
+ //INVARIANT:
+ //[ii, i) <= time_ns
+ //[i, j) ?
+ //[j, jj) > time_ns
+
+ CuePoint** const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ CuePoint* const pCP = *k;
+ assert(pCP);
+
+ pCP->Load(pReader);
+
+ const long long t = pCP->GetTime(m_pSegment);
+
+ if (t <= time_ns)
+ i = k + 1;
+ else
+ j = k;
+
+ assert(i <= j);
+ }
+
+ assert(i == j);
+ assert(i <= jj);
+ assert(i > ii);
+
+ pCP = *--i;
+ assert(pCP);
+ assert(pCP->GetTime(m_pSegment) <= time_ns);
+#else
+ if (m_cue_points == NULL)
+ return false;
+
+ if (m_count == 0)
+ return false;
+
+ CuePoint** const ii = m_cue_points;
+ CuePoint** i = ii;
+
+ CuePoint** const jj = ii + m_count;
+ CuePoint** j = jj;
+
+ pCP = *i;
+ assert(pCP);
+
+ if (time_ns <= pCP->GetTime(m_pSegment))
+ {
+ pTP = pCP->Find(pTrack);
+ return (pTP != NULL);
+ }
+
+ while (i < j)
+ {
+ //INVARIANT:
+ //[ii, i) <= time_ns
+ //[i, j) ?
+ //[j, jj) > time_ns
+
+ CuePoint** const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ CuePoint* const pCP = *k;
+ assert(pCP);
+
+ const long long t = pCP->GetTime(m_pSegment);
+
+ if (t <= time_ns)
+ i = k + 1;
+ else
+ j = k;
+
+ assert(i <= j);
+ }
+
+ assert(i == j);
+ assert(i <= jj);
+ assert(i > ii);
+
+ pCP = *--i;
+ assert(pCP);
+ assert(pCP->GetTime(m_pSegment) <= time_ns);
+#endif
+
+ //TODO: here and elsewhere, it's probably not correct to search
+ //for the cue point with this time, and then search for a matching
+ //track. In principle, the matching track could be on some earlier
+ //cue point, and with our current algorithm, we'd miss it. To make
+ //this bullet-proof, we'd need to create a secondary structure,
+ //with a list of cue points that apply to a track, and then search
+ //that track-based structure for a matching cue point.
+
+ pTP = pCP->Find(pTrack);
+ return (pTP != NULL);
+}
+
+
+#if 0
+bool Cues::FindNext(
+ long long time_ns,
+ const Track* pTrack,
+ const CuePoint*& pCP,
+ const CuePoint::TrackPosition*& pTP) const
+{
+ pCP = 0;
+ pTP = 0;
+
+ if (m_count == 0)
+ return false;
+
+ assert(m_cue_points);
+
+ const CuePoint* const* const ii = m_cue_points;
+ const CuePoint* const* i = ii;
+
+ const CuePoint* const* const jj = ii + m_count;
+ const CuePoint* const* j = jj;
+
+ while (i < j)
+ {
+ //INVARIANT:
+ //[ii, i) <= time_ns
+ //[i, j) ?
+ //[j, jj) > time_ns
+
+ const CuePoint* const* const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ pCP = *k;
+ assert(pCP);
+
+ const long long t = pCP->GetTime(m_pSegment);
+
+ if (t <= time_ns)
+ i = k + 1;
+ else
+ j = k;
+
+ assert(i <= j);
+ }
+
+ assert(i == j);
+ assert(i <= jj);
+
+ if (i >= jj) //time_ns is greater than max cue point
+ return false;
+
+ pCP = *i;
+ assert(pCP);
+ assert(pCP->GetTime(m_pSegment) > time_ns);
+
+ pTP = pCP->Find(pTrack);
+ return (pTP != NULL);
+}
+#endif
+
+
+const CuePoint* Cues::GetFirst() const
+{
+ if (m_cue_points == NULL)
+ return NULL;
+
+ if (m_count == 0)
+ return NULL;
+
+#if 0
+ LoadCuePoint(); //init cues
+
+ const size_t count = m_count + m_preload_count;
+
+ if (count == 0) //weird
+ return NULL;
+#endif
+
+ CuePoint* const* const pp = m_cue_points;
+ assert(pp);
+
+ CuePoint* const pCP = pp[0];
+ assert(pCP);
+ assert(pCP->GetTimeCode() >= 0);
+
+ return pCP;
+}
+
+
+const CuePoint* Cues::GetLast() const
+{
+ if (m_cue_points == NULL)
+ return NULL;
+
+ if (m_count == 0)
+ return NULL;
+
+#if 0
+ LoadCuePoint(); //init cues
+
+ const size_t count = m_count + m_preload_count;
+
+ if (count == 0) //weird
+ return NULL;
+
+ const size_t index = count - 1;
+
+ CuePoint* const* const pp = m_cue_points;
+ assert(pp);
+
+ CuePoint* const pCP = pp[index];
+ assert(pCP);
+
+ pCP->Load(m_pSegment->m_pReader);
+ assert(pCP->GetTimeCode() >= 0);
+#else
+ const size_t index = m_count - 1;
+
+ CuePoint* const* const pp = m_cue_points;
+ assert(pp);
+
+ CuePoint* const pCP = pp[index];
+ assert(pCP);
+ assert(pCP->GetTimeCode() >= 0);
+#endif
+
+ return pCP;
+}
+
+
+const CuePoint* Cues::GetNext(const CuePoint* pCurr) const
+{
+ if (pCurr == NULL)
+ return NULL;
+
+ assert(pCurr->GetTimeCode() >= 0);
+ assert(m_cue_points);
+ assert(m_count >= 1);
+
+#if 0
+ const size_t count = m_count + m_preload_count;
+
+ size_t index = pCurr->m_index;
+ assert(index < count);
+
+ CuePoint* const* const pp = m_cue_points;
+ assert(pp);
+ assert(pp[index] == pCurr);
+
+ ++index;
+
+ if (index >= count)
+ return NULL;
+
+ CuePoint* const pNext = pp[index];
+ assert(pNext);
+
+ pNext->Load(m_pSegment->m_pReader);
+#else
+ size_t index = pCurr->m_index;
+ assert(index < m_count);
+
+ CuePoint* const* const pp = m_cue_points;
+ assert(pp);
+ assert(pp[index] == pCurr);
+
+ ++index;
+
+ if (index >= m_count)
+ return NULL;
+
+ CuePoint* const pNext = pp[index];
+ assert(pNext);
+ assert(pNext->GetTimeCode() >= 0);
+#endif
+
+ return pNext;
+}
+
+
+const BlockEntry* Cues::GetBlock(
+ const CuePoint* pCP,
+ const CuePoint::TrackPosition* pTP) const
+{
+ if (pCP == NULL)
+ return NULL;
+
+ if (pTP == NULL)
+ return NULL;
+
+ return m_pSegment->GetBlock(*pCP, *pTP);
+}
+
+
+const BlockEntry* Segment::GetBlock(
+ const CuePoint& cp,
+ const CuePoint::TrackPosition& tp)
+{
+ Cluster** const ii = m_clusters;
+ Cluster** i = ii;
+
+ const long count = m_clusterCount + m_clusterPreloadCount;
+
+ Cluster** const jj = ii + count;
+ Cluster** j = jj;
+
+ while (i < j)
+ {
+ //INVARIANT:
+ //[ii, i) < pTP->m_pos
+ //[i, j) ?
+ //[j, jj) > pTP->m_pos
+
+ Cluster** const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ Cluster* const pCluster = *k;
+ assert(pCluster);
+
+ //const long long pos_ = pCluster->m_pos;
+ //assert(pos_);
+ //const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
+
+ const long long pos = pCluster->GetPosition();
+ assert(pos >= 0);
+
+ if (pos < tp.m_pos)
+ i = k + 1;
+ else if (pos > tp.m_pos)
+ j = k;
+ else
+ return pCluster->GetEntry(cp, tp);
+ }
+
+ assert(i == j);
+ //assert(Cluster::HasBlockEntries(this, tp.m_pos));
+
+ Cluster* const pCluster = Cluster::Create(this, -1, tp.m_pos, -1);
+ assert(pCluster);
+
+ const ptrdiff_t idx = i - m_clusters;
+
+ PreloadCluster(pCluster, idx);
+ assert(m_clusters);
+ assert(m_clusterPreloadCount > 0);
+ assert(m_clusters[idx] == pCluster);
+
+ return pCluster->GetEntry(cp, tp);
+}
+
+
+const Cluster* Segment::FindOrPreloadCluster(long long requested_pos)
+{
+ if (requested_pos < 0)
+ return 0;
+
+ Cluster** const ii = m_clusters;
+ Cluster** i = ii;
+
+ const long count = m_clusterCount + m_clusterPreloadCount;
+
+ Cluster** const jj = ii + count;
+ Cluster** j = jj;
+
+ while (i < j)
+ {
+ //INVARIANT:
+ //[ii, i) < pTP->m_pos
+ //[i, j) ?
+ //[j, jj) > pTP->m_pos
+
+ Cluster** const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ Cluster* const pCluster = *k;
+ assert(pCluster);
+
+ //const long long pos_ = pCluster->m_pos;
+ //assert(pos_);
+ //const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);
+
+ const long long pos = pCluster->GetPosition();
+ assert(pos >= 0);
+
+ if (pos < requested_pos)
+ i = k + 1;
+ else if (pos > requested_pos)
+ j = k;
+ else
+ return pCluster;
+ }
+
+ assert(i == j);
+ //assert(Cluster::HasBlockEntries(this, tp.m_pos));
+
+ Cluster* const pCluster = Cluster::Create(
+ this,
+ -1,
+ requested_pos,
+ -1);
+ assert(pCluster);
+
+ const ptrdiff_t idx = i - m_clusters;
+
+ PreloadCluster(pCluster, idx);
+ assert(m_clusters);
+ assert(m_clusterPreloadCount > 0);
+ assert(m_clusters[idx] == pCluster);
+
+ return pCluster;
+}
+
+
+CuePoint::CuePoint(size_t idx, long long pos) :
+ m_element_start(0),
+ m_element_size(0),
+ m_index(idx),
+ m_timecode(-1 * pos),
+ m_track_positions(NULL),
+ m_track_positions_count(0)
+{
+ assert(pos > 0);
+}
+
+
+CuePoint::~CuePoint()
+{
+ delete[] m_track_positions;
+}
+
+
+void CuePoint::Load(IMkvReader* pReader)
+{
+ //odbgstream os;
+ //os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;
+
+ if (m_timecode >= 0) //already loaded
+ return;
+
+ assert(m_track_positions == NULL);
+ assert(m_track_positions_count == 0);
+
+ long long pos_ = -m_timecode;
+ const long long element_start = pos_;
+
+ long long stop;
+
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos_, len);
+ assert(id == 0x3B); //CuePoint ID
+ //assert((pos + len) <= stop);
+
+ pos_ += len; //consume ID
+
+ const long long size = ReadUInt(pReader, pos_, len);
+ assert(size >= 0);
+ //assert((pos + len) <= stop);
+
+ pos_ += len; //consume Size field
+ //assert((pos + size) <= stop);
+
+ //pos_ now points to start of payload
+
+ stop = pos_ + size;
+ }
+
+ const long long element_size = stop - element_start;
+
+ long long pos = pos_;
+
+ //First count number of track positions
+
+ while (pos < stop)
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume Size field
+ assert((pos + size) <= stop);
+
+ if (id == 0x33) //CueTime ID
+ m_timecode = UnserializeUInt(pReader, pos, size);
+
+ else if (id == 0x37) //CueTrackPosition(s) ID
+ ++m_track_positions_count;
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(m_timecode >= 0);
+ assert(m_track_positions_count > 0);
+
+ //os << "CuePoint::Load(cont'd): idpos=" << idpos
+ // << " timecode=" << m_timecode
+ // << endl;
+
+ m_track_positions = new TrackPosition[m_track_positions_count];
+
+ //Now parse track positions
+
+ TrackPosition* p = m_track_positions;
+ pos = pos_;
+
+ while (pos < stop)
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume Size field
+ assert((pos + size) <= stop);
+
+ if (id == 0x37) //CueTrackPosition(s) ID
+ {
+ TrackPosition& tp = *p++;
+ tp.Parse(pReader, pos, size);
+ }
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(size_t(p - m_track_positions) == m_track_positions_count);
+
+ m_element_start = element_start;
+ m_element_size = element_size;
+}
+
+
+
+void CuePoint::TrackPosition::Parse(
+ IMkvReader* pReader,
+ long long start_,
+ long long size_)
+{
+ const long long stop = start_ + size_;
+ long long pos = start_;
+
+ m_track = -1;
+ m_pos = -1;
+ m_block = 1; //default
+
+ while (pos < stop)
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume Size field
+ assert((pos + size) <= stop);
+
+ if (id == 0x77) //CueTrack ID
+ m_track = UnserializeUInt(pReader, pos, size);
+
+ else if (id == 0x71) //CueClusterPos ID
+ m_pos = UnserializeUInt(pReader, pos, size);
+
+ else if (id == 0x1378) //CueBlockNumber
+ m_block = UnserializeUInt(pReader, pos, size);
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(m_pos >= 0);
+ assert(m_track > 0);
+ //assert(m_block > 0);
+}
+
+
+const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const
+{
+ assert(pTrack);
+
+ const long long n = pTrack->GetNumber();
+
+ const TrackPosition* i = m_track_positions;
+ const TrackPosition* const j = i + m_track_positions_count;
+
+ while (i != j)
+ {
+ const TrackPosition& p = *i++;
+
+ if (p.m_track == n)
+ return &p;
+ }
+
+ return NULL; //no matching track number found
+}
+
+
+long long CuePoint::GetTimeCode() const
+{
+ return m_timecode;
+}
+
+long long CuePoint::GetTime(const Segment* pSegment) const
+{
+ assert(pSegment);
+ assert(m_timecode >= 0);
+
+ const SegmentInfo* const pInfo = pSegment->GetInfo();
+ assert(pInfo);
+
+ const long long scale = pInfo->GetTimeCodeScale();
+ assert(scale >= 1);
+
+ const long long time = scale * m_timecode;
+
+ return time;
+}
+
+
+long long Segment::Unparsed() const
+{
+ if (m_size < 0)
+ return LLONG_MAX;
+
+ const long long stop = m_start + m_size;
+
+ const long long result = stop - m_pos;
+ assert(result >= 0);
+
+ return result;
+}
+
+
+const Cluster* Segment::GetFirst() const
+{
+ if ((m_clusters == NULL) || (m_clusterCount <= 0))
+ return &m_eos;
+
+ Cluster* const pCluster = m_clusters[0];
+ assert(pCluster);
+
+ return pCluster;
+}
+
+
+const Cluster* Segment::GetLast() const
+{
+ if ((m_clusters == NULL) || (m_clusterCount <= 0))
+ return &m_eos;
+
+ const long idx = m_clusterCount - 1;
+
+ Cluster* const pCluster = m_clusters[idx];
+ assert(pCluster);
+
+ return pCluster;
+}
+
+
+unsigned long Segment::GetCount() const
+{
+ return m_clusterCount;
+}
+
+
+const Cluster* Segment::GetNext(const Cluster* pCurr)
+{
+ assert(pCurr);
+ assert(pCurr != &m_eos);
+ assert(m_clusters);
+
+ long idx = pCurr->m_index;
+
+ if (idx >= 0)
+ {
+ assert(m_clusterCount > 0);
+ assert(idx < m_clusterCount);
+ assert(pCurr == m_clusters[idx]);
+
+ ++idx;
+
+ if (idx >= m_clusterCount)
+ return &m_eos; //caller will LoadCluster as desired
+
+ Cluster* const pNext = m_clusters[idx];
+ assert(pNext);
+ assert(pNext->m_index >= 0);
+ assert(pNext->m_index == idx);
+
+ return pNext;
+ }
+
+ assert(m_clusterPreloadCount > 0);
+
+ //const long long off_ = pCurr->m_pos;
+ //const long long off = off_ * ((off_ < 0) ? -1 : 1);
+ //long long pos = m_start + off;
+
+ long long pos = pCurr->m_element_start;
+
+ assert(m_size >= 0); //TODO
+ const long long stop = m_start + m_size; //end of segment
+
+ {
+ long len;
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0); //TODO
+ assert((pos + len) <= stop); //TODO
+
+ const long long id = ReadUInt(m_pReader, pos, len);
+ assert(id == 0x0F43B675); //Cluster ID //TODO
+
+ pos += len; //consume ID
+
+ //Read Size
+ result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0); //TODO
+ assert((pos + len) <= stop); //TODO
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+ assert(size > 0); //TODO
+ assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
+
+ pos += len; //consume length of size of element
+ assert((pos + size) <= stop); //TODO
+
+ //Pos now points to start of payload
+
+ pos += size; //consume payload
+ }
+
+ long long off_next = 0;
+ //long long element_start_next = 0;
+ long long element_size_next = 0;
+
+ while (pos < stop)
+ {
+ long len;
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0); //TODO
+ assert((pos + len) <= stop); //TODO
+
+ const long long idpos = pos; //pos of next (potential) cluster
+
+ const long long id = ReadUInt(m_pReader, idpos, len);
+ assert(id > 0); //TODO
+
+ pos += len; //consume ID
+
+ //Read Size
+ result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0); //TODO
+ assert((pos + len) <= stop); //TODO
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+ assert(size >= 0); //TODO
+
+ pos += len; //consume length of size of element
+ assert((pos + size) <= stop); //TODO
+
+ const long long element_size = size + pos - idpos;
+
+ //Pos now points to start of payload
+
+ if (size == 0) //weird
+ continue;
+
+ if (id == 0x0F43B675) //Cluster ID
+ {
+ const long long off_next_ = idpos - m_start;
+
+ long long pos_;
+ long len_;
+
+ const long status = Cluster::HasBlockEntries(
+ this,
+ off_next_,
+ pos_,
+ len_);
+
+ assert(status >= 0);
+
+ if (status > 0)
+ {
+ off_next = off_next_;
+ //element_start_next = idpos;
+ element_size_next = element_size;
+ break;
+ }
+ }
+
+ pos += size; //consume payload
+ }
+
+ if (off_next <= 0)
+ return 0;
+
+ Cluster** const ii = m_clusters + m_clusterCount;
+ Cluster** i = ii;
+
+ Cluster** const jj = ii + m_clusterPreloadCount;
+ Cluster** j = jj;
+
+ while (i < j)
+ {
+ //INVARIANT:
+ //[0, i) < pos_next
+ //[i, j) ?
+ //[j, jj) > pos_next
+
+ Cluster** const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ Cluster* const pNext = *k;
+ assert(pNext);
+ assert(pNext->m_index < 0);
+
+ //const long long pos_ = pNext->m_pos;
+ //assert(pos_);
+ //pos = pos_ * ((pos_ < 0) ? -1 : 1);
+
+ pos = pNext->GetPosition();
+
+ if (pos < off_next)
+ i = k + 1;
+ else if (pos > off_next)
+ j = k;
+ else
+ return pNext;
+ }
+
+ assert(i == j);
+
+ Cluster* const pNext = Cluster::Create(this,
+ -1,
+ off_next,
+ element_size_next);
+ assert(pNext);
+
+ const ptrdiff_t idx_next = i - m_clusters; //insertion position
+
+ PreloadCluster(pNext, idx_next);
+ assert(m_clusters);
+ assert(idx_next < m_clusterSize);
+ assert(m_clusters[idx_next] == pNext);
+
+ return pNext;
+}
+
+
+long Segment::ParseNext(
+ const Cluster* pCurr,
+ const Cluster*& pResult,
+ long long& pos,
+ long& len)
+{
+ assert(pCurr);
+ assert(!pCurr->EOS());
+ assert(m_clusters);
+
+ pResult = 0;
+
+ if (pCurr->m_index >= 0) //loaded (not merely preloaded)
+ {
+ assert(m_clusters[pCurr->m_index] == pCurr);
+
+ const long next_idx = pCurr->m_index + 1;
+
+ if (next_idx < m_clusterCount)
+ {
+ pResult = m_clusters[next_idx];
+ return 0; //success
+ }
+
+ //curr cluster is last among loaded
+
+ const long result = LoadCluster(pos, len);
+
+ if (result < 0) //error or underflow
+ return result;
+
+ if (result > 0) //no more clusters
+ {
+ //pResult = &m_eos;
+ return 1;
+ }
+
+ pResult = GetLast();
+ return 0; //success
+ }
+
+ long long total, avail;
+
+ long status = m_pReader->Length(&total, &avail);
+
+ if (status < 0) //error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ //const long long off_curr_ = pCurr->m_pos;
+ //const long long off_curr = off_curr_ * ((off_curr_ < 0) ? -1 : 1);
+
+ //pos = m_start + off_curr;
+
+ const long long segment_stop = (m_size < 0) ? -1 : m_start + m_size;
+
+ //interrogate curr cluster
+
+ pos = pCurr->m_element_start;
+
+ if (pCurr->m_size >= 0) //loaded (either partially or fully)
+ {
+ assert(pCurr->m_element_size > pCurr->m_size);
+ pos += pCurr->m_element_size;
+ }
+ else //weird: preloaded only
+ {
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id = ReadUInt(m_pReader, pos, len);
+
+ if (id != 0x0F43B675) //weird: not Cluster ID
+ return -1;
+
+ pos += len; //consume ID
+
+ //Read Size
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ if (size == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ assert((pCurr->m_size <= 0) || (pCurr->m_size == size));
+
+ pos += len; //consume length of size of element
+
+ if ((segment_stop >= 0) && ((pos + size) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ //Pos now points to start of payload
+
+ pos += size; //consume payload (that is, the current cluster)
+ assert((segment_stop < 0) || (pos <= segment_stop));
+
+ //By consuming the payload, we are assuming that the curr
+ //cluster isn't interesting. That is, we don't bother checking
+ //whether the payload of the curr cluster is less than what
+ //happens to be available (obtained via IMkvReader::Length).
+ //Presumably the caller has already dispensed with the current
+ //cluster, and really does want the next cluster.
+ }
+
+ //pos now points to just beyond the last fully-loaded cluster
+
+ //Parse next cluster. This is strictly a parsing activity.
+ //Creation of a new cluster object happens later, after the
+ //parsing is done.
+
+ long long off_next = 0;
+ long long element_start = -1;
+ long long element_size = -1;
+
+ for (;;)
+ {
+ if ((total >= 0) && (pos >= total))
+ break;
+
+ if ((segment_stop >= 0) && (pos >= segment_stop))
+ break;
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long idpos = pos; //absolute
+ const long long idoff = pos - m_start; //relative
+
+ const long long id = ReadUInt(m_pReader, idpos, len); //absolute
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ if (id == 0) //weird
+ return -1; //generic error
+
+ pos += len; //consume ID
+
+ //Read Size
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume length of size of element
+
+ //Pos now points to start of payload
+
+ if (size == 0) //weird
+ continue;
+
+ element_start = idpos;
+ const long long element_stop = pos + size;
+
+ if ((segment_stop >= 0) && (element_stop > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ element_size = element_stop - element_start;
+
+ if (id == 0x0C53BB6B) //Cues ID
+ {
+ if (m_pCues == NULL)
+ {
+ m_pCues = new Cues(this,
+ pos,
+ size,
+ element_start,
+ element_size);
+ assert(m_pCues); //TODO
+ }
+
+ pos += size; //consume payload
+ assert((segment_stop < 0) || (pos <= segment_stop));
+
+ continue;
+ }
+
+ if (id != 0x0F43B675) //Cluster ID
+ {
+ pos += size; //consume payload
+ assert((segment_stop < 0) || (pos <= segment_stop));
+
+ continue;
+ }
+
+#if 0
+ len = static_cast<long>(size);
+
+ if (element_stop > avail)
+ return E_BUFFER_NOT_FULL;
+#endif
+
+ long long pos_;
+ long len_;
+
+ status = Cluster::HasBlockEntries(this, idoff, pos_, len_);
+
+ if (status < 0) //error or underflow
+ {
+ pos = pos_;
+ len = len_;
+
+ return status;
+ }
+
+ if (status > 0) //have block entries
+ {
+ off_next = idoff;
+ break;
+ }
+
+ pos += size; //consume payload
+ assert((segment_stop < 0) || (pos <= segment_stop));
+ }
+
+ if (off_next <= 0) //no next cluster found
+ {
+ //pResult = &m_eos;
+ return 1;
+ }
+
+ //We have parsed the next cluster.
+ //We have not created a cluster object yet. What we need
+ //to do now is determine whether it has already be preloaded
+ //(in which case, an object for this cluster has already been
+ //created), and if not, create a new cluster object.
+
+ Cluster** const ii = m_clusters + m_clusterCount;
+ Cluster** i = ii;
+
+ Cluster** const jj = ii + m_clusterPreloadCount;
+ Cluster** j = jj;
+
+ while (i < j)
+ {
+ //INVARIANT:
+ //[0, i) < pos_next
+ //[i, j) ?
+ //[j, jj) > pos_next
+
+ Cluster** const k = i + (j - i) / 2;
+ assert(k < jj);
+
+ const Cluster* const pNext = *k;
+ assert(pNext);
+ assert(pNext->m_index < 0);
+
+ //const long long pos_ = pNext->m_pos;
+ //assert(pos_);
+ //pos = pos_ * ((pos_ < 0) ? -1 : 1);
+
+ pos = pNext->GetPosition();
+ assert(pos >= 0);
+
+ if (pos < off_next)
+ i = k + 1;
+ else if (pos > off_next)
+ j = k;
+ else
+ {
+#if 0
+ status = pNext->Load(pos, len);
+ assert(status == 0);
+
+ if (status < 0) //should never happen
+ return status;
+#endif
+ pResult = pNext;
+ return 0; //success
+ }
+ }
+
+ assert(i == j);
+
+ Cluster* const pNext = Cluster::Create(this,
+ -1, //preloaded
+ off_next,
+ element_size);
+ assert(pNext);
+
+ const ptrdiff_t idx_next = i - m_clusters; //insertion position
+
+ PreloadCluster(pNext, idx_next);
+ assert(m_clusters);
+ assert(idx_next < m_clusterSize);
+ assert(m_clusters[idx_next] == pNext);
+
+#if 0
+ status = pNext->Load(pos, len);
+ assert(status == 0);
+
+ if (status < 0)
+ return status;
+#endif
+
+ pResult = pNext;
+ return 0; //success
+}
+
+
+const Cluster* Segment::FindCluster(long long time_ns) const
+{
+ if ((m_clusters == NULL) || (m_clusterCount <= 0))
+ return &m_eos;
+
+ {
+ Cluster* const pCluster = m_clusters[0];
+ assert(pCluster);
+ assert(pCluster->m_index == 0);
+
+ if (time_ns <= pCluster->GetTime())
+ return pCluster;
+ }
+
+ //Binary search of cluster array
+
+ long i = 0;
+ long j = m_clusterCount;
+
+ while (i < j)
+ {
+ //INVARIANT:
+ //[0, i) <= time_ns
+ //[i, j) ?
+ //[j, m_clusterCount) > time_ns
+
+ const long k = i + (j - i) / 2;
+ assert(k < m_clusterCount);
+
+ Cluster* const pCluster = m_clusters[k];
+ assert(pCluster);
+ assert(pCluster->m_index == k);
+
+ const long long t = pCluster->GetTime();
+
+ if (t <= time_ns)
+ i = k + 1;
+ else
+ j = k;
+
+ assert(i <= j);
+ }
+
+ assert(i == j);
+ assert(i > 0);
+ assert(i <= m_clusterCount);
+
+ const long k = i - 1;
+
+ Cluster* const pCluster = m_clusters[k];
+ assert(pCluster);
+ assert(pCluster->m_index == k);
+ assert(pCluster->GetTime() <= time_ns);
+
+ return pCluster;
+}
+
+
+#if 0
+const BlockEntry* Segment::Seek(
+ long long time_ns,
+ const Track* pTrack) const
+{
+ assert(pTrack);
+
+ if ((m_clusters == NULL) || (m_clusterCount <= 0))
+ return pTrack->GetEOS();
+
+ Cluster** const i = m_clusters;
+ assert(i);
+
+ {
+ Cluster* const pCluster = *i;
+ assert(pCluster);
+ assert(pCluster->m_index == 0); //m_clusterCount > 0
+ assert(pCluster->m_pSegment == this);
+
+ if (time_ns <= pCluster->GetTime())
+ return pCluster->GetEntry(pTrack);
+ }
+
+ Cluster** const j = i + m_clusterCount;
+
+ if (pTrack->GetType() == 2) //audio
+ {
+ //TODO: we could decide to use cues for this, as we do for video.
+ //But we only use it for video because looking around for a keyframe
+ //can get expensive. Audio doesn't require anything special so a
+ //straight cluster search is good enough (we assume).
+
+ Cluster** lo = i;
+ Cluster** hi = j;
+
+ while (lo < hi)
+ {
+ //INVARIANT:
+ //[i, lo) <= time_ns
+ //[lo, hi) ?
+ //[hi, j) > time_ns
+
+ Cluster** const mid = lo + (hi - lo) / 2;
+ assert(mid < hi);
+
+ Cluster* const pCluster = *mid;
+ assert(pCluster);
+ assert(pCluster->m_index == long(mid - m_clusters));
+ assert(pCluster->m_pSegment == this);
+
+ const long long t = pCluster->GetTime();
+
+ if (t <= time_ns)
+ lo = mid + 1;
+ else
+ hi = mid;
+
+ assert(lo <= hi);
+ }
+
+ assert(lo == hi);
+ assert(lo > i);
+ assert(lo <= j);
+
+ while (lo > i)
+ {
+ Cluster* const pCluster = *--lo;
+ assert(pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ const BlockEntry* const pBE = pCluster->GetEntry(pTrack);
+
+ if ((pBE != 0) && !pBE->EOS())
+ return pBE;
+
+ //landed on empty cluster (no entries)
+ }
+
+ return pTrack->GetEOS(); //weird
+ }
+
+ assert(pTrack->GetType() == 1); //video
+
+ Cluster** lo = i;
+ Cluster** hi = j;
+
+ while (lo < hi)
+ {
+ //INVARIANT:
+ //[i, lo) <= time_ns
+ //[lo, hi) ?
+ //[hi, j) > time_ns
+
+ Cluster** const mid = lo + (hi - lo) / 2;
+ assert(mid < hi);
+
+ Cluster* const pCluster = *mid;
+ assert(pCluster);
+
+ const long long t = pCluster->GetTime();
+
+ if (t <= time_ns)
+ lo = mid + 1;
+ else
+ hi = mid;
+
+ assert(lo <= hi);
+ }
+
+ assert(lo == hi);
+ assert(lo > i);
+ assert(lo <= j);
+
+ Cluster* pCluster = *--lo;
+ assert(pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ {
+ const BlockEntry* const pBE = pCluster->GetEntry(pTrack, time_ns);
+
+ if ((pBE != 0) && !pBE->EOS()) //found a keyframe
+ return pBE;
+ }
+
+ const VideoTrack* const pVideo = static_cast<const VideoTrack*>(pTrack);
+
+ while (lo != i)
+ {
+ pCluster = *--lo;
+ assert(pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ const BlockEntry* const pBlockEntry = pCluster->GetMaxKey(pVideo);
+
+ if ((pBlockEntry != 0) && !pBlockEntry->EOS())
+ return pBlockEntry;
+ }
+
+ //weird: we're on the first cluster, but no keyframe found
+ //should never happen but we must return something anyway
+
+ return pTrack->GetEOS();
+}
+#endif
+
+
+#if 0
+bool Segment::SearchCues(
+ long long time_ns,
+ Track* pTrack,
+ Cluster*& pCluster,
+ const BlockEntry*& pBlockEntry,
+ const CuePoint*& pCP,
+ const CuePoint::TrackPosition*& pTP)
+{
+ if (pTrack->GetType() != 1) //not video
+ return false; //TODO: for now, just handle video stream
+
+ if (m_pCues == NULL)
+ return false;
+
+ if (!m_pCues->Find(time_ns, pTrack, pCP, pTP))
+ return false; //weird
+
+ assert(pCP);
+ assert(pTP);
+ assert(pTP->m_track == pTrack->GetNumber());
+
+ //We have the cue point and track position we want,
+ //so we now need to search for the cluster having
+ //the indicated position.
+
+ return GetCluster(pCP, pTP, pCluster, pBlockEntry);
+}
+#endif
+
+
+const Tracks* Segment::GetTracks() const
+{
+ return m_pTracks;
+}
+
+
+const SegmentInfo* Segment::GetInfo() const
+{
+ return m_pInfo;
+}
+
+
+const Cues* Segment::GetCues() const
+{
+ return m_pCues;
+}
+
+
+const SeekHead* Segment::GetSeekHead() const
+{
+ return m_pSeekHead;
+}
+
+
+long long Segment::GetDuration() const
+{
+ assert(m_pInfo);
+ return m_pInfo->GetDuration();
+}
+
+
+SegmentInfo::SegmentInfo(
+ Segment* pSegment,
+ long long start,
+ long long size_,
+ long long element_start,
+ long long element_size) :
+ m_pSegment(pSegment),
+ m_start(start),
+ m_size(size_),
+ m_pMuxingAppAsUTF8(NULL),
+ m_pWritingAppAsUTF8(NULL),
+ m_pTitleAsUTF8(NULL),
+ m_element_start(element_start),
+ m_element_size(element_size)
+{
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = start;
+ const long long stop = start + size_;
+
+ m_timecodeScale = 1000000;
+ m_duration = -1;
+
+ while (pos < stop)
+ {
+ if (Match(pReader, pos, 0x0AD7B1, m_timecodeScale))
+ assert(m_timecodeScale > 0);
+
+ else if (Match(pReader, pos, 0x0489, m_duration))
+ assert(m_duration >= 0);
+
+ else if (Match(pReader, pos, 0x0D80, m_pMuxingAppAsUTF8)) //[4D][80]
+ assert(m_pMuxingAppAsUTF8);
+
+ else if (Match(pReader, pos, 0x1741, m_pWritingAppAsUTF8)) //[57][41]
+ assert(m_pWritingAppAsUTF8);
+
+ else if (Match(pReader, pos, 0x3BA9, m_pTitleAsUTF8)) //[7B][A9]
+ assert(m_pTitleAsUTF8);
+
+ else
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ //id;
+ assert(id >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+ assert((stop - pos) > 0);
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len + size; //consume size and payload
+ assert(pos <= stop);
+ }
+ }
+
+ assert(pos == stop);
+}
+
+SegmentInfo::~SegmentInfo()
+{
+ if (m_pMuxingAppAsUTF8)
+ {
+ delete[] m_pMuxingAppAsUTF8;
+ m_pMuxingAppAsUTF8 = NULL;
+ }
+
+ if (m_pWritingAppAsUTF8)
+ {
+ delete[] m_pWritingAppAsUTF8;
+ m_pWritingAppAsUTF8 = NULL;
+ }
+
+ if (m_pTitleAsUTF8)
+ {
+ delete[] m_pTitleAsUTF8;
+ m_pTitleAsUTF8 = NULL;
+ }
+}
+
+long long SegmentInfo::GetTimeCodeScale() const
+{
+ return m_timecodeScale;
+}
+
+
+long long SegmentInfo::GetDuration() const
+{
+ if (m_duration < 0)
+ return -1;
+
+ assert(m_timecodeScale >= 1);
+
+ const double dd = double(m_duration) * double(m_timecodeScale);
+ const long long d = static_cast<long long>(dd);
+
+ return d;
+}
+
+const char* SegmentInfo::GetMuxingAppAsUTF8() const
+{
+ return m_pMuxingAppAsUTF8;
+}
+
+
+const char* SegmentInfo::GetWritingAppAsUTF8() const
+{
+ return m_pWritingAppAsUTF8;
+}
+
+const char* SegmentInfo::GetTitleAsUTF8() const
+{
+ return m_pTitleAsUTF8;
+}
+
+Track::Track(
+ Segment* pSegment,
+ const Info& i,
+ long long element_start,
+ long long element_size) :
+ m_pSegment(pSegment),
+ m_info(i),
+ m_element_start(element_start),
+ m_element_size(element_size)
+{
+}
+
+Track::~Track()
+{
+ Info& info = const_cast<Info&>(m_info);
+ info.Clear();
+}
+
+Track::Info::Info():
+ type(-1),
+ number(-1),
+ uid(ULLONG_MAX),
+ nameAsUTF8(NULL),
+ codecId(NULL),
+ codecPrivate(NULL),
+ codecPrivateSize(0),
+ codecNameAsUTF8(NULL)
+{
+}
+
+void Track::Info::Clear()
+{
+ delete[] nameAsUTF8;
+ nameAsUTF8 = NULL;
+
+ delete[] codecId;
+ codecId = NULL;
+
+ delete[] codecPrivate;
+ codecPrivate = NULL;
+
+ codecPrivateSize = 0;
+
+ delete[] codecNameAsUTF8;
+ codecNameAsUTF8 = NULL;
+}
+
+const BlockEntry* Track::GetEOS() const
+{
+ return &m_eos;
+}
+
+long long Track::GetType() const
+{
+ return m_info.type;
+}
+
+long long Track::GetNumber() const
+{
+ return m_info.number;
+}
+
+unsigned long long Track::GetUid() const
+{
+ return m_info.uid;
+}
+
+const char* Track::GetNameAsUTF8() const
+{
+ return m_info.nameAsUTF8;
+}
+
+const char* Track::GetCodecNameAsUTF8() const
+{
+ return m_info.codecNameAsUTF8;
+}
+
+
+const char* Track::GetCodecId() const
+{
+ return m_info.codecId;
+}
+
+const unsigned char* Track::GetCodecPrivate(size_t& size) const
+{
+ size = m_info.codecPrivateSize;
+ return m_info.codecPrivate;
+}
+
+
+bool Track::GetLacing() const
+{
+ return m_info.lacing;
+}
+
+
+long Track::GetFirst(const BlockEntry*& pBlockEntry) const
+{
+ const Cluster* pCluster = m_pSegment->GetFirst();
+
+ for (int i = 0; ; )
+ {
+ if (pCluster == NULL)
+ {
+ pBlockEntry = GetEOS();
+ return 1;
+ }
+
+ if (pCluster->EOS())
+ {
+ if (m_pSegment->Unparsed() <= 0) //all clusters have been loaded
+ {
+ pBlockEntry = GetEOS();
+ return 1;
+ }
+
+ pBlockEntry = 0;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ pBlockEntry = pCluster->GetFirst();
+
+ if (pBlockEntry == 0) //empty cluster
+ {
+ pCluster = m_pSegment->GetNext(pCluster);
+ continue;
+ }
+
+ for (;;)
+ {
+ const Block* const pBlock = pBlockEntry->GetBlock();
+ assert(pBlock);
+
+ const long long tn = pBlock->GetTrackNumber();
+
+ if ((tn == m_info.number) && VetEntry(pBlockEntry))
+ return 0;
+
+ pBlockEntry = pCluster->GetNext(pBlockEntry);
+
+ if (pBlockEntry == 0)
+ break;
+ }
+
+ ++i;
+
+ if (i >= 100)
+ break;
+
+ pCluster = m_pSegment->GetNext(pCluster);
+ }
+
+ //NOTE: if we get here, it means that we didn't find a block with
+ //a matching track number. We interpret that as an error (which
+ //might be too conservative).
+
+ pBlockEntry = GetEOS(); //so we can return a non-NULL value
+ return 1;
+}
+
+
+long Track::GetNext(
+ const BlockEntry* pCurrEntry,
+ const BlockEntry*& pNextEntry) const
+{
+ assert(pCurrEntry);
+ assert(!pCurrEntry->EOS()); //?
+
+ const Block* const pCurrBlock = pCurrEntry->GetBlock();
+ assert(pCurrBlock->GetTrackNumber() == m_info.number);
+
+ const Cluster* pCluster = pCurrEntry->GetCluster();
+ assert(pCluster);
+ assert(!pCluster->EOS());
+
+ pNextEntry = pCluster->GetNext(pCurrEntry);
+
+ for (int i = 0; ; )
+ {
+ while (pNextEntry)
+ {
+ const Block* const pNextBlock = pNextEntry->GetBlock();
+ assert(pNextBlock);
+
+ if (pNextBlock->GetTrackNumber() == m_info.number)
+ return 0;
+
+ pNextEntry = pCluster->GetNext(pNextEntry);
+ }
+
+ pCluster = m_pSegment->GetNext(pCluster);
+
+ if (pCluster == NULL)
+ {
+ pNextEntry = GetEOS();
+ return 1;
+ }
+
+ if (pCluster->EOS())
+ {
+ if (m_pSegment->Unparsed() <= 0) //all clusters have been loaded
+ {
+ pNextEntry = GetEOS();
+ return 1;
+ }
+
+ //TODO: there is a potential O(n^2) problem here: we tell the
+ //caller to (pre)load another cluster, which he does, but then he
+ //calls GetNext again, which repeats the same search. This is
+ //a pathological case, since the only way it can happen is if
+ //there exists a long sequence of clusters none of which contain a
+ // block from this track. One way around this problem is for the
+ //caller to be smarter when he loads another cluster: don't call
+ //us back until you have a cluster that contains a block from this
+ //track. (Of course, that's not cheap either, since our caller
+ //would have to scan the each cluster as it's loaded, so that
+ //would just push back the problem.)
+
+ pNextEntry = NULL;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ pNextEntry = pCluster->GetFirst();
+
+ if (pNextEntry == NULL) //empty cluster
+ continue;
+
+ ++i;
+
+ if (i >= 100)
+ break;
+ }
+
+ //NOTE: if we get here, it means that we didn't find a block with
+ //a matching track number after lots of searching, so we give
+ //up trying.
+
+ pNextEntry = GetEOS(); //so we can return a non-NULL value
+ return 1;
+}
+
+
+Track::EOSBlock::EOSBlock()
+{
+}
+
+
+bool Track::EOSBlock::EOS() const
+{
+ return true;
+}
+
+
+const Cluster* Track::EOSBlock::GetCluster() const
+{
+ return NULL;
+}
+
+
+size_t Track::EOSBlock::GetIndex() const
+{
+ return 0;
+}
+
+
+const Block* Track::EOSBlock::GetBlock() const
+{
+ return NULL;
+}
+
+
+bool Track::EOSBlock::IsBFrame() const
+{
+ return false;
+}
+
+
+VideoTrack::VideoTrack(
+ Segment* pSegment,
+ const Info& i,
+ long long element_start,
+ long long element_size) :
+ Track(pSegment, i, element_start, element_size),
+ m_width(-1),
+ m_height(-1),
+ m_rate(-1)
+{
+ assert(i.type == 1);
+ assert(i.number > 0);
+
+ IMkvReader* const pReader = pSegment->m_pReader;
+
+ const Settings& s = i.settings;
+ assert(s.start >= 0);
+ assert(s.size >= 0);
+
+ long long pos = s.start;
+ assert(pos >= 0);
+
+ const long long stop = pos + s.size;
+
+ while (pos < stop)
+ {
+#ifdef _DEBUG
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+#endif
+ if (Match(pReader, pos, 0x30, m_width))
+ ;
+ else if (Match(pReader, pos, 0x3A, m_height))
+ ;
+ else if (Match(pReader, pos, 0x0383E3, m_rate))
+ ;
+ else
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+ assert((pos + size) <= stop);
+
+ //pos now designates start of payload
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+ }
+
+ return;
+}
+
+
+bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const
+{
+ assert(pBlockEntry);
+
+ const Block* const pBlock = pBlockEntry->GetBlock();
+ assert(pBlock);
+ assert(pBlock->GetTrackNumber() == m_info.number);
+
+ return pBlock->IsKey();
+}
+
+
+long VideoTrack::Seek(
+ long long time_ns,
+ const BlockEntry*& pResult) const
+{
+ const long status = GetFirst(pResult);
+
+ if (status < 0) //buffer underflow, etc
+ return status;
+
+ assert(pResult);
+
+ if (pResult->EOS())
+ return 0;
+
+ const Cluster* pCluster = pResult->GetCluster();
+ assert(pCluster);
+ assert(pCluster->GetIndex() >= 0);
+
+ if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
+ return 0;
+
+ Cluster** const clusters = m_pSegment->m_clusters;
+ assert(clusters);
+
+ const long count = m_pSegment->GetCount(); //loaded only, not pre-loaded
+ assert(count > 0);
+
+ Cluster** const i = clusters + pCluster->GetIndex();
+ assert(i);
+ assert(*i == pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ Cluster** const j = clusters + count;
+
+ Cluster** lo = i;
+ Cluster** hi = j;
+
+ while (lo < hi)
+ {
+ //INVARIANT:
+ //[i, lo) <= time_ns
+ //[lo, hi) ?
+ //[hi, j) > time_ns
+
+ Cluster** const mid = lo + (hi - lo) / 2;
+ assert(mid < hi);
+
+ pCluster = *mid;
+ assert(pCluster);
+ assert(pCluster->GetIndex() >= 0);
+ assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
+
+ const long long t = pCluster->GetTime();
+
+ if (t <= time_ns)
+ lo = mid + 1;
+ else
+ hi = mid;
+
+ assert(lo <= hi);
+ }
+
+ assert(lo == hi);
+ assert(lo > i);
+ assert(lo <= j);
+
+ pCluster = *--lo;
+ assert(pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ pResult = pCluster->GetEntry(this, time_ns);
+
+ if ((pResult != 0) && !pResult->EOS()) //found a keyframe
+ return 0;
+
+ while (lo != i)
+ {
+ pCluster = *--lo;
+ assert(pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ //TODO:
+ //We need to handle the case when a cluster
+ //contains multiple keyframes. Simply returning
+ //the largest keyframe on the cluster isn't
+ //good enough.
+ pResult = pCluster->GetMaxKey(this);
+
+ if ((pResult != 0) && !pResult->EOS())
+ return 0;
+ }
+
+ //weird: we're on the first cluster, but no keyframe found
+ //should never happen but we must return something anyway
+
+ pResult = GetEOS();
+ return 0;
+}
+
+
+long long VideoTrack::GetWidth() const
+{
+ return m_width;
+}
+
+
+long long VideoTrack::GetHeight() const
+{
+ return m_height;
+}
+
+
+double VideoTrack::GetFrameRate() const
+{
+ return m_rate;
+}
+
+
+AudioTrack::AudioTrack(
+ Segment* pSegment,
+ const Info& i,
+ long long element_start,
+ long long element_size) :
+ Track(pSegment, i, element_start, element_size),
+ m_rate(0.0),
+ m_channels(0),
+ m_bitDepth(-1)
+{
+ assert(i.type == 2);
+ assert(i.number > 0);
+
+ IMkvReader* const pReader = pSegment->m_pReader;
+
+ const Settings& s = i.settings;
+ assert(s.start >= 0);
+ assert(s.size >= 0);
+
+ long long pos = s.start;
+ assert(pos >= 0);
+
+ const long long stop = pos + s.size;
+
+ while (pos < stop)
+ {
+#ifdef _DEBUG
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+#endif
+ if (Match(pReader, pos, 0x35, m_rate))
+ ;
+ else if (Match(pReader, pos, 0x1F, m_channels))
+ ;
+ else if (Match(pReader, pos, 0x2264, m_bitDepth))
+ ;
+ else
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+ assert((pos + size) <= stop);
+
+ //pos now designates start of payload
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+ }
+
+ if (m_channels <= 0)
+ m_channels = 1; //Matroska spec says this is the default
+
+ return;
+}
+
+
+bool AudioTrack::VetEntry(const BlockEntry* pBlockEntry) const
+{
+ assert(pBlockEntry);
+
+ const Block* const pBlock = pBlockEntry->GetBlock();
+ assert(pBlock);
+ assert(pBlock->GetTrackNumber() == m_info.number);
+
+ return true;
+}
+
+
+long AudioTrack::Seek(
+ long long time_ns,
+ const BlockEntry*& pResult) const
+{
+ const long status = GetFirst(pResult);
+
+ if (status < 0) //buffer underflow, etc
+ return status;
+
+ assert(pResult);
+
+ if (pResult->EOS())
+ return 0;
+
+ const Cluster* pCluster = pResult->GetCluster();
+ assert(pCluster);
+ assert(pCluster->GetIndex() >= 0);
+
+ if (time_ns <= pResult->GetBlock()->GetTime(pCluster))
+ return 0;
+
+ Cluster** const clusters = m_pSegment->m_clusters;
+ assert(clusters);
+
+ const long count = m_pSegment->GetCount(); //loaded only, not preloaded
+ assert(count > 0);
+
+ Cluster** const i = clusters + pCluster->GetIndex();
+ assert(i);
+ assert(*i == pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ Cluster** const j = clusters + count;
+
+ Cluster** lo = i;
+ Cluster** hi = j;
+
+ while (lo < hi)
+ {
+ //INVARIANT:
+ //[i, lo) <= time_ns
+ //[lo, hi) ?
+ //[hi, j) > time_ns
+
+ Cluster** const mid = lo + (hi - lo) / 2;
+ assert(mid < hi);
+
+ pCluster = *mid;
+ assert(pCluster);
+ assert(pCluster->GetIndex() >= 0);
+ assert(pCluster->GetIndex() == long(mid - m_pSegment->m_clusters));
+
+ const long long t = pCluster->GetTime();
+
+ if (t <= time_ns)
+ lo = mid + 1;
+ else
+ hi = mid;
+
+ assert(lo <= hi);
+ }
+
+ assert(lo == hi);
+ assert(lo > i);
+ assert(lo <= j);
+
+ while (lo > i)
+ {
+ pCluster = *--lo;
+ assert(pCluster);
+ assert(pCluster->GetTime() <= time_ns);
+
+ pResult = pCluster->GetEntry(this);
+
+ if ((pResult != 0) && !pResult->EOS())
+ return 0;
+
+ //landed on empty cluster (no entries)
+ }
+
+ pResult = GetEOS(); //weird
+ return 0;
+}
+
+
+double AudioTrack::GetSamplingRate() const
+{
+ return m_rate;
+}
+
+
+long long AudioTrack::GetChannels() const
+{
+ return m_channels;
+}
+
+long long AudioTrack::GetBitDepth() const
+{
+ return m_bitDepth;
+}
+
+Tracks::Tracks(
+ Segment* pSegment,
+ long long start,
+ long long size_,
+ long long element_start,
+ long long element_size) :
+ m_pSegment(pSegment),
+ m_start(start),
+ m_size(size_),
+ m_trackEntries(NULL),
+ m_trackEntriesEnd(NULL),
+ m_element_start(element_start),
+ m_element_size(element_size)
+{
+ long long stop = m_start + m_size;
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos1 = m_start;
+ int count = 0;
+
+ while (pos1 < stop)
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos1, len);
+ assert(id >= 0);
+ assert((pos1 + len) <= stop);
+
+ pos1 += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos1, len);
+ assert(size >= 0);
+ assert((pos1 + len) <= stop);
+
+ pos1 += len; //consume length of size
+
+ //pos now desinates start of element
+ if (id == 0x2E) //TrackEntry ID
+ ++count;
+
+ pos1 += size; //consume payload
+ assert(pos1 <= stop);
+ }
+
+ if (count <= 0)
+ return;
+
+ m_trackEntries = new Track*[count];
+ m_trackEntriesEnd = m_trackEntries;
+
+ long long pos = m_start;
+
+ while (pos < stop)
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert((pos + len) <= stop);
+
+ const long long element_start = pos;
+
+ pos += len; //consume id
+
+ const long long size1 = ReadUInt(pReader, pos, len);
+ assert(size1 >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+
+ //pos now desinates start of element
+
+ const long long element_size = size1 + pos - element_start;
+
+ if (id == 0x2E) //TrackEntry ID
+ {
+ Track*& pTrack = *m_trackEntriesEnd;
+ ParseTrackEntry(pos, size1, pTrack, element_start, element_size);
+
+ if (pTrack)
+ ++m_trackEntriesEnd;
+ }
+
+ pos += size1; //consume payload
+ assert(pos <= stop);
+ }
+}
+
+
+unsigned long Tracks::GetTracksCount() const
+{
+ const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
+ assert(result >= 0);
+
+ return static_cast<unsigned long>(result);
+}
+
+
+void Tracks::ParseTrackEntry(
+ long long start,
+ long long size,
+ Track*& pTrack,
+ long long element_start,
+ long long element_size)
+{
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = start;
+ const long long stop = start + size;
+
+ Track::Info i;
+
+ Track::Settings videoSettings;
+ videoSettings.start = -1;
+
+ Track::Settings audioSettings;
+ audioSettings.start = -1;
+
+ long long lacing = 1; //default is true
+
+ while (pos < stop)
+ {
+#ifdef _DEBUG
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ len;
+ id;
+#endif
+ if (Match(pReader, pos, 0x57, i.number))
+ assert(i.number > 0);
+ //else if (Match(pReader, pos, 0x33C5, i.uid))
+ // ;
+ else if (Match(pReader, pos, 0x03, i.type))
+ ;
+ else if (Match(pReader, pos, 0x136E, i.nameAsUTF8))
+ assert(i.nameAsUTF8);
+ else if (Match(pReader, pos, 0x06, i.codecId))
+ ;
+ else if (Match(pReader, pos, 0x1C, lacing))
+ assert(lacing <= 1);
+ else if (Match(pReader,
+ pos,
+ 0x23A2,
+ i.codecPrivate,
+ i.codecPrivateSize))
+ ;
+ else if (Match(pReader, pos, 0x058688, i.codecNameAsUTF8))
+ assert(i.codecNameAsUTF8);
+ else
+ {
+ long len;
+
+ const long long idpos = pos;
+ idpos;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+ const long long start = pos;
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+
+ if (id == 0x60)
+ {
+ videoSettings.start = start;
+ videoSettings.size = size;
+ }
+ else if (id == 0x61)
+ {
+ audioSettings.start = start;
+ audioSettings.size = size;
+ }
+ else if (id == 0x33C5) //Track UID
+ {
+ assert(size <= 8);
+
+ i.uid = 0;
+ long long pos_ = start;
+ const long long pos_end = start + size;
+
+ while (pos_ != pos_end)
+ {
+ unsigned char b;
+
+ const long status = pReader->Read(pos_, 1, &b);
+ assert(status == 0);
+
+ i.uid <<= 8;
+ i.uid |= b;
+
+ ++pos_;
+ }
+ }
+ }
+ }
+
+ assert(pos == stop);
+ //TODO: propertly vet info.number, to ensure both its existence,
+ //and that it is unique among all tracks.
+ assert(i.number > 0);
+
+ i.lacing = (lacing > 0) ? true : false;
+
+ //TODO: vet settings, to ensure that video settings (0x60)
+ //were specified when type = 1, and that audio settings (0x61)
+ //were specified when type = 2.
+ if (i.type == 1) //video
+ {
+ assert(audioSettings.start < 0);
+ assert(videoSettings.start >= 0);
+
+ i.settings = videoSettings;
+
+ VideoTrack* const t = new VideoTrack(
+ m_pSegment,
+ i,
+ element_start,
+ element_size);
+ assert(t); //TODO
+ pTrack = t;
+ }
+ else if (i.type == 2) //audio
+ {
+ assert(videoSettings.start < 0);
+ assert(audioSettings.start >= 0);
+
+ i.settings = audioSettings;
+
+ AudioTrack* const t = new AudioTrack(
+ m_pSegment,
+ i,
+ element_start,
+ element_size);
+ assert(t); //TODO
+ pTrack = t;
+ }
+ else
+ {
+ // for now we do not support other track types yet.
+ // TODO: support other track types
+ i.Clear();
+
+ pTrack = NULL;
+ }
+
+ return;
+}
+
+
+Tracks::~Tracks()
+{
+ Track** i = m_trackEntries;
+ Track** const j = m_trackEntriesEnd;
+
+ while (i != j)
+ {
+ Track* const pTrack = *i++;
+ delete pTrack;
+ }
+
+ delete[] m_trackEntries;
+}
+
+const Track* Tracks::GetTrackByNumber(unsigned long tn_) const
+{
+ const long long tn = tn_;
+
+ Track** i = m_trackEntries;
+ Track** const j = m_trackEntriesEnd;
+
+ while (i != j)
+ {
+ Track* const pTrack = *i++;
+
+ if (pTrack == NULL)
+ continue;
+
+ if (tn == pTrack->GetNumber())
+ return pTrack;
+ }
+
+ return NULL; //not found
+}
+
+
+const Track* Tracks::GetTrackByIndex(unsigned long idx) const
+{
+ const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
+
+ if (idx >= static_cast<unsigned long>(count))
+ return NULL;
+
+ return m_trackEntries[idx];
+}
+
+
+long long Cluster::Unparsed() const
+{
+ if (m_size < 0) //not even partially loaded
+ return LLONG_MAX;
+
+ assert(m_pos >= m_element_start);
+ assert(m_element_size > m_size);
+
+ const long long element_stop = m_element_start + m_element_size;
+ assert(m_pos <= element_stop);
+
+ const long long result = element_stop - m_pos;
+ assert(result >= 0);
+
+ return result;
+}
+
+
+void Cluster::Load() const
+{
+ assert(m_pSegment);
+ assert(m_pos >= m_element_start);
+ assert(m_size);
+
+ if (m_size > 0) //loaded
+ {
+ assert(m_timecode >= 0);
+ return;
+ }
+
+ assert(m_pos == m_element_start);
+ assert(m_size < 0);
+ assert(m_timecode < 0);
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ //m_pos *= -1; //relative to segment
+ //long long pos = m_pSegment->m_start + m_pos; //absolute
+
+ long len;
+
+ const long long id_ = ReadUInt(pReader, m_pos, len);
+ assert(id_ >= 0);
+ assert(id_ == 0x0F43B675); //Cluster ID
+
+ m_pos += len; //consume id
+
+ m_size = ReadUInt(pReader, m_pos, len);
+ assert(m_size >= 0);
+
+ m_pos += len; //consume size field
+
+ const long long stop = m_pos + m_size;
+
+ const long long element_size = stop - m_element_start;
+ assert((m_element_size <= 0) || (m_element_size == element_size));
+
+ if (m_element_size <= 0)
+ m_element_size = element_size;
+
+ long long timecode = -1;
+
+ while (m_pos < stop)
+ {
+ if (Match(pReader, m_pos, 0x67, timecode))
+ break;
+ else
+ {
+ const long long id = ReadUInt(pReader, m_pos, len);
+ assert(id >= 0); //TODO
+ assert((m_pos + len) <= stop);
+
+ m_pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, m_pos, len);
+ assert(size >= 0); //TODO
+ assert((m_pos + len) <= stop);
+
+ m_pos += len; //consume size
+
+ if (id == 0x20) //BlockGroup ID
+ break;
+
+ if (id == 0x23) //SimpleBlock ID
+ break;
+
+ m_pos += size; //consume payload
+ assert(m_pos <= stop);
+ }
+ }
+
+ assert(m_pos <= stop);
+ assert(timecode >= 0);
+
+ m_timecode = timecode;
+}
+
+
+long Cluster::Load(long long& pos, long& len) const
+{
+ assert(m_pSegment);
+ assert(m_pos >= m_element_start);
+ assert(m_size);
+
+ if (m_size > 0) //loaded (partially or fully)
+ {
+ assert(m_timecode >= 0);
+ assert(m_element_size > m_size);
+ assert(m_pos <= (m_element_start + m_element_size));
+
+ return 0;
+ }
+
+ assert(m_pos == m_element_start);
+ assert(m_size < 0);
+ assert(m_timecode < 0);
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long total, avail;
+
+ const int status = pReader->Length(&total, &avail);
+
+ if (status < 0) //error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ pos = m_pos;
+
+ long long cluster_size, cluster_stop;
+
+ {
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error or underflow
+ return static_cast<long>(result);
+
+ if (result > 0) //underflow (weird)
+ return E_BUFFER_NOT_FULL;
+
+ //if ((pos + len) > segment_stop)
+ // return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id_ = ReadUInt(pReader, pos, len);
+
+ if (id_ < 0) //error
+ return static_cast<long>(id_);
+
+ if (id_ != 0x0F43B675) //Cluster ID
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume id
+
+ //read cluster size
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ //if ((pos + len) > segment_stop)
+ // return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ cluster_size = ReadUInt(pReader, pos, len);
+
+ if (cluster_size < 0) //error
+ return static_cast<long>(cluster_size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (cluster_size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ if (cluster_size == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume length of size of element
+
+ cluster_stop = pos + cluster_size;
+ }
+
+ //pos points to start of payload
+
+#if 0
+ len = static_cast<long>(size_);
+
+ if (cluster_stop > avail)
+ return E_BUFFER_NOT_FULL;
+#endif
+
+ long long timecode = -1;
+ long long new_pos = -1;
+ bool bBlock = false;
+
+ while (pos < cluster_stop)
+ {
+ //Parse ID
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id = ReadUInt(pReader, pos, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ if (id == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume ID field
+
+ //Parse Size
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume size field
+
+ if (pos > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ //pos now points to start of payload
+
+ if (size == 0) //weird
+ continue;
+
+ if ((pos + size) > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == 0x67) //TimeCode ID
+ {
+ len = static_cast<long>(size);
+
+ if ((pos + size) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ timecode = UnserializeUInt(pReader, pos, size);
+
+ if (timecode < 0) //error (or underflow)
+ return static_cast<long>(timecode);
+
+ new_pos = pos + size;
+
+ if (bBlock)
+ break;
+ }
+ else if (id == 0x20) //BlockGroup ID
+ {
+ bBlock = true;
+ break;
+ }
+ else if (id == 0x23) //SimpleBlock ID
+ {
+ bBlock = true;
+ break;
+ }
+
+ pos += size; //consume payload
+ assert(pos <= cluster_stop);
+ }
+
+ assert(pos <= cluster_stop);
+
+ if (timecode < 0) //no timecode found
+ return E_FILE_FORMAT_INVALID;
+
+ if (!bBlock)
+ return E_FILE_FORMAT_INVALID;
+
+ m_pos = new_pos; //designates position just beyond timecode payload
+ m_size = cluster_size; // m_size > 0 means we're partially loaded
+ m_element_size = cluster_stop - m_element_start;
+
+ m_timecode = timecode; // m_timecode >= 0 means we're partially loaded
+
+ //LoadBlockEntries();
+
+ return 0;
+}
+
+
+long Cluster::Parse(long long& pos, long& len) const
+{
+ long status = Load(pos, len);
+
+ if (status < 0)
+ return status;
+
+ assert(m_pos >= m_element_start);
+ assert(m_size > 0);
+ assert(m_element_size > m_size);
+ assert(m_timecode >= 0);
+
+ const long long cluster_stop = m_element_start + m_element_size;
+
+ if (m_pos >= cluster_stop)
+ return 1; //nothing else to do
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long total, avail;
+
+ status = pReader->Length(&total, &avail);
+
+ if (status < 0) //error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ pos = m_pos;
+
+ while (pos < cluster_stop)
+ {
+ //Parse ID
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id = ReadUInt(pReader, pos, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ if (id == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume ID field
+
+ //Parse Size
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume size field
+
+ if (pos > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ //pos now points to start of payload
+
+ if (size == 0) //weird
+ continue;
+
+ const long long block_start = pos;
+ const long long block_stop = pos + size;
+
+ if (block_stop > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((id != 0x20) && (id != 0x23)) //BlockGroup or SimpleBlock
+ {
+ pos += size; //consume payload
+ assert(pos <= cluster_stop);
+
+ continue;
+ }
+
+ //Parse track number
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long track = ReadUInt(pReader, pos, len);
+
+ if (track < 0) //error
+ return static_cast<long>(track);
+
+ if (track == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume track number
+
+ if (pos >= block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += 2; //consume timecode
+
+ if (pos >= block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (pos >= avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ unsigned char flags;
+
+ status = pReader->Read(pos, 1, &flags);
+
+ if (status < 0) //error or underflow
+ {
+ len = 1;
+ return status;
+ }
+
+ ++pos; //consume flags byte
+
+ if (pos >= block_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ const int lacing = int(flags & 0x06) >> 1;
+
+ if ((lacing != 0) && (block_stop > avail))
+ {
+ len = static_cast<long>(block_stop - pos);
+ return E_BUFFER_NOT_FULL;
+ }
+
+ ParseBlock(id, block_start, size);
+
+ m_pos = block_stop;
+ assert(m_pos <= cluster_stop);
+
+ return 0; //success
+ }
+
+ m_pos = pos;
+ assert(m_pos <= cluster_stop);
+
+ return 1; //no more entries
+}
+
+
+long Cluster::GetEntry(long index, const mkvparser::BlockEntry*& pEntry) const
+{
+ assert(m_pos >= m_element_start);
+
+ pEntry = 0;
+
+ if (index < 0)
+ return -1; //generic error
+
+ if (m_entries_count < 0)
+ return E_BUFFER_NOT_FULL;
+
+ assert(m_entries);
+ assert(m_entries_size > 0);
+ assert(m_entries_count <= m_entries_size);
+ assert(m_size > 0);
+ assert(m_element_size > m_size);
+
+ if (index < m_entries_count)
+ {
+ pEntry = m_entries[index];
+ assert(pEntry);
+
+ return 1; //found entry
+ }
+
+ const long long element_stop = m_element_start + m_element_size;
+
+ if (m_pos >= element_stop)
+ return 0; //nothing left to parse
+
+ return E_BUFFER_NOT_FULL; //underflow, since more remains to be parsed
+}
+
+
+Cluster* Cluster::Create(
+ Segment* pSegment,
+ long idx,
+ long long off,
+ long long element_size)
+{
+ assert(pSegment);
+ assert(off >= 0);
+
+ const long long element_start = pSegment->m_start + off;
+
+ Cluster* const pCluster = new Cluster(pSegment,
+ idx,
+ //-off, //means preloaded only
+ element_start,
+ element_size);
+ assert(pCluster);
+
+ return pCluster;
+}
+
+
+Cluster::Cluster() :
+ m_pSegment(NULL),
+ m_index(0),
+ m_pos(0),
+ m_size(0),
+ m_element_start(0),
+ m_element_size(0),
+ m_timecode(0),
+ m_entries(NULL),
+ m_entries_size(0),
+ m_entries_count(0) //means "no entries"
+{
+}
+
+
+Cluster::Cluster(
+ Segment* pSegment,
+ long idx,
+ long long element_start,
+ long long element_size) :
+ m_pSegment(pSegment),
+ m_index(idx),
+ m_pos(element_start),
+ m_element_start(element_start),
+ m_element_size(element_size),
+ m_size(-1),
+ m_timecode(-1),
+ m_entries(NULL),
+ m_entries_size(0),
+ m_entries_count(-1) //means "has not been parsed yet"
+{
+}
+
+
+Cluster::~Cluster()
+{
+ if (m_entries_count <= 0)
+ return;
+
+ BlockEntry** i = m_entries;
+ BlockEntry** const j = m_entries + m_entries_count;
+
+ while (i != j)
+ {
+ BlockEntry* p = *i++;
+ assert(p);
+
+ delete p;
+ }
+
+ delete[] m_entries;
+}
+
+
+bool Cluster::EOS() const
+{
+ return (m_pSegment == NULL);
+}
+
+
+long Cluster::GetIndex() const
+{
+ return m_index;
+}
+
+
+long long Cluster::GetPosition() const
+{
+ const long long pos = m_element_start - m_pSegment->m_start;
+ assert(pos >= 0);
+
+ return pos;
+}
+
+
+long long Cluster::GetElementSize() const
+{
+ return m_element_size;
+}
+
+
+#if 0
+bool Cluster::HasBlockEntries(
+ const Segment* pSegment,
+ long long off) //relative to start of segment payload
+{
+ assert(pSegment);
+ assert(off >= 0); //relative to segment
+
+ IMkvReader* const pReader = pSegment->m_pReader;
+
+ long long pos = pSegment->m_start + off; //absolute
+ long long size;
+
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ id;
+ assert(id >= 0);
+ assert(id == 0x0F43B675); //Cluster ID
+
+ pos += len; //consume id
+
+ size = ReadUInt(pReader, pos, len);
+ assert(size > 0);
+
+ pos += len; //consume size
+
+ //pos now points to start of payload
+ }
+
+ const long long stop = pos + size;
+
+ while (pos < stop)
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume size
+
+ if (id == 0x20) //BlockGroup ID
+ return true;
+
+ if (id == 0x23) //SimpleBlock ID
+ return true;
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ return false;
+}
+#endif
+
+
+long Cluster::HasBlockEntries(
+ const Segment* pSegment,
+ long long off, //relative to start of segment payload
+ long long& pos,
+ long& len)
+{
+ assert(pSegment);
+ assert(off >= 0); //relative to segment
+
+ IMkvReader* const pReader = pSegment->m_pReader;
+
+ long long total, avail;
+
+ long status = pReader->Length(&total, &avail);
+
+ if (status < 0) //error
+ return status;
+
+ assert((total < 0) || (avail <= total));
+
+ pos = pSegment->m_start + off; //absolute
+
+ const long long segment_stop =
+ (pSegment->m_size < 0) ? -1 : pSegment->m_start + pSegment->m_size;
+
+ long long cluster_stop;
+
+ {
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //need more data
+ return E_BUFFER_NOT_FULL;
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id = ReadUInt(pReader, pos, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ if (id != 0x0F43B675) //weird: not cluster ID
+ return -1; //generic error
+
+ pos += len; //consume Cluster ID field
+
+ //read size field
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //weird
+ return E_BUFFER_NOT_FULL;
+
+ if ((segment_stop >= 0) && ((pos + len) > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ if (size == 0)
+ return 0; //cluster does not have entries
+
+ pos += len; //consume size field
+
+ cluster_stop = pos + size;
+
+ if ((segment_stop >= 0) && (cluster_stop > segment_stop))
+ return E_FILE_FORMAT_INVALID;
+
+ if ((total >= 0) && (cluster_stop > total))
+ return E_FILE_FORMAT_INVALID;
+ }
+
+ //pos points to start of payload
+
+ while (pos < cluster_stop)
+ {
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //need more data
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long id = ReadUInt(pReader, pos, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ pos += len; //consume id field
+
+ if (pos >= cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ //read size field
+
+ if ((pos + 1) > avail)
+ {
+ len = 1;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if (result > 0) //underflow
+ return E_BUFFER_NOT_FULL;
+
+ if ((pos + len) > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > avail)
+ return E_BUFFER_NOT_FULL;
+
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+
+ if (size == unknown_size)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume size field
+
+ if (pos > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (size == 0) //weird
+ continue;
+
+ if ((pos + size) > cluster_stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == 0x20) //BlockGroup ID
+ return 1; //have at least one entry
+
+ if (id == 0x23) //SimpleBlock ID
+ return 1; //have at least one entry
+
+ pos += size; //consume payload
+ assert(pos <= cluster_stop);
+ }
+
+ return 0; //no entries detected
+}
+
+
+void Cluster::LoadBlockEntries() const
+{
+ //LoadBlockEntries loads all of the entries on the cluster.
+
+ //if (m_entries)
+ // return;
+
+ //if (m_entries_count == 0) //already parsed, and no entries found
+ // return;
+
+ if (m_pSegment == 0) //EOS cluster
+ return;
+
+ assert(m_pos >= m_element_start);
+ assert(m_size); //preloaded only, or (partially) loaded
+ //assert(m_entries_count < 0);
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ //if (m_pos < 0)
+ // m_pos *= -1; //relative to segment
+ //long long pos = m_pSegment->m_start + m_pos; //absolute
+
+ if (m_size < 0)
+ {
+ assert(m_pos == m_element_start);
+
+ long len;
+
+ const long long id = ReadUInt(pReader, m_pos, len);
+ // id;
+ assert(id >= 0);
+ assert(id == 0x0F43B675); //Cluster ID
+
+ m_pos += len; //consume id
+
+ m_size = ReadUInt(pReader, m_pos, len);
+ assert(m_size > 0);
+
+ const long long unknown_size = (1LL << (7 * len)) - 1;
+ unknown_size;
+ assert(m_size != unknown_size);
+
+ m_pos += len; //consume size field
+
+ //m_pos now points to start of cluster payload
+
+ const long long cluster_stop = m_pos + m_size;
+ const long long element_size = cluster_stop - m_element_start;
+ assert((m_element_size <= 0) || (m_element_size == element_size));
+
+ if (element_size <= 0)
+ m_element_size = element_size;
+ }
+
+ assert(m_size > 0);
+ assert(m_element_size > m_size);
+
+ const long long cluster_stop = m_element_start + m_element_size;
+
+ if (m_pos >= cluster_stop)
+ return;
+
+ long long timecode = -1; //of cluster itself
+
+ //First count the number of entries (that remain)
+
+ long long pos = m_pos;
+ int entries_count = 0; //that remain
+
+ while (pos < cluster_stop)
+ {
+ if (Match(pReader, pos, 0x67, timecode))
+ {
+ if (m_timecode >= 0)
+ assert(timecode == m_timecode);
+ else
+ m_timecode = timecode;
+ }
+ else
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= cluster_stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO
+ assert((pos + len) <= cluster_stop);
+
+ pos += len; //consume size
+
+ if (id == 0x20) //BlockGroup ID
+ ++entries_count;
+ else if (id == 0x23) //SimpleBlock ID
+ ++entries_count;
+
+ pos += size; //consume payload
+ assert(pos <= cluster_stop);
+ }
+ }
+
+ assert(pos == cluster_stop);
+ assert(m_timecode >= 0);
+
+ if (entries_count == 0) //nothing remains to be done
+ {
+ m_pos = pos;
+
+ if (m_entries_count < 0)
+ m_entries_count = 0;
+
+ return;
+ }
+
+ BlockEntry** ppEntry;
+
+ if (m_entries_count < 0) //haven't parsed anything yet
+ {
+ assert(m_entries == NULL);
+ assert(m_entries_size == 0);
+
+ m_entries_size = entries_count;
+ m_entries = new BlockEntry*[m_entries_size];
+
+ ppEntry = m_entries;
+ m_entries_count = entries_count;
+ }
+ else
+ {
+ assert(m_entries);
+ assert(m_entries_size > 0);
+ assert(m_entries_count > 0);
+ assert(m_entries_count <= m_entries_size);
+
+ const long entries_size = m_entries_count + entries_count;
+
+ if (m_entries_size < entries_size)
+ {
+ BlockEntry** const entries = new BlockEntry*[entries_size];
+ assert(entries);
+
+ BlockEntry** src = m_entries;
+ BlockEntry** const src_end = src + m_entries_count;
+
+ BlockEntry** dst = entries;
+
+ while (src != src_end)
+ *dst++ = *src++;
+
+ delete[] m_entries;
+
+ m_entries = entries;
+ m_entries_size = entries_size;
+ }
+
+ ppEntry = m_entries + m_entries_count;
+ m_entries_count = entries_size;
+ }
+
+ while (m_pos < cluster_stop)
+ {
+ long len;
+ const long long id = ReadUInt(pReader, m_pos, len);
+ assert(id >= 0); //TODO
+ assert((m_pos + len) <= cluster_stop);
+
+ m_pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, m_pos, len);
+ assert(size >= 0); //TODO
+ assert((m_pos + len) <= cluster_stop);
+
+ m_pos += len; //consume size
+
+ if (id == 0x20) //BlockGroup ID
+ ParseBlockGroup(m_pos, size, ppEntry);
+ else if (id == 0x23) //SimpleBlock ID
+ ParseSimpleBlock(m_pos, size, ppEntry);
+
+ m_pos += size; //consume payload
+ assert(m_pos <= cluster_stop);
+ }
+
+ assert(m_pos == cluster_stop);
+ assert((ppEntry - m_entries) == m_entries_count);
+}
+
+
+
+long long Cluster::GetTimeCode() const
+{
+ Load();
+ return m_timecode;
+}
+
+
+long long Cluster::GetTime() const
+{
+ const long long tc = GetTimeCode();
+ assert(tc >= 0);
+
+ const SegmentInfo* const pInfo = m_pSegment->GetInfo();
+ assert(pInfo);
+
+ const long long scale = pInfo->GetTimeCodeScale();
+ assert(scale >= 1);
+
+ const long long t = m_timecode * scale;
+
+ return t;
+}
+
+
+long long Cluster::GetFirstTime() const
+{
+ const BlockEntry* const pEntry = GetFirst();
+
+ if (pEntry == NULL) //empty cluster
+ return GetTime();
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ return pBlock->GetTime(this);
+}
+
+
+long long Cluster::GetLastTime() const
+{
+ const BlockEntry* const pEntry = GetLast();
+
+ if (pEntry == NULL) //empty cluster
+ return GetTime();
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ return pBlock->GetTime(this);
+}
+
+
+void Cluster::ParseBlock(
+ long long id,
+ long long pos, //absolute pos of payload
+ long long size) const
+{
+ BlockEntry** ppEntry;
+
+ if (m_entries_count < 0) //haven't parsed anything yet
+ {
+ assert(m_entries == NULL);
+ assert(m_entries_size == 0);
+
+ m_entries_size = 1024;
+ m_entries = new BlockEntry*[m_entries_size];
+
+ ppEntry = m_entries;
+ m_entries_count = 1;
+ }
+ else
+ {
+ assert(m_entries);
+ assert(m_entries_size > 0);
+ assert(m_entries_count > 0);
+ assert(m_entries_count <= m_entries_size);
+
+ if (m_entries_count >= m_entries_size)
+ {
+ const long entries_size = 2 * m_entries_size;
+
+ BlockEntry** const entries = new BlockEntry*[entries_size];
+ assert(entries);
+
+ BlockEntry** src = m_entries;
+ BlockEntry** const src_end = src + m_entries_count;
+
+ BlockEntry** dst = entries;
+
+ while (src != src_end)
+ *dst++ = *src++;
+
+ delete[] m_entries;
+
+ m_entries = entries;
+ m_entries_size = entries_size;
+ }
+
+ ppEntry = m_entries + m_entries_count;
+ ++m_entries_count;
+ }
+
+ if (id == 0x20) //BlockGroup ID
+ ParseBlockGroup(pos, size, ppEntry);
+ else
+ {
+ assert(id == 0x23); //SimpleBlock ID
+ ParseSimpleBlock(pos, size, ppEntry);
+ }
+}
+
+
+void Cluster::ParseBlockGroup(
+ long long st,
+ long long sz,
+ BlockEntry**& ppEntry) const
+{
+ assert(m_entries);
+ assert(m_entries_size > 0);
+ assert(ppEntry);
+ assert(ppEntry >= m_entries);
+
+ const ptrdiff_t idx = ppEntry - m_entries;
+ assert(idx >= 0);
+ assert(idx < m_entries_size);
+
+ Cluster* const this_ = const_cast<Cluster*>(this);
+ *ppEntry++ = new BlockGroup(this_, idx, st, sz);
+}
+
+
+
+void Cluster::ParseSimpleBlock(
+ long long st,
+ long long sz,
+ BlockEntry**& ppEntry) const
+{
+ assert(m_entries);
+ assert(m_entries_size > 0);
+ assert(ppEntry);
+ assert(ppEntry >= m_entries);
+
+ const ptrdiff_t idx = ppEntry - m_entries;
+ assert(idx >= 0);
+ assert(idx < m_entries_size);
+
+ Cluster* const this_ = const_cast<Cluster*>(this);
+ *ppEntry++ = new SimpleBlock(this_, idx, st, sz);
+}
+
+
+const BlockEntry* Cluster::GetFirst() const
+{
+ LoadBlockEntries();
+
+ if ((m_entries == NULL) || (m_entries_count <= 0))
+ return NULL;
+
+ const BlockEntry* const pFirst = m_entries[0];
+ assert(pFirst);
+
+ return pFirst;
+}
+
+
+const BlockEntry* Cluster::GetLast() const
+{
+ LoadBlockEntries();
+
+ if ((m_entries == NULL) || (m_entries_count <= 0))
+ return NULL;
+
+ const long idx = m_entries_count - 1;
+
+ const BlockEntry* const pLast = m_entries[idx];
+ assert(pLast);
+
+ return pLast;
+}
+
+
+const BlockEntry* Cluster::GetNext(const BlockEntry* pEntry) const
+{
+ assert(pEntry);
+ assert(m_entries != NULL);
+ assert(m_entries_count > 0);
+
+ size_t idx = pEntry->GetIndex();
+ assert(idx < size_t(m_entries_count));
+ assert(m_entries[idx] == pEntry);
+
+ ++idx;
+
+ if (idx >= size_t(m_entries_count))
+ return NULL;
+
+ return m_entries[idx];
+}
+
+
+long Cluster::GetEntryCount() const
+{
+ return m_entries_count;
+}
+
+
+const BlockEntry* Cluster::GetEntry(
+ const Track* pTrack,
+ long long time_ns) const
+{
+ assert(pTrack);
+
+ if (m_pSegment == NULL) //this is the special EOS cluster
+ return pTrack->GetEOS();
+
+ LoadBlockEntries();
+
+ if ((m_entries == NULL) || (m_entries_count <= 0))
+ return NULL; //return EOS here?
+
+ const BlockEntry* pResult = pTrack->GetEOS();
+
+ BlockEntry** i = m_entries;
+ assert(i);
+
+ BlockEntry** const j = i + m_entries_count;
+
+ while (i != j)
+ {
+ const BlockEntry* const pEntry = *i++;
+ assert(pEntry);
+ assert(!pEntry->EOS());
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ if (pBlock->GetTrackNumber() != pTrack->GetNumber())
+ continue;
+
+ if (pTrack->VetEntry(pEntry))
+ {
+ if (time_ns < 0) //just want first candidate block
+ return pEntry;
+
+ const long long ns = pBlock->GetTime(this);
+
+ if (ns > time_ns)
+ break;
+
+ pResult = pEntry;
+ }
+ else if (time_ns >= 0)
+ {
+ const long long ns = pBlock->GetTime(this);
+
+ if (ns > time_ns)
+ break;
+ }
+ }
+
+ return pResult;
+}
+
+
+const BlockEntry*
+Cluster::GetEntry(
+ const CuePoint& cp,
+ const CuePoint::TrackPosition& tp) const
+{
+ assert(m_pSegment);
+
+ LoadBlockEntries();
+
+ if (m_entries == NULL)
+ return NULL;
+
+ const long long count = m_entries_count;
+
+ if (count <= 0)
+ return NULL;
+
+ const long long tc = cp.GetTimeCode();
+
+ if ((tp.m_block > 0) && (tp.m_block <= count))
+ {
+ const size_t block = static_cast<size_t>(tp.m_block);
+ const size_t index = block - 1;
+
+ const BlockEntry* const pEntry = m_entries[index];
+ assert(pEntry);
+ assert(!pEntry->EOS());
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ if ((pBlock->GetTrackNumber() == tp.m_track) &&
+ (pBlock->GetTimeCode(this) == tc))
+ {
+ return pEntry;
+ }
+ }
+
+ const BlockEntry* const* i = m_entries;
+ const BlockEntry* const* const j = i + count;
+
+ while (i != j)
+ {
+#ifdef _DEBUG
+ const ptrdiff_t idx = i - m_entries;
+ idx;
+#endif
+
+ const BlockEntry* const pEntry = *i++;
+ assert(pEntry);
+ assert(!pEntry->EOS());
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ if (pBlock->GetTrackNumber() != tp.m_track)
+ continue;
+
+ const long long tc_ = pBlock->GetTimeCode(this);
+ assert(tc_ >= 0);
+
+ if (tc_ < tc)
+ continue;
+
+ if (tc_ > tc)
+ return NULL;
+
+ const Tracks* const pTracks = m_pSegment->GetTracks();
+ assert(pTracks);
+
+ const long tn = static_cast<long>(tp.m_track);
+ const Track* const pTrack = pTracks->GetTrackByNumber(tn);
+
+ if (pTrack == NULL)
+ return NULL;
+
+ const long long type = pTrack->GetType();
+
+ if (type == 2) //audio
+ return pEntry;
+
+ if (type != 1) //not video
+ return NULL;
+
+ if (!pBlock->IsKey())
+ return NULL;
+
+ return pEntry;
+ }
+
+ return NULL;
+}
+
+
+const BlockEntry* Cluster::GetMaxKey(const VideoTrack* pTrack) const
+{
+ assert(pTrack);
+
+ if (m_pSegment == NULL) //EOS
+ return pTrack->GetEOS();
+
+ LoadBlockEntries();
+
+ if ((m_entries == NULL) || (m_entries_count <= 0))
+ return pTrack->GetEOS();
+
+ BlockEntry** i = m_entries + m_entries_count;
+ BlockEntry** const j = m_entries;
+
+ while (i != j)
+ {
+ const BlockEntry* const pEntry = *--i;
+ assert(pEntry);
+ assert(!pEntry->EOS());
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ if (pBlock->GetTrackNumber() != pTrack->GetNumber())
+ continue;
+
+ if (pBlock->IsKey())
+ return pEntry;
+ }
+
+ return pTrack->GetEOS(); //no satisfactory block found
+}
+
+
+
+BlockEntry::BlockEntry()
+{
+}
+
+
+BlockEntry::~BlockEntry()
+{
+}
+
+
+SimpleBlock::SimpleBlock(
+ Cluster* pCluster,
+ size_t idx,
+ long long start,
+ long long size) :
+ m_pCluster(pCluster),
+ m_index(idx),
+ m_block(start, size, pCluster->m_pSegment->m_pReader)
+{
+}
+
+
+bool SimpleBlock::EOS() const
+{
+ return false;
+}
+
+
+const Cluster* SimpleBlock::GetCluster() const
+{
+ return m_pCluster;
+}
+
+
+size_t SimpleBlock::GetIndex() const
+{
+ return m_index;
+}
+
+
+const Block* SimpleBlock::GetBlock() const
+{
+ return &m_block;
+}
+
+
+//bool SimpleBlock::IsBFrame() const
+//{
+// return false;
+//}
+
+
+BlockGroup::BlockGroup(
+ Cluster* pCluster,
+ size_t idx,
+ long long start,
+ long long size_) :
+ m_pCluster(pCluster),
+ m_index(idx),
+ m_prevTimeCode(0),
+ m_nextTimeCode(0),
+ m_pBlock(NULL) //TODO: accept multiple blocks within a block group
+{
+ IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
+
+ long long pos = start;
+ const long long stop = start + size_;
+
+ bool bSimpleBlock = false;
+ bool bReferenceBlock = false;
+
+ while (pos < stop)
+ {
+ short t;
+
+ if (Match(pReader, pos, 0x7B, t))
+ {
+ if (t < 0)
+ m_prevTimeCode = t;
+ else if (t > 0)
+ m_nextTimeCode = t;
+ else
+ assert(false);
+
+ bReferenceBlock = true;
+ }
+ else
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume size
+
+ switch (id)
+ {
+ case 0x23: //SimpleBlock ID
+ bSimpleBlock = true;
+ //YES, FALL THROUGH TO NEXT CASE
+
+ case 0x21: //Block ID
+ ParseBlock(pos, size);
+ break;
+
+ default:
+ break;
+ }
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+ }
+
+ assert(pos == stop);
+ assert(m_pBlock);
+
+ if (!bSimpleBlock)
+ m_pBlock->SetKey(!bReferenceBlock);
+}
+
+
+BlockGroup::~BlockGroup()
+{
+ delete m_pBlock;
+}
+
+
+void BlockGroup::ParseBlock(long long start, long long size)
+{
+ IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
+
+ Block* const pBlock = new Block(start, size, pReader);
+ assert(pBlock); //TODO
+
+ //TODO: the Matroska spec says you have multiple blocks within the
+ //same block group, with blocks ranked by priority (the flag bits).
+
+ assert(m_pBlock == NULL);
+ m_pBlock = pBlock;
+}
+
+
+bool BlockGroup::EOS() const
+{
+ return false;
+}
+
+
+const Cluster* BlockGroup::GetCluster() const
+{
+ return m_pCluster;
+}
+
+
+size_t BlockGroup::GetIndex() const
+{
+ return m_index;
+}
+
+
+const Block* BlockGroup::GetBlock() const
+{
+ return m_pBlock;
+}
+
+
+short BlockGroup::GetPrevTimeCode() const
+{
+ return m_prevTimeCode;
+}
+
+
+short BlockGroup::GetNextTimeCode() const
+{
+ return m_nextTimeCode;
+}
+
+
+//bool BlockGroup::IsBFrame() const
+//{
+// return (m_nextTimeCode > 0);
+//}
+
+
+Block::Block(long long start, long long size_, IMkvReader* pReader) :
+ m_start(start),
+ m_size(size_)
+{
+ long long pos = start;
+ const long long stop = start + size_;
+
+ long len;
+
+ m_track = ReadUInt(pReader, pos, len);
+ assert(m_track > 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume track number
+ assert((stop - pos) >= 2);
+
+ m_timecode = Unserialize2SInt(pReader, pos);
+
+ pos += 2;
+ assert((stop - pos) >= 1);
+
+ long status = pReader->Read(pos, 1, &m_flags);
+ assert(status == 0);
+
+#if 0
+ const int invisible = int(m_flags & 0x08) >> 3;
+ invisible;
+ assert(!invisible); //TODO
+#endif
+
+ const int lacing = int(m_flags & 0x06) >> 1;
+
+ ++pos; //consume flags byte
+ assert(pos <= stop);
+
+ if (lacing == 0) //no lacing
+ {
+ m_frame_count = 1;
+ m_frames = new Frame[m_frame_count];
+
+ Frame& f = m_frames[0];
+ f.pos = pos;
+
+ const long long frame_size = stop - pos;
+ assert(frame_size <= LONG_MAX);
+
+ f.len = static_cast<long>(frame_size);
+
+ return;
+ }
+
+ assert(pos < stop);
+
+ unsigned char count;
+
+ status = pReader->Read(pos, 1, &count);
+ assert(status == 0);
+
+ ++pos; //consume frame count
+ assert(pos <= stop);
+
+ m_frame_count = ++count;
+ m_frames = new Frame[m_frame_count];
+
+ if (lacing == 1) //Xiph
+ {
+ Frame* pf = m_frames;
+ Frame* const pf_end = pf + m_frame_count;
+
+ long size = 0;
+
+ while (count > 1)
+ {
+ long frame_size = 0;
+
+ for (;;)
+ {
+ unsigned char val;
+
+ status = pReader->Read(pos, 1, &val);
+ assert(status == 0);
+
+ ++pos; //consume xiph size byte
+
+ frame_size += val;
+
+ if (val < 255)
+ break;
+ }
+
+ Frame& f = *pf++;
+ assert(pf < pf_end);
+
+ f.len = frame_size;
+ size += frame_size; //contribution of this frame
+
+ --count;
+ }
+
+ assert(pf < pf_end);
+ assert(pos < stop);
+
+ {
+ Frame& f = *pf++;
+ assert(pf == pf_end);
+
+ const long long total_size = stop - pos;
+ assert(total_size > size);
+
+ const long long frame_size = total_size - size;
+ assert(frame_size <= LONG_MAX);
+
+ f.len = static_cast<long>(frame_size);
+ }
+
+ pf = m_frames;
+ while (pf != pf_end)
+ {
+ Frame& f = *pf++;
+ assert((pos + f.len) <= stop);
+
+ f.pos = pos;
+ pos += f.len;
+ }
+
+ assert(pos == stop);
+ }
+ else if (lacing == 2) //fixed-size lacing
+ {
+ const long long total_size = stop - pos;
+ assert((total_size % m_frame_count) == 0);
+
+ const long long frame_size = total_size / m_frame_count;
+ assert(frame_size <= LONG_MAX);
+
+ Frame* pf = m_frames;
+ Frame* const pf_end = pf + m_frame_count;
+
+ while (pf != pf_end)
+ {
+ assert((pos + frame_size) <= stop);
+
+ Frame& f = *pf++;
+
+ f.pos = pos;
+ f.len = static_cast<long>(frame_size);
+
+ pos += frame_size;
+ }
+
+ assert(pos == stop);
+ }
+ else
+ {
+ assert(lacing == 3); //EBML lacing
+ assert(pos < stop);
+
+ long size = 0;
+
+ long long frame_size = ReadUInt(pReader, pos, len);
+ assert(frame_size > 0);
+ assert(frame_size <= LONG_MAX);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size of first frame
+ assert((pos + frame_size) <= stop);
+
+ Frame* pf = m_frames;
+ Frame* const pf_end = pf + m_frame_count;
+
+ {
+ Frame& curr = *pf;
+
+ curr.len = static_cast<long>(frame_size);
+ size += curr.len; //contribution of this frame
+ }
+
+ --count;
+
+ while (count > 1)
+ {
+ assert(pos < stop);
+ assert(pf < pf_end);
+
+ const Frame& prev = *pf++;
+ assert(pf < pf_end);
+ assert(prev.len == frame_size);
+
+ Frame& curr = *pf;
+
+ const long long delta_size_ = ReadUInt(pReader, pos, len);
+ assert(delta_size_ >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of (delta) size
+ assert(pos <= stop);
+
+ const int exp = 7*len - 1;
+ const long long bias = (1LL << exp) - 1LL;
+ const long long delta_size = delta_size_ - bias;
+
+ frame_size += delta_size;
+ assert(frame_size > 0);
+ assert(frame_size <= LONG_MAX);
+
+ curr.len = static_cast<long>(frame_size);
+ size += curr.len; //contribution of this frame
+
+ --count;
+ }
+
+ {
+ assert(pos < stop);
+ assert(pf < pf_end);
+
+ const Frame& prev = *pf++;
+ assert(pf < pf_end);
+ assert(prev.len == frame_size);
+
+ Frame& curr = *pf++;
+ assert(pf == pf_end);
+
+ const long long total_size = stop - pos;
+ assert(total_size > 0);
+ assert(total_size > size);
+
+ frame_size = total_size - size;
+ assert(frame_size > 0);
+ assert(frame_size <= LONG_MAX);
+
+ curr.len = static_cast<long>(frame_size);
+ }
+
+ pf = m_frames;
+ while (pf != pf_end)
+ {
+ Frame& f = *pf++;
+ assert((pos + f.len) <= stop);
+
+ f.pos = pos;
+ pos += f.len;
+ }
+
+ assert(pos == stop);
+ }
+}
+
+
+Block::~Block()
+{
+ delete[] m_frames;
+}
+
+
+long long Block::GetTimeCode(const Cluster* pCluster) const
+{
+ assert(pCluster);
+
+ const long long tc0 = pCluster->GetTimeCode();
+ assert(tc0 >= 0);
+
+ const long long tc = tc0 + static_cast<long long>(m_timecode);
+ assert(tc >= 0);
+
+ return tc; //unscaled timecode units
+}
+
+
+long long Block::GetTime(const Cluster* pCluster) const
+{
+ assert(pCluster);
+
+ const long long tc = GetTimeCode(pCluster);
+
+ const Segment* const pSegment = pCluster->m_pSegment;
+ const SegmentInfo* const pInfo = pSegment->GetInfo();
+ assert(pInfo);
+
+ const long long scale = pInfo->GetTimeCodeScale();
+ assert(scale >= 1);
+
+ const long long ns = tc * scale;
+
+ return ns;
+}
+
+
+long long Block::GetTrackNumber() const
+{
+ return m_track;
+}
+
+
+bool Block::IsKey() const
+{
+ return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
+}
+
+
+void Block::SetKey(bool bKey)
+{
+ if (bKey)
+ m_flags |= static_cast<unsigned char>(1 << 7);
+ else
+ m_flags &= 0x7F;
+}
+
+
+bool Block::IsInvisible() const
+{
+ return bool(int(m_flags & 0x08) != 0);
+}
+
+
+int Block::GetFrameCount() const
+{
+ return m_frame_count;
+}
+
+
+const Block::Frame& Block::GetFrame(int idx) const
+{
+ assert(idx >= 0);
+ assert(idx < m_frame_count);
+
+ const Frame& f = m_frames[idx];
+ assert(f.pos > 0);
+ assert(f.len > 0);
+
+ return f;
+}
+
+
+long Block::Frame::Read(IMkvReader* pReader, unsigned char* buf) const
+{
+ assert(pReader);
+ assert(buf);
+
+ const long status = pReader->Read(pos, len, buf);
+ return status;
+}
+
+
+} //end namespace mkvparser
diff --git a/engines/sludge/libwebm/mkvparser.hpp b/engines/sludge/libwebm/mkvparser.hpp
new file mode 100644
index 0000000000..1ebfbaf361
--- /dev/null
+++ b/engines/sludge/libwebm/mkvparser.hpp
@@ -0,0 +1,729 @@
+// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+#ifndef MKVPARSER_HPP
+#define MKVPARSER_HPP
+
+#include <cstdlib>
+#include <cstdio>
+#include <cstddef>
+
+namespace mkvparser
+{
+
+const int E_FILE_FORMAT_INVALID = -2;
+const int E_BUFFER_NOT_FULL = -3;
+
+class IMkvReader
+{
+public:
+ virtual int Read(long long pos, long len, unsigned char* buf) = 0;
+ virtual int Length(long long* total, long long* available) = 0;
+protected:
+ virtual ~IMkvReader();
+};
+
+long long GetUIntLength(IMkvReader*, long long, long&);
+long long ReadUInt(IMkvReader*, long long, long&);
+long long SyncReadUInt(IMkvReader*, long long pos, long long stop, long&);
+long long UnserializeUInt(IMkvReader*, long long pos, long long size);
+float Unserialize4Float(IMkvReader*, long long);
+double Unserialize8Double(IMkvReader*, long long);
+short Unserialize2SInt(IMkvReader*, long long);
+signed char Unserialize1SInt(IMkvReader*, long long);
+bool Match(IMkvReader*, long long&, unsigned long, long long&);
+bool Match(IMkvReader*, long long&, unsigned long, char*&);
+bool Match(IMkvReader*, long long&, unsigned long, unsigned char*&, size_t&);
+bool Match(IMkvReader*, long long&, unsigned long, double&);
+bool Match(IMkvReader*, long long&, unsigned long, short&);
+
+void GetVersion(int& major, int& minor, int& build, int& revision);
+
+struct EBMLHeader
+{
+ EBMLHeader();
+ ~EBMLHeader();
+ long long m_version;
+ long long m_readVersion;
+ long long m_maxIdLength;
+ long long m_maxSizeLength;
+ char* m_docType;
+ long long m_docTypeVersion;
+ long long m_docTypeReadVersion;
+
+ long long Parse(IMkvReader*, long long&);
+ void Init();
+};
+
+
+class Segment;
+class Track;
+class Cluster;
+
+class Block
+{
+ Block(const Block&);
+ Block& operator=(const Block&);
+
+public:
+ const long long m_start;
+ const long long m_size;
+
+ Block(long long start, long long size, IMkvReader*);
+ ~Block();
+
+ long long GetTrackNumber() const;
+ long long GetTimeCode(const Cluster*) const; //absolute, but not scaled
+ long long GetTime(const Cluster*) const; //absolute, and scaled (ns)
+ bool IsKey() const;
+ void SetKey(bool);
+ bool IsInvisible() const;
+
+ int GetFrameCount() const; //to index frames: [0, count)
+
+ struct Frame
+ {
+ long long pos; //absolute offset
+ long len;
+
+ long Read(IMkvReader*, unsigned char*) const;
+ };
+
+ const Frame& GetFrame(int frame_index) const;
+
+private:
+ long long m_track; //Track::Number()
+ short m_timecode; //relative to cluster
+ unsigned char m_flags;
+
+ Frame* m_frames;
+ int m_frame_count;
+
+};
+
+
+class BlockEntry
+{
+ BlockEntry(const BlockEntry&);
+ BlockEntry& operator=(const BlockEntry&);
+
+public:
+ virtual ~BlockEntry();
+ virtual bool EOS() const = 0;
+ virtual const Cluster* GetCluster() const = 0;
+ virtual size_t GetIndex() const = 0;
+ virtual const Block* GetBlock() const = 0;
+ //virtual bool IsBFrame() const = 0;
+
+protected:
+ BlockEntry();
+
+};
+
+
+class SimpleBlock : public BlockEntry
+{
+ SimpleBlock(const SimpleBlock&);
+ SimpleBlock& operator=(const SimpleBlock&);
+
+public:
+ SimpleBlock(Cluster*, size_t, long long start, long long size);
+
+ bool EOS() const;
+ const Cluster* GetCluster() const;
+ size_t GetIndex() const;
+ const Block* GetBlock() const;
+ //bool IsBFrame() const;
+
+protected:
+ Cluster* const m_pCluster;
+ const size_t m_index;
+ Block m_block;
+
+};
+
+
+class BlockGroup : public BlockEntry
+{
+ BlockGroup(const BlockGroup&);
+ BlockGroup& operator=(const BlockGroup&);
+
+public:
+ BlockGroup(Cluster*, size_t, long long, long long);
+ ~BlockGroup();
+
+ bool EOS() const;
+ const Cluster* GetCluster() const;
+ size_t GetIndex() const;
+ const Block* GetBlock() const;
+ //bool IsBFrame() const;
+
+ short GetPrevTimeCode() const; //relative to block's time
+ short GetNextTimeCode() const; //as above
+
+protected:
+ Cluster* const m_pCluster;
+ const size_t m_index;
+
+private:
+ BlockGroup(Cluster*, size_t, unsigned long);
+ void ParseBlock(long long start, long long size);
+
+ short m_prevTimeCode;
+ short m_nextTimeCode;
+
+ //TODO: the Matroska spec says you can have multiple blocks within the
+ //same block group, with blocks ranked by priority (the flag bits).
+ //For now we just cache a single block.
+#if 0
+ typedef std::deque<Block*> blocks_t;
+ blocks_t m_blocks; //In practice should contain only a single element.
+#else
+ Block* m_pBlock;
+#endif
+
+};
+
+
+class Track
+{
+ Track(const Track&);
+ Track& operator=(const Track&);
+
+public:
+ Segment* const m_pSegment;
+ const long long m_element_start;
+ const long long m_element_size;
+ virtual ~Track();
+
+ long long GetType() const;
+ long long GetNumber() const;
+ unsigned long long GetUid() const;
+ const char* GetNameAsUTF8() const;
+ const char* GetCodecNameAsUTF8() const;
+ const char* GetCodecId() const;
+ const unsigned char* GetCodecPrivate(size_t&) const;
+ bool GetLacing() const;
+
+ const BlockEntry* GetEOS() const;
+
+ struct Settings
+ {
+ long long start;
+ long long size;
+ };
+
+ struct Info
+ {
+ long long type;
+ long long number;
+ unsigned long long uid;
+ char* nameAsUTF8;
+ char* codecId;
+ unsigned char* codecPrivate;
+ size_t codecPrivateSize;
+ char* codecNameAsUTF8;
+ bool lacing;
+ Settings settings;
+
+ Info();
+ void Clear();
+ };
+
+ long GetFirst(const BlockEntry*&) const;
+ long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;
+ virtual bool VetEntry(const BlockEntry*) const = 0;
+ virtual long Seek(long long time_ns, const BlockEntry*&) const = 0;
+
+protected:
+ Track(
+ Segment*,
+ const Info&,
+ long long element_start,
+ long long element_size);
+ const Info m_info;
+
+ class EOSBlock : public BlockEntry
+ {
+ public:
+ EOSBlock();
+
+ bool EOS() const;
+ const Cluster* GetCluster() const;
+ size_t GetIndex() const;
+ const Block* GetBlock() const;
+ bool IsBFrame() const;
+ };
+
+ EOSBlock m_eos;
+
+};
+
+
+class VideoTrack : public Track
+{
+ VideoTrack(const VideoTrack&);
+ VideoTrack& operator=(const VideoTrack&);
+
+public:
+ VideoTrack(
+ Segment*,
+ const Info&,
+ long long element_start,
+ long long element_size);
+ long long GetWidth() const;
+ long long GetHeight() const;
+ double GetFrameRate() const;
+
+ bool VetEntry(const BlockEntry*) const;
+ long Seek(long long time_ns, const BlockEntry*&) const;
+
+private:
+ long long m_width;
+ long long m_height;
+ double m_rate;
+
+};
+
+
+class AudioTrack : public Track
+{
+ AudioTrack(const AudioTrack&);
+ AudioTrack& operator=(const AudioTrack&);
+
+public:
+ AudioTrack(
+ Segment*,
+ const Info&,
+ long long element_start,
+ long long element_size);
+ double GetSamplingRate() const;
+ long long GetChannels() const;
+ long long GetBitDepth() const;
+ bool VetEntry(const BlockEntry*) const;
+ long Seek(long long time_ns, const BlockEntry*&) const;
+
+private:
+ double m_rate;
+ long long m_channels;
+ long long m_bitDepth;
+};
+
+
+class Tracks
+{
+ Tracks(const Tracks&);
+ Tracks& operator=(const Tracks&);
+
+public:
+ Segment* const m_pSegment;
+ const long long m_start;
+ const long long m_size;
+ const long long m_element_start;
+ const long long m_element_size;
+
+ Tracks(
+ Segment*,
+ long long start,
+ long long size,
+ long long element_start,
+ long long element_size);
+ virtual ~Tracks();
+
+ const Track* GetTrackByNumber(unsigned long tn) const;
+ const Track* GetTrackByIndex(unsigned long idx) const;
+
+private:
+ Track** m_trackEntries;
+ Track** m_trackEntriesEnd;
+
+ void ParseTrackEntry(
+ long long,
+ long long,
+ Track*&,
+ long long element_start,
+ long long element_size);
+
+public:
+ unsigned long GetTracksCount() const;
+};
+
+
+class SegmentInfo
+{
+ SegmentInfo(const SegmentInfo&);
+ SegmentInfo& operator=(const SegmentInfo&);
+
+public:
+ Segment* const m_pSegment;
+ const long long m_start;
+ const long long m_size;
+ const long long m_element_start;
+ const long long m_element_size;
+
+ SegmentInfo(
+ Segment*,
+ long long start,
+ long long size,
+ long long element_start,
+ long long element_size);
+
+ ~SegmentInfo();
+
+ long long GetTimeCodeScale() const;
+ long long GetDuration() const; //scaled
+ const char* GetMuxingAppAsUTF8() const;
+ const char* GetWritingAppAsUTF8() const;
+ const char* GetTitleAsUTF8() const;
+
+private:
+ long long m_timecodeScale;
+ double m_duration;
+ char* m_pMuxingAppAsUTF8;
+ char* m_pWritingAppAsUTF8;
+ char* m_pTitleAsUTF8;
+};
+
+
+class SeekHead
+{
+ SeekHead(const SeekHead&);
+ SeekHead& operator=(const SeekHead&);
+
+public:
+ Segment* const m_pSegment;
+ const long long m_start;
+ const long long m_size;
+ const long long m_element_start;
+ const long long m_element_size;
+
+ SeekHead(
+ Segment*,
+ long long start,
+ long long size,
+ long long element_start,
+ long long element_size);
+
+ ~SeekHead();
+
+ struct Entry
+ {
+ long long id;
+ long long pos;
+ };
+
+ int GetCount() const;
+ const Entry* GetEntry(int idx) const;
+
+private:
+ Entry* m_entries;
+ int m_count;
+
+ static void ParseEntry(
+ IMkvReader*,
+ long long pos,
+ long long size,
+ Entry*&);
+
+};
+
+class Cues;
+class CuePoint
+{
+ friend class Cues;
+
+ CuePoint(size_t, long long);
+ ~CuePoint();
+
+ CuePoint(const CuePoint&);
+ CuePoint& operator=(const CuePoint&);
+
+public:
+ long long m_element_start;
+ long long m_element_size;
+
+ void Load(IMkvReader*);
+
+ long long GetTimeCode() const; //absolute but unscaled
+ long long GetTime(const Segment*) const; //absolute and scaled (ns units)
+
+ struct TrackPosition
+ {
+ long long m_track;
+ long long m_pos; //of cluster
+ long long m_block;
+ //codec_state //defaults to 0
+ //reference = clusters containing req'd referenced blocks
+ // reftime = timecode of the referenced block
+
+ void Parse(IMkvReader*, long long, long long);
+ };
+
+ const TrackPosition* Find(const Track*) const;
+
+private:
+ const size_t m_index;
+ long long m_timecode;
+ TrackPosition* m_track_positions;
+ size_t m_track_positions_count;
+
+};
+
+
+class Cues
+{
+ friend class Segment;
+
+ Cues(
+ Segment*,
+ long long start,
+ long long size,
+ long long element_start,
+ long long element_size);
+ ~Cues();
+
+ Cues(const Cues&);
+ Cues& operator=(const Cues&);
+
+public:
+ Segment* const m_pSegment;
+ const long long m_start;
+ const long long m_size;
+ const long long m_element_start;
+ const long long m_element_size;
+
+ bool Find( //lower bound of time_ns
+ long long time_ns,
+ const Track*,
+ const CuePoint*&,
+ const CuePoint::TrackPosition*&) const;
+
+#if 0
+ bool FindNext( //upper_bound of time_ns
+ long long time_ns,
+ const Track*,
+ const CuePoint*&,
+ const CuePoint::TrackPosition*&) const;
+#endif
+
+ const CuePoint* GetFirst() const;
+ const CuePoint* GetLast() const;
+ const CuePoint* GetNext(const CuePoint*) const;
+
+ const BlockEntry* GetBlock(
+ const CuePoint*,
+ const CuePoint::TrackPosition*) const;
+
+ bool LoadCuePoint() const;
+ long GetCount() const; //loaded only
+ //long GetTotal() const; //loaded + preloaded
+ bool DoneParsing() const;
+
+private:
+ void Init() const;
+ void PreloadCuePoint(size_t&, long long) const;
+
+ mutable CuePoint** m_cue_points;
+ mutable size_t m_count;
+ mutable size_t m_preload_count;
+ mutable long long m_pos;
+
+};
+
+
+class Cluster
+{
+ friend class Segment;
+
+ Cluster(const Cluster&);
+ Cluster& operator=(const Cluster&);
+
+public:
+ Segment* const m_pSegment;
+
+public:
+ static Cluster* Create(
+ Segment*,
+ long index, //index in segment
+ long long off, //offset relative to segment
+ long long element_size);
+
+ Cluster(); //EndOfStream
+ ~Cluster();
+
+ bool EOS() const;
+
+ long long GetTimeCode() const; //absolute, but not scaled
+ long long GetTime() const; //absolute, and scaled (nanosecond units)
+ long long GetFirstTime() const; //time (ns) of first (earliest) block
+ long long GetLastTime() const; //time (ns) of last (latest) block
+
+ const BlockEntry* GetFirst() const;
+ const BlockEntry* GetLast() const;
+ const BlockEntry* GetNext(const BlockEntry*) const;
+ const BlockEntry* GetEntry(const Track*, long long ns = -1) const;
+ const BlockEntry* GetEntry(
+ const CuePoint&,
+ const CuePoint::TrackPosition&) const;
+ const BlockEntry* GetMaxKey(const VideoTrack*) const;
+
+// static bool HasBlockEntries(const Segment*, long long);
+
+ static long HasBlockEntries(
+ const Segment*,
+ long long idoff,
+ long long& pos,
+ long& size);
+
+ long GetEntryCount() const;
+
+ void Load() const;
+ long Load(long long& pos, long& size) const;
+
+ void LoadBlockEntries() const;
+
+ long Parse(long long& pos, long& size) const;
+ long GetEntry(long index, const mkvparser::BlockEntry*&) const;
+
+protected:
+ Cluster(
+ Segment*,
+ long index,
+ //long long off,
+ long long element_start,
+ long long element_size);
+
+public:
+ const long long m_element_start;
+ long long GetPosition() const; //offset relative to segment
+
+ long GetIndex() const;
+ long long GetElementSize() const;
+ //long long GetPayloadSize() const;
+
+ long long Unparsed() const;
+
+private:
+ long m_index;
+ mutable long long m_pos;
+ mutable long long m_size;
+ mutable long long m_element_size;
+ mutable long long m_timecode;
+ mutable BlockEntry** m_entries;
+ mutable long m_entries_size;
+ mutable long m_entries_count;
+
+ void ParseBlock(long long id, long long pos, long long size) const;
+ void ParseBlockGroup(long long, long long, BlockEntry**&) const;
+ void ParseSimpleBlock(long long, long long, BlockEntry**&) const;
+
+};
+
+
+class Segment
+{
+ friend class Cues;
+ friend class VideoTrack;
+ friend class AudioTrack;
+
+ Segment(const Segment&);
+ Segment& operator=(const Segment&);
+
+private:
+ Segment(IMkvReader*, long long pos, long long size);
+
+public:
+ IMkvReader* const m_pReader;
+ const long long m_start; //posn of segment payload
+ const long long m_size; //size of segment payload
+ Cluster m_eos; //TODO: make private?
+
+ static long long CreateInstance(IMkvReader*, long long, Segment*&);
+ ~Segment();
+
+ long Load(); //loads headers and all clusters
+
+ //for incremental loading
+ long long Unparsed() const;
+ long long ParseHeaders(); //stops when first cluster is found
+ //long FindNextCluster(long long& pos, long& size) const;
+ long LoadCluster(long long& pos, long& size); //load one cluster
+ long LoadCluster();
+
+ long ParseNext(
+ const Cluster* pCurr,
+ const Cluster*& pNext,
+ long long& pos,
+ long& size);
+
+#if 0
+ //This pair parses one cluster, but only changes the state of the
+ //segment object when the cluster is actually added to the index.
+ long ParseCluster(long long& cluster_pos, long long& new_pos) const;
+ bool AddCluster(long long cluster_pos, long long new_pos);
+#endif
+
+ const SeekHead* GetSeekHead() const;
+ const Tracks* GetTracks() const;
+ const SegmentInfo* GetInfo() const;
+ const Cues* GetCues() const;
+
+ long long GetDuration() const;
+
+ unsigned long GetCount() const;
+ const Cluster* GetFirst() const;
+ const Cluster* GetLast() const;
+ const Cluster* GetNext(const Cluster*);
+
+ const Cluster* FindCluster(long long time_nanoseconds) const;
+ //const BlockEntry* Seek(long long time_nanoseconds, const Track*) const;
+
+ const Cluster* FindOrPreloadCluster(long long pos);
+
+ long ParseCues(
+ long long cues_off, //offset relative to start of segment
+ long long& parse_pos,
+ long& parse_len);
+
+private:
+
+ long long m_pos; //absolute file posn; what has been consumed so far
+
+ SeekHead* m_pSeekHead;
+ SegmentInfo* m_pInfo;
+ Tracks* m_pTracks;
+ Cues* m_pCues;
+ Cluster** m_clusters;
+ long m_clusterCount; //number of entries for which m_index >= 0
+ long m_clusterPreloadCount; //number of entries for which m_index < 0
+ long m_clusterSize; //array size
+
+ void AppendCluster(Cluster*);
+ void PreloadCluster(Cluster*, ptrdiff_t);
+
+ //void ParseSeekHead(long long pos, long long size);
+ //void ParseSeekEntry(long long pos, long long size);
+ //void ParseCues(long long);
+
+ const BlockEntry* GetBlock(
+ const CuePoint&,
+ const CuePoint::TrackPosition&);
+
+};
+
+} //end namespace mkvparser
+
+inline long mkvparser::Segment::LoadCluster()
+{
+ long long pos;
+ long size;
+
+ return LoadCluster(pos, size);
+}
+
+#endif //MKVPARSER_HPP
diff --git a/engines/sludge/libwebm/mkvreader.cpp b/engines/sludge/libwebm/mkvreader.cpp
new file mode 100644
index 0000000000..5ead48718b
--- /dev/null
+++ b/engines/sludge/libwebm/mkvreader.cpp
@@ -0,0 +1,118 @@
+// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+// Modified by Rikard Peterson 2011 to fit in the SLUDGE engine.
+
+#include "mkvreader.hpp"
+#include "../newfatal.h"
+#include "../fileset.h"
+
+#include <cassert>
+#include <stdio.h>
+
+MkvReader::MkvReader() :
+ m_file(0)
+{
+}
+
+MkvReader::~MkvReader()
+{
+ Close();
+}
+
+int MkvReader::Open(int fileNumber)
+{
+ if (! fileNumber)
+ return -1;
+
+ if (m_file)
+ return -1;
+
+ m_file = fileNumber;
+
+ setResourceForFatal (fileNumber);
+ m_length = openFileFromNum (fileNumber);
+ if (m_length == 0) {
+ finishAccess();
+ setResourceForFatal (-1);
+ return -1;
+ }
+ /*
+#ifdef WIN32
+ m_start = _ftelli64(bigDataFile);
+#else*/
+ m_start = ftell(bigDataFile);
+/*#endif
+*/
+ finishAccess();
+ return 0;
+}
+
+void MkvReader::Close()
+{
+ if (m_file)
+ {
+ finishAccess();
+ setResourceForFatal (-1);
+ m_file = 0;
+ }
+}
+
+int MkvReader::Length(long long* total, long long* available)
+{
+ if (! m_file)
+ return -1;
+
+ if (total)
+ *total = m_length;
+
+ if (available)
+ *available = m_length;
+
+ return 0;
+}
+
+int MkvReader::Read(long long offset, long len, unsigned char* buffer)
+{
+ if (! m_file)
+ return -1;
+
+ if (offset < 0)
+ return -1;
+
+ if (len < 0)
+ return -1;
+
+ if (len == 0)
+ return 0;
+
+
+ if (offset >= m_length)
+ return -1;
+
+ if (startAccess())
+ fprintf(stderr, "Warning: Datafile already in use when playing movie!\n");
+/*
+#ifdef WIN32
+ const int status = _fseeki64(bigDataFile, m_start+offset, SEEK_SET);
+
+ if (status)
+ return -1; //error
+#else*/
+ fseek(bigDataFile, m_start+offset, SEEK_SET);
+//#endif
+
+ const size_t size = fread(buffer, 1, len, bigDataFile);
+
+ finishAccess();
+
+ if (size < size_t(len))
+ return -1; //error
+
+ return 0; //success
+}
diff --git a/engines/sludge/libwebm/mkvreader.hpp b/engines/sludge/libwebm/mkvreader.hpp
new file mode 100644
index 0000000000..6be9e16aac
--- /dev/null
+++ b/engines/sludge/libwebm/mkvreader.hpp
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+
+// Modified by Rikard Peterson 2011 to fit in the SLUDGE engine.
+
+#ifndef MKVREADER_HPP
+#define MKVREADER_HPP
+
+#include "mkvparser.hpp"
+#include <cstdio>
+
+class MkvReader : public mkvparser::IMkvReader
+{
+ MkvReader(const MkvReader&);
+ MkvReader& operator=(const MkvReader&);
+public:
+ MkvReader();
+ virtual ~MkvReader();
+
+ int Open(int fileNumber);
+ void Close();
+ bool IsOpen() const;
+
+ virtual int Read(long long position, long length, unsigned char* buffer);
+ virtual int Length(long long* total, long long* available);
+private:
+ long long m_length;
+ unsigned int m_start;
+ int m_file;
+};
+
+#endif //MKVREADER_HPP
diff --git a/engines/sludge/line.cpp b/engines/sludge/line.cpp
new file mode 100644
index 0000000000..f57bb642a3
--- /dev/null
+++ b/engines/sludge/line.cpp
@@ -0,0 +1,143 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#if 0
+#include <stdlib.h>
+#endif
+
+#include "graphics.h"
+#include "allfiles.h"
+
+extern int sceneWidth, sceneHeight;
+#if 0
+extern GLuint backdropTextureName;
+#endif
+
+void drawLine(int x1, int y1, int x2, int y2) {
+ int x, y;
+ bool backwards = false;
+#if 0
+ if (x1 < 0) x1 = 0;
+ if (y1 < 0) y1 = 0;
+ if (x2 < 0) x2 = 0;
+ if (y2 < 0) y2 = 0;
+ if (x1 > sceneWidth) x1 = sceneWidth - 1;
+ if (x2 > sceneWidth) x2 = sceneWidth - 1;
+ if (y1 > sceneHeight) y1 = sceneHeight - 1;
+ if (y2 > sceneHeight) y2 = sceneHeight - 1;
+
+ if (x1 > x2) {
+ x = x2;
+ backwards = !backwards;
+ } else x = x1;
+
+ if (y1 > y2) {
+ y = y2;
+ backwards = !backwards;
+ } else y = y1;
+
+ int diffX = abs(x2 - x1);
+ int diffY = abs(y2 - y1);
+
+ if (! diffX) {
+ diffX = 1;
+ if (x == sceneWidth - 1) x = sceneWidth - 2;
+ }
+ if (! diffY) {
+ diffY = 1;
+ if (y == sceneHeight - 1) y = sceneHeight - 2;
+ }
+ setPixelCoords(true);
+
+
+ glLineWidth(2.0);
+
+
+ int xoffset = 0;
+ while (xoffset < diffX) {
+ int w = (diffX - xoffset < viewportWidth) ? diffX - xoffset : viewportWidth;
+
+ int yoffset = 0;
+ while (yoffset < diffY) {
+ int h = (diffY - yoffset < viewportHeight) ? diffY - yoffset : viewportHeight;
+
+ // Render the scene - first the old backdrop
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ const GLfloat vertices[] = {
+ (GLfloat) - x - xoffset, (GLfloat)1 - y - yoffset, 0.,
+ (GLfloat)sceneWidth - x - xoffset, (GLfloat)1 - y - yoffset, 0.,
+ (GLfloat) - x - xoffset, (GLfloat)sceneHeight - y - yoffset, 0.,
+ (GLfloat)sceneWidth - x - xoffset, (GLfloat)sceneHeight - y - yoffset, 0.
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ backdropTexW, 0.0f,
+ 0.0f, backdropTexH,
+ backdropTexW, backdropTexH
+ };
+
+
+ glUseProgram(shader.texture);
+
+ setPMVMatrix(shader.texture);
+ drawQuad(shader.texture, vertices, 1, texCoords);
+
+ // Then the line
+ //FIXME:Removing the lines doesn't work, but also didn't work properly before.
+
+ GLint xo1 = -xoffset, xo2 = -xoffset;
+ if (! backwards) {
+ xo2 += diffX;
+ } else {
+ xo1 += diffX;
+ }
+ const GLint lineVertices[] = {
+ xo1, -yoffset, 0,
+ xo2, -yoffset + diffY, 0,
+ };
+
+
+ glUseProgram(shader.color);
+
+ setPMVMatrix(shader.color);
+
+ glUniform4f(glGetUniformLocation(shader.color, "myColor"), 0.0f, 0.0f, 0.0f, 1.0f);
+
+ int vertexLoc = glGetAttribLocation(shader.color, "myVertex");
+ glEnableVertexAttribArray(vertexLoc);
+ glVertexAttribPointer(vertexLoc, 3, GL_INT, GL_FALSE, 0, lineVertices);
+ glDrawArrays(GL_LINES, 0, 2);
+ glDisableVertexAttribArray(vertexLoc);
+
+ glUseProgram(0);
+
+ // Copy Our ViewPort To The Texture
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, x + xoffset, y + yoffset, viewportOffsetX, viewportOffsetY, w, h, backdropTextureName);
+
+ yoffset += viewportHeight;
+ }
+ xoffset += viewportWidth;
+ }
+ setPixelCoords(false);
+#endif
+}
diff --git a/engines/sludge/line.h b/engines/sludge/line.h
new file mode 100644
index 0000000000..e7d00f051c
--- /dev/null
+++ b/engines/sludge/line.h
@@ -0,0 +1,27 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_LINE_H
+#define SLUDGE_LINE_H
+
+void drawLine(int x1, int y1, int x2, int y2);
+
+#endif
diff --git a/engines/sludge/linuxstuff.cpp b/engines/sludge/linuxstuff.cpp
new file mode 100644
index 0000000000..d5b76a5cf0
--- /dev/null
+++ b/engines/sludge/linuxstuff.cpp
@@ -0,0 +1,225 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#if defined __unix__ && !(defined __APPLE__)
+#include <iostream>
+#include <fstream>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <getopt.h>
+
+#include "linuxstuff.h"
+#include "platform-dependent.h"
+#include "allfiles.h"
+#include "language.h" // for settings
+#include "debug.h"
+#include "helpers.h"
+
+extern settingsStruct gameSettings;
+cmdlineSettingsStruct cmdlineSettings;
+
+extern char **languageName;
+
+/*
+ * Functions declared in linuxstuff.h:
+ */
+
+void printCmdlineUsage() {
+ fprintf(stdout, "OpenSLUDGE engine, usage: sludge-engine [<options>] <gamefile name>\n\n");
+ fprintf(stdout, "Options:\n");
+ fprintf(stdout, "-f, --fullscreen Set display mode to fullscreen\n");
+ fprintf(stdout, "-w, --window Set display mode to windowed\n");
+ fprintf(stdout, "-L, --list-languages Print available languages and their indices\n");
+ fprintf(stdout, "-l<index>, --language=<index> Set language to <index> (look up with -L)\n");
+ fprintf(stdout, "-a<number>, --antialias=<number> Turn antialiasing on (1) or off (0)\n");
+ fprintf(stdout, " or choose linear interpolation (-1)\n");
+ fprintf(stdout, "-d<number>, --debug=<number> Turn debug mode on (1) or off (0)\n");
+ fprintf(stdout, "-h, --help Print this help message\n\n");
+ fprintf(stdout, "Options are saved, so you don't need to specify them every time.\n");
+ fprintf(stdout, "If you entered a wrong language number, use -l0 to reset the language to the default setting.\n");
+ fprintf(stdout, "You can always toggle between fullscreen and windowed mode with \"Alt+Enter\"\n");
+ fprintf(stdout, "or antialiasing on and off with \"Alt+A\".\n");
+}
+
+void printLanguageTable() {
+ if (gameSettings.numLanguages) {
+ fprintf(stdout, "Index Language\n");
+ for (unsigned int i = 0; i <= gameSettings.numLanguages; i++) {
+ if (languageName[i]) {
+ fprintf(stdout, "%d %s\n", i, languageName[i]);
+ } else {
+ fprintf(stdout, "%d Language %d\n", i, i);
+ }
+ }
+ } else {
+ fprintf(stdout, "No translations available.\n");
+ }
+}
+
+bool parseCmdlineParameters(int argc, char *argv[]) {
+ int retval = true;
+ cmdlineSettings.fullscreenSet = false;
+ cmdlineSettings.languageSet = false;
+ cmdlineSettings.aaSet = false;
+ cmdlineSettings.debugModeSet = false;
+ cmdlineSettings.listLanguages = false;
+ while (1) {
+ static struct option long_options[] = {
+ {"fullscreen", no_argument, 0, 'f' },
+ {"window", no_argument, 0, 'w' },
+ {"list-languages", no_argument, 0, 'L' },
+ {"language", required_argument, 0, 'l' },
+ {"antialias", required_argument, 0, 'a' },
+ {"debug", required_argument, 0, 'd' },
+ {"help", no_argument, 0, 'h' },
+ {0, 0, 0, 0} /* This is a filler for -1 */
+ };
+ int option_index = 0;
+ int c = getopt_long(argc, argv, "fwLl:a:d:h", long_options, &option_index);
+ if (c == -1) break;
+ switch (c) {
+ case 'f':
+ cmdlineSettings.fullscreenSet = true;
+ cmdlineSettings.userFullScreen = true;
+ break;
+ case 'w':
+ cmdlineSettings.fullscreenSet = true;
+ cmdlineSettings.userFullScreen = false;
+ break;
+ case 'L':
+ cmdlineSettings.listLanguages = true;
+ break;
+ case 'l':
+ cmdlineSettings.languageSet = true;
+ cmdlineSettings.languageID = atoi(optarg);
+ break;
+ case 'a':
+ cmdlineSettings.aaSet = true;
+ cmdlineSettings.antiAlias = atoi(optarg);
+ break;
+ case 'd':
+ cmdlineSettings.debugModeSet = true;
+ cmdlineSettings.debugMode = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ retval = false;
+ break;
+ }
+ }
+ return retval;
+}
+
+/*
+ * Functions declared in platform-dependent.h:
+ */
+
+char *grabFileName() {
+ return NULL;
+}
+
+int showSetupWindow() {
+ if (cmdlineSettings.listLanguages) {
+ printLanguageTable();
+ return 0;
+ }
+ if (cmdlineSettings.languageSet) {
+ if (cmdlineSettings.languageID <= gameSettings.numLanguages) {
+ gameSettings.languageID = cmdlineSettings.languageID;
+ } else {
+ fprintf(stdout, "Language index %d doesn't exist. Please specify an index between 0 and %d.\n\n",
+ cmdlineSettings.languageID, gameSettings.numLanguages);
+ printLanguageTable();
+ return 0;
+ }
+ }
+ if (cmdlineSettings.fullscreenSet) {
+ gameSettings.userFullScreen = cmdlineSettings.userFullScreen;
+ }
+ if (cmdlineSettings.aaSet) {
+ gameSettings.antiAlias = cmdlineSettings.antiAlias;
+ }
+ if (cmdlineSettings.debugModeSet) {
+ gameSettings.debugMode = cmdlineSettings.debugMode;
+ }
+ return 1;
+}
+
+void msgBox(const char *head, const char *msg) {
+ fprintf(stderr, "%s\n%s\n", head, msg);
+}
+
+int msgBoxQuestion(const char *head, const char *msg) {
+ return 1;
+}
+
+void changeToUserDir() {
+ if (chdir(getenv("HOME"))) {
+ debugOut("Error: Failed changing to directory %s\n", getenv("HOME"));
+ }
+ mkdir(".sludge-engine", 0000777);
+ if (chdir(".sludge-engine")) {
+ debugOut("Error: Failed changing to directory %s\n", ".sludge-engine");
+ }
+}
+
+uint32_t launch(char *filename) {
+ debugOut("Trying to launch: %s\n", filename);
+
+ if (!fileExists("/usr/bin/xdg-open")) {
+ debugOut("Launching failed due to missing /usr/bin/xdg-open.\n");
+ return 0;
+ }
+
+ if (!(filename[0] == 'h' &&
+ filename[1] == 't' &&
+ filename[2] == 't' &&
+ filename[3] == 'p' &&
+ (filename[4] == ':' || (filename[4] == 's' && filename[5] == ':'))) &&
+ !fileExists(filename)) {
+ return 0;
+ }
+
+ int status;
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ return 0;
+ } else if (pid == 0) {
+ execl("/usr/bin/xdg-open", "xdg-open", filename, (char *)0);
+ exit(EXIT_FAILURE);
+ } else {
+ waitpid(pid, &status, 0);
+ }
+
+ if (status == EXIT_SUCCESS) {
+ return 69;
+ } else {
+ return 0;
+ }
+}
+
+bool defaultUserFullScreen() {
+ return false;
+}
+#endif
diff --git a/engines/sludge/linuxstuff.h b/engines/sludge/linuxstuff.h
new file mode 100644
index 0000000000..ba7344c040
--- /dev/null
+++ b/engines/sludge/linuxstuff.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_LINUXSTUFF_H
+#define SLUDGE_LINUXSTUFF_H
+
+struct cmdlineSettingsStruct {
+ bool languageSet;
+ unsigned int languageID;
+ bool fullscreenSet;
+ bool userFullScreen;
+ bool aaSet;
+ int antiAlias;
+ bool debugModeSet;
+ bool debugMode;
+ bool listLanguages;
+};
+
+void printCmdlineUsage();
+bool parseCmdlineParameters(int argc, char *argv[]);
+
+#endif
diff --git a/engines/sludge/loadsave.cpp b/engines/sludge/loadsave.cpp
new file mode 100644
index 0000000000..8641a67a7d
--- /dev/null
+++ b/engines/sludge/loadsave.cpp
@@ -0,0 +1,678 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+#include "sprites.h"
+#include "fonttext.h"
+#include "newfatal.h"
+#include "variable.h"
+#include "CommonCode/version.h"
+#include "language.h"
+#include "moreio.h"
+#include "sludger.h"
+#include "people.h"
+#include "talk.h"
+#include "objtypes.h"
+#include "backdrop.h"
+#include "region.h"
+#include "floor.h"
+#include "zbuffer.h"
+#include "cursors.h"
+#include "statusba.h"
+#include "sound.h"
+#include "fileset.h"
+#include "debug.h"
+
+//----------------------------------------------------------------------
+// From elsewhere
+//----------------------------------------------------------------------
+
+extern loadedFunction *allRunningFunctions; // In sludger.cpp
+extern char *typeName[]; // In variable.cpp
+extern int numGlobals; // In sludger.cpp
+extern variable *globalVars; // In sludger.cpp
+extern flor *currentFloor; // In floor.cpp
+extern zBufferData zBuffer; // In zbuffer.cpp
+extern speechStruct *speech; // In talk.cpp
+extern personaAnimation *mouseCursorAnim; // In cursor.cpp
+extern int mouseCursorFrameNum; // " " "
+extern int loadedFontNum, fontHeight, fontTableSize; // In fonttext.cpp
+extern int numFontColours; // " " "
+extern char *fontOrderString; // " " "
+extern FILETIME fileTime; // In sludger.cpp
+extern int speechMode; // " " "
+extern int lightMapNumber; // In backdrop.cpp
+extern unsigned int sceneWidth, sceneHeight; // " " "
+extern int cameraX, cameraY; // " " "
+extern float cameraZoom;
+extern unsigned char brightnessLevel; // " " "
+extern short fontSpace; // in textfont.cpp
+extern unsigned char fadeMode; // In transition.cpp
+extern bool captureAllKeys;
+extern bool allowAnyFilename;
+extern unsigned short saveEncoding; // in savedata.cpp
+extern unsigned char currentBurnR, currentBurnG, currentBurnB;
+extern unsigned int currentBlankColour; // in backdrop.cpp
+extern parallaxLayer *parallaxStuff; // "
+extern int lightMapMode; // "
+extern int languageNum;
+
+
+//----------------------------------------------------------------------
+// Globals (so we know what's saved already and what's a reference
+//----------------------------------------------------------------------
+
+struct stackLibrary {
+ stackHandler *stack;
+ stackLibrary *next;
+};
+
+int stackLibTotal = 0;
+stackLibrary *stackLib = NULL;
+
+//----------------------------------------------------------------------
+// For saving and loading stacks...
+//----------------------------------------------------------------------
+#if ALLOW_FILE
+bool saveVariable(variable *from, FILE *fp);
+bool loadVariable(variable *to, FILE *fp);
+
+void saveStack(variableStack *vs, FILE *fp) {
+ int elements = 0;
+ int a;
+
+ variableStack *search = vs;
+ while (search) {
+ elements ++;
+ search = search -> next;
+ }
+
+ stackDebug((stackfp, " stack contains %d elements\n", elements));
+
+ put2bytes(elements, fp);
+ search = vs;
+ for (a = 0; a < elements; a ++) {
+ saveVariable(& search -> thisVar, fp);
+ search = search -> next;
+ }
+}
+
+variableStack *loadStack(FILE *fp, variableStack **last) {
+ int elements = get2bytes(fp);
+ int a;
+ variableStack *first = NULL;
+ variableStack * * changeMe = & first;
+
+ for (a = 0; a < elements; a ++) {
+ variableStack *nS = new variableStack;
+ if (!checkNew(nS)) return NULL;
+ loadVariable(& (nS -> thisVar), fp);
+ if (last && a == elements - 1) {
+ stackDebug((stackfp, "Setting last to %p\n", nS));
+ *last = nS;
+ }
+ nS -> next = NULL;
+ (* changeMe) = nS;
+ changeMe = & (nS -> next);
+ }
+
+ return first;
+}
+
+bool saveStackRef(stackHandler *vs, FILE *fp) {
+ stackLibrary *s = stackLib;
+ int a = 0;
+ while (s) {
+ if (s -> stack == vs) {
+ fputc(1, fp);
+ put2bytes(stackLibTotal - a, fp);
+ return true;
+ }
+ s = s -> next;
+ a ++;
+ }
+ fputc(0, fp);
+ saveStack(vs -> first, fp);
+ s = new stackLibrary;
+ stackLibTotal ++;
+ if (! checkNew(s)) return false;
+ s -> next = stackLib;
+ s -> stack = vs;
+ stackLib = s;
+ return true;
+}
+#endif
+
+void clearStackLib() {
+ stackLibrary *k;
+ while (stackLib) {
+ k = stackLib;
+ stackLib = stackLib -> next;
+ delete k;
+ }
+ stackLibTotal = 0;
+}
+
+stackHandler *getStackFromLibrary(int n) {
+ n = stackLibTotal - n;
+ while (n) {
+ stackLib = stackLib -> next;
+ n --;
+ }
+ return stackLib -> stack;
+}
+#if ALLOW_FILE
+stackHandler *loadStackRef(FILE *fp) {
+ stackHandler *nsh;
+
+ if (fgetc(fp)) { // It's one we've loaded already...
+ stackDebug((stackfp, "loadStackRef (duplicate, get from library)\n"));
+
+ nsh = getStackFromLibrary(get2bytes(fp));
+ nsh -> timesUsed ++;
+ } else {
+ stackDebug((stackfp, "loadStackRef (new one)\n"));
+
+ // Load the new stack
+
+ nsh = new stackHandler;
+ if (! checkNew(nsh)) return NULL;
+ nsh -> last = NULL;
+ nsh -> first = loadStack(fp, & nsh->last);
+ nsh -> timesUsed = 1;
+ stackDebug((stackfp, " first = %p\n", nsh->first));
+ if (nsh->first)
+ stackDebug((stackfp, " first->next = %p\n", nsh->first->next));
+ stackDebug((stackfp, " last = %p\n", nsh->last));
+ if (nsh->last)
+ stackDebug((stackfp, " last->next = %p\n", nsh->last->next));
+
+ // Add it to the library of loaded stacks
+
+ stackLibrary *s = new stackLibrary;
+ if (! checkNew(s)) return NULL;
+ s -> stack = nsh;
+ s -> next = stackLib;
+ stackLib = s;
+ stackLibTotal ++;
+ }
+ return nsh;
+}
+
+//----------------------------------------------------------------------
+// For saving and loading variables...
+//----------------------------------------------------------------------
+
+bool saveVariable(variable *from, FILE *fp) {
+#if DEBUG_STACKINESS
+ {
+ char *str = getTextFromAnyVar(*from);
+ stackDebug((stackfp, "in saveVariable, type %d, %s\n", from->varType, str));
+ delete str;
+ }
+#endif
+
+ fputc(from -> varType, fp);
+ switch (from -> varType) {
+ case SVT_INT:
+ case SVT_FUNC:
+ case SVT_BUILT:
+ case SVT_FILE:
+ case SVT_OBJTYPE:
+ put4bytes(from -> varData.intValue, fp);
+ return true;
+
+ case SVT_STRING:
+ writeString(from -> varData.theString, fp);
+ return true;
+
+ case SVT_STACK:
+ return saveStackRef(from -> varData.theStack, fp);
+
+ case SVT_COSTUME:
+ saveCostume(from -> varData.costumeHandler, fp);
+ return false;
+
+ case SVT_ANIM:
+ saveAnim(from -> varData.animHandler, fp);
+ return false;
+
+ case SVT_NULL:
+ return false;
+
+ default:
+ fatal("Can't save variables of this type:",
+ (from->varType < SVT_NUM_TYPES) ?
+ typeName[from->varType] :
+ "bad ID");
+ }
+ return true;
+}
+
+bool loadVariable(variable *to, FILE *fp) {
+ to -> varType = (variableType) fgetc(fp);
+ switch (to -> varType) {
+ case SVT_INT:
+ case SVT_FUNC:
+ case SVT_BUILT:
+ case SVT_FILE:
+ case SVT_OBJTYPE:
+ to -> varData.intValue = get4bytes(fp);
+ return true;
+
+ case SVT_STRING:
+ to -> varData.theString = readString(fp);
+ return true;
+
+ case SVT_STACK:
+ to -> varData.theStack = loadStackRef(fp);
+#if DEBUG_STACKINESS
+ {
+ char *str = getTextFromAnyVar(*to);
+ stackDebug((stackfp, "just loaded %s\n", str));
+ delete str;
+ }
+#endif
+ return true;
+
+ case SVT_COSTUME:
+ to -> varData.costumeHandler = new persona;
+ if (! checkNew(to -> varData.costumeHandler)) return false;
+ loadCostume(to -> varData.costumeHandler, fp);
+ return true;
+
+ case SVT_ANIM:
+ to -> varData.animHandler = new personaAnimation;
+ if (! checkNew(to -> varData.animHandler)) return false;
+ loadAnim(to -> varData.animHandler, fp);
+ return true;
+
+ default:
+ break;
+ }
+ return true;
+}
+
+//----------------------------------------------------------------------
+// For saving and loading functions
+//----------------------------------------------------------------------
+
+void saveFunction(loadedFunction *fun, FILE *fp) {
+ int a;
+ put2bytes(fun -> originalNumber, fp);
+ if (fun -> calledBy) {
+ fputc(1, fp);
+ saveFunction(fun -> calledBy, fp);
+ } else {
+ fputc(0, fp);
+ }
+ put4bytes(fun -> timeLeft, fp);
+ put2bytes(fun -> runThisLine, fp);
+ fputc(fun -> cancelMe, fp);
+ fputc(fun -> returnSomething, fp);
+ fputc(fun -> isSpeech, fp);
+ saveVariable(& (fun -> reg), fp);
+
+ if (fun -> freezerLevel) {
+ fatal(ERROR_GAME_SAVE_FROZEN);
+ }
+ saveStack(fun -> stack, fp);
+ for (a = 0; a < fun -> numLocals; a ++) {
+ saveVariable(& (fun -> localVars[a]), fp);
+ }
+}
+
+
+loadedFunction *loadFunction(FILE *fp) {
+ int a;
+
+ // Reserve memory...
+
+ loadedFunction *buildFunc = new loadedFunction;
+ if (! checkNew(buildFunc)) return NULL;
+
+ // See what it was called by and load if we need to...
+
+ buildFunc -> originalNumber = get2bytes(fp);
+ buildFunc -> calledBy = NULL;
+ if (fgetc(fp)) {
+ buildFunc -> calledBy = loadFunction(fp);
+ if (! buildFunc -> calledBy) return NULL;
+ }
+
+ buildFunc -> timeLeft = get4bytes(fp);
+ buildFunc -> runThisLine = get2bytes(fp);
+ buildFunc -> freezerLevel = 0;
+ buildFunc -> cancelMe = fgetc(fp);
+ buildFunc -> returnSomething = fgetc(fp);
+ buildFunc -> isSpeech = fgetc(fp);
+ loadVariable(& (buildFunc -> reg), fp);
+ loadFunctionCode(buildFunc);
+
+ buildFunc -> stack = loadStack(fp, NULL);
+
+ for (a = 0; a < buildFunc -> numLocals; a ++) {
+ loadVariable(& (buildFunc -> localVars[a]), fp);
+ }
+
+ return buildFunc;
+}
+#endif
+//----------------------------------------------------------------------
+// Save everything
+//----------------------------------------------------------------------
+
+bool saveGame(char *fname) {
+#if ALLOW_FILE
+ int a;
+
+ FILE *fp = fopen(fname, "wb");
+ if (fp == NULL) return false;
+
+ fprintf(fp, "SLUDSA");
+ fputc(0, fp);
+ fputc(0, fp);
+ fputc(MAJOR_VERSION, fp);
+ fputc(MINOR_VERSION, fp);
+
+ if (! saveThumbnail(fp)) return false;
+
+ fwrite(& fileTime, sizeof(FILETIME), 1, fp);
+
+ // DON'T ADD ANYTHING NEW BEFORE THIS POINT!
+
+ fputc(allowAnyFilename, fp);
+ fputc(captureAllKeys, fp);
+ fputc(true, fp); // updateDisplay
+ fputc(fontTableSize > 0, fp);
+
+ if (fontTableSize > 0) {
+ put2bytes(loadedFontNum, fp);
+ put2bytes(fontHeight, fp);
+ writeString(fontOrderString, fp);
+ }
+ putSigned(fontSpace, fp);
+
+ // Save backdrop
+ put2bytes(cameraX, fp);
+ put2bytes(cameraY, fp);
+ putFloat(cameraZoom, fp);
+
+ fputc(brightnessLevel, fp);
+ saveHSI(fp);
+
+ // Save event handlers
+ saveHandlers(fp);
+
+ // Save regions
+ saveRegions(fp);
+
+ saveAnim(mouseCursorAnim, fp);
+ put2bytes(mouseCursorFrameNum, fp);
+
+ // Save functions
+ loadedFunction *thisFunction = allRunningFunctions;
+ int countFunctions = 0;
+ while (thisFunction) {
+ countFunctions ++;
+ thisFunction = thisFunction -> next;
+ }
+ put2bytes(countFunctions, fp);
+
+ thisFunction = allRunningFunctions;
+ while (thisFunction) {
+ saveFunction(thisFunction, fp);
+ thisFunction = thisFunction -> next;
+ }
+
+ for (a = 0; a < numGlobals; a ++) {
+ saveVariable(& globalVars[a], fp);
+ }
+
+ savePeople(fp);
+
+ if (currentFloor -> numPolygons) {
+ fputc(1, fp);
+ put2bytes(currentFloor -> originalNum, fp);
+ } else fputc(0, fp);
+
+ if (zBuffer.tex) {
+ fputc(1, fp);
+ put2bytes(zBuffer.originalNum, fp);
+ } else fputc(0, fp);
+
+ if (lightMap.data) {
+ fputc(1, fp);
+ put2bytes(lightMapNumber, fp);
+ } else fputc(0, fp);
+
+ fputc(lightMapMode, fp);
+ fputc(speechMode, fp);
+ fputc(fadeMode, fp);
+ saveSpeech(speech, fp);
+ saveStatusBars(fp);
+ saveSounds(fp);
+
+ put2bytes(saveEncoding, fp);
+
+ blur_saveSettings(fp);
+
+ put2bytes(currentBlankColour, fp);
+ fputc(currentBurnR, fp);
+ fputc(currentBurnG, fp);
+ fputc(currentBurnB, fp);
+
+ saveParallaxRecursive(parallaxStuff, fp);
+ fputc(0, fp);
+
+ fputc(languageNum, fp); // Selected language
+
+ saveSnapshot(fp);
+
+ fclose(fp);
+#endif
+ clearStackLib();
+ return true;
+}
+
+//----------------------------------------------------------------------
+// Load everything
+//----------------------------------------------------------------------
+
+int ssgVersion;
+
+bool loadGame(char *fname) {
+#if ALLOW_FILE
+ FILE *fp;
+ FILETIME savedGameTime;
+ int a;
+
+ while (allRunningFunctions) finishFunction(allRunningFunctions);
+
+ fp = openAndVerify(fname, 'S', 'A', ERROR_GAME_LOAD_NO, ssgVersion);
+ if (fp == NULL) return false;
+
+ if (ssgVersion >= VERSION(1, 4)) {
+ if (! skipThumbnail(fp)) return fatal(ERROR_GAME_LOAD_CORRUPT, fname);
+ }
+
+ size_t bytes_read = fread(& savedGameTime, sizeof(FILETIME), 1, fp);
+ if (bytes_read != sizeof(FILETIME) && ferror(fp)) {
+ debugOut("Reading error in loadGame.\n");
+ }
+
+ if (savedGameTime.dwLowDateTime != fileTime.dwLowDateTime ||
+ savedGameTime.dwHighDateTime != fileTime.dwHighDateTime) {
+ return fatal(ERROR_GAME_LOAD_WRONG, fname);
+ }
+
+ // DON'T ADD ANYTHING NEW BEFORE THIS POINT!
+
+ if (ssgVersion >= VERSION(1, 4)) {
+ allowAnyFilename = fgetc(fp);
+ }
+ captureAllKeys = fgetc(fp);
+ fgetc(fp); // updateDisplay (part of movie playing)
+
+ bool fontLoaded = fgetc(fp);
+ int fontNum;
+ char *charOrder;
+ if (fontLoaded) {
+ fontNum = get2bytes(fp);
+ fontHeight = get2bytes(fp);
+
+ if (ssgVersion < VERSION(2, 2)) {
+ int x;
+ charOrder = new char[257];
+ if (! checkNew(charOrder)) return false;
+
+ for (int a = 0; a < 256; a ++) {
+ x = fgetc(fp);
+ charOrder[x] = a;
+ }
+ charOrder[256] = 0;
+ } else {
+ charOrder = readString(fp);
+ }
+ }
+ loadFont(fontNum, charOrder, fontHeight);
+ delete [] charOrder;
+
+ fontSpace = getSigned(fp);
+
+ killAllPeople();
+ killAllRegions();
+
+ int camerX = get2bytes(fp);
+ int camerY = get2bytes(fp);
+ float camerZ;
+ if (ssgVersion >= VERSION(2, 0)) {
+ camerZ = getFloat(fp);
+ } else {
+ camerZ = 1.0;
+ }
+
+ brightnessLevel = fgetc(fp);
+
+ loadHSI(fp, 0, 0, true);
+ loadHandlers(fp);
+ loadRegions(fp);
+
+ mouseCursorAnim = new personaAnimation;
+ if (! checkNew(mouseCursorAnim)) return false;
+ if (! loadAnim(mouseCursorAnim, fp)) return false;
+ mouseCursorFrameNum = get2bytes(fp);
+
+ loadedFunction *rFunc;
+ loadedFunction * * buildList = & allRunningFunctions;
+
+
+ int countFunctions = get2bytes(fp);
+ while (countFunctions --) {
+ rFunc = loadFunction(fp);
+ rFunc -> next = NULL;
+ (* buildList) = rFunc;
+ buildList = & (rFunc -> next);
+ }
+
+ for (a = 0; a < numGlobals; a ++) {
+ unlinkVar(globalVars[a]);
+ loadVariable(& globalVars[a], fp);
+ }
+
+ loadPeople(fp);
+
+
+ if (fgetc(fp)) {
+ if (! setFloor(get2bytes(fp))) return false;
+ } else setFloorNull();
+
+ if (fgetc(fp)) {
+ if (! setZBuffer(get2bytes(fp))) return false;
+ }
+
+ if (fgetc(fp)) {
+ if (! loadLightMap(get2bytes(fp))) return false;
+ }
+
+ if (ssgVersion >= VERSION(1, 4)) {
+ lightMapMode = fgetc(fp) % 3;
+ }
+
+ speechMode = fgetc(fp);
+ fadeMode = fgetc(fp);
+ loadSpeech(speech, fp);
+ loadStatusBars(fp);
+ loadSounds(fp);
+
+ saveEncoding = get2bytes(fp);
+
+ if (ssgVersion >= VERSION(1, 6)) {
+ if (ssgVersion < VERSION(2, 0)) {
+ // aaLoad
+ fgetc(fp);
+ getFloat(fp);
+ getFloat(fp);
+ }
+
+ blur_loadSettings(fp);
+ }
+
+ if (ssgVersion >= VERSION(1, 3)) {
+ currentBlankColour = get2bytes(fp);
+ currentBurnR = fgetc(fp);
+ currentBurnG = fgetc(fp);
+ currentBurnB = fgetc(fp);
+
+ // Read parallax layers
+ while (fgetc(fp)) {
+ int im = get2bytes(fp);
+ int fx = get2bytes(fp);
+ int fy = get2bytes(fp);
+
+ if (! loadParallax(im, fx, fy)) return false;
+ }
+
+ int selectedLanguage = fgetc(fp);
+ if (selectedLanguage != languageNum) {
+ // Load the saved language!
+ languageNum = selectedLanguage;
+ setFileIndices(NULL, gameSettings.numLanguages, languageNum);
+ }
+ }
+
+ nosnapshot();
+ if (ssgVersion >= VERSION(1, 4)) {
+ if (fgetc(fp)) {
+ if (! restoreSnapshot(fp)) return false;
+ }
+ }
+
+ fclose(fp);
+
+ cameraX = camerX;
+ cameraY = camerY;
+ cameraZoom = camerZ;
+#endif
+
+ clearStackLib();
+ return true;
+}
diff --git a/engines/sludge/loadsave.h b/engines/sludge/loadsave.h
new file mode 100644
index 0000000000..d2ce316706
--- /dev/null
+++ b/engines/sludge/loadsave.h
@@ -0,0 +1,33 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_LOADSAVE_H
+#define SLUDGE_LOADSAVE_H
+
+bool saveGame(char *fname);
+bool loadGame(char *fname);
+
+#if ALLOW_FILE
+loadedFunction *loadFunction(FILE *fp);
+void saveFunction(loadedFunction *fun, FILE *fp);
+#endif
+
+#endif
diff --git a/engines/sludge/main_loop.cpp b/engines/sludge/main_loop.cpp
new file mode 100644
index 0000000000..0d807afacf
--- /dev/null
+++ b/engines/sludge/main_loop.cpp
@@ -0,0 +1,508 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#if defined __unix__ && !(defined __APPLE__)
+#include "linuxstuff.h"
+#endif
+
+#if 0
+#ifdef _WIN32
+#include "winstuff.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+
+#include <iostream>
+
+
+#include <stdexcept>
+
+#include <time.h>
+#include <sys/time.h>
+
+#if !defined(HAVE_GLES2)
+#include "GLee.h"
+#else
+#include <GLES2/gl2.h>
+#include "eglport/eglport.h"
+#endif
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_syswm.h>
+#endif
+
+#include "allfiles.h"
+#include "debug.h"
+#include "platform-dependent.h"
+#include "language.h"
+#include "stringy.h"
+#include "sludger.h"
+#include "backdrop.h"
+#include "language.h"
+#include "newfatal.h"
+#include "people.h"
+#include "floor.h"
+#include "objtypes.h"
+#include "talk.h"
+#include "statusba.h"
+#include "transition.h"
+#include "CommonCode/specialsettings.h"
+#include "timing.h"
+#include "sound.h"
+#include "sludger.h"
+#include "graphics.h"
+#include "helpers.h"
+
+
+#ifdef _WIN32
+#define PATHSLASH '\\'
+#else
+#define PATHSLASH '/'
+#endif
+
+extern bool runningFullscreen;
+
+#ifndef MAX_PATH
+#define MAX_PATH 1024 // maximum size of a path name
+#endif
+
+HWND hMainWindow = NULL;
+
+int realWinWidth = 640, realWinHeight = 480;
+extern float cameraZoom;
+
+extern int specialSettings;
+extern inputType input;
+extern variableStack *noStack;
+
+int dialogValue = 0;
+
+char *gameName = NULL;
+char *gamePath = NULL;
+char *bundleFolder;
+
+void setGameFilePath(char *f) {
+ char currentDir[1000];
+#if 0
+ if (!getcwd(currentDir, 998)) {
+ debugOut("Can't get current directory.\n");
+ }
+
+ int got = -1, a;
+
+ for (a = 0; f[a]; a ++) {
+ if (f[a] == PATHSLASH) got = a;
+ }
+
+ if (got != -1) {
+ f[got] = 0;
+ if (chdir(f)) {
+ debugOut("Error: Failed changing to directory %s\n", f);
+ }
+ f[got] = PATHSLASH;
+ }
+
+ gamePath = new char[400];
+ if (!checkNew(gamePath)) return;
+
+ if (!getcwd(gamePath, 398)) {
+ debugOut("Can't get game directory.\n");
+ }
+
+ if (chdir(currentDir)) {
+ debugOut("Error: Failed changing to directory %s\n", currentDir);
+ }
+#endif
+}
+
+#if ALLOW_FILE
+void saveHSI(FILE *writer);
+#endif
+
+extern bool reallyWantToQuit;
+
+#ifdef _WIN32
+#undef main
+#endif
+
+int weAreDoneSoQuit;
+
+void checkInput() {
+ static bool fakeRightclick = false;
+#if 0
+ SDL_Event event;
+
+ /* Check for events */
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+
+ case SDL_VIDEORESIZE:
+ realWinWidth = event.resize.w;
+ realWinHeight = event.resize.h;
+ setGraphicsWindow(false, true, true);
+ break;
+
+ case SDL_MOUSEMOTION:
+ input.justMoved = true;
+ input.mouseX = event.motion.x * ((float)winWidth / cameraZoom) / realWinWidth;
+ input.mouseY = event.motion.y * ((float)winHeight / cameraZoom) / realWinHeight;
+ break;
+
+ case SDL_MOUSEBUTTONDOWN:
+ if (event.button.button == SDL_BUTTON_LEFT) {
+ if (SDL_GetModState() & KMOD_CTRL) {
+ input.rightClick = true;
+ fakeRightclick = true;
+ } else {
+ input.leftClick = true;
+ fakeRightclick = false;
+ }
+ }
+ if (event.button.button == SDL_BUTTON_RIGHT) input.rightClick = true;
+ input.mouseX = event.motion.x * ((float)winWidth / cameraZoom) / realWinWidth;
+ input.mouseY = event.motion.y * ((float)winHeight / cameraZoom) / realWinHeight;
+ break;
+
+ case SDL_MOUSEBUTTONUP:
+ if (event.button.button == SDL_BUTTON_LEFT) {
+ if (fakeRightclick) {
+ fakeRightclick = false;
+ input.rightRelease = true;
+ } else {
+ input.leftRelease = true;
+ }
+ }
+ if (event.button.button == SDL_BUTTON_RIGHT) input.rightRelease = true;
+ input.mouseX = event.motion.x * ((float)winWidth / cameraZoom) / realWinWidth;
+ input.mouseY = event.motion.y * ((float)winHeight / cameraZoom) / realWinHeight;
+ break;
+
+ case SDL_KEYDOWN:
+ // A Windows key is pressed - let's leave fullscreen.
+ if (runningFullscreen) {
+ if (event.key.keysym.sym == SDLK_LSUPER || event.key.keysym.sym == SDLK_LSUPER) {
+ setGraphicsWindow(!runningFullscreen);
+ }
+ }
+ // Ignore Command keypresses - they're for the OS to handle.
+ if (event.key.keysym.mod & KMOD_META) {
+ // Command+F - let's switch to/from full screen
+ if ('f' == event.key.keysym.unicode) {
+ setGraphicsWindow(!runningFullscreen);
+ }
+ break;
+ } else if (event.key.keysym.mod & KMOD_ALT) {
+ // Alt + Enter also switches full screen mode
+ if (SDLK_RETURN == event.key.keysym.sym) {
+ setGraphicsWindow(!runningFullscreen);
+ }
+ if (SDLK_a == event.key.keysym.sym) {
+ gameSettings.antiAlias = !gameSettings.antiAlias;
+ break;
+ }
+ // Allow Alt+F4 to quit
+ if (SDLK_F4 == event.key.keysym.sym) {
+ SDL_Event event;
+ event.type = SDL_QUIT;
+ SDL_PushEvent(&event);
+ }
+
+ break;
+ }
+ switch (event.key.keysym.sym) {
+ case SDLK_BACKSPACE:
+ case SDLK_DELETE: // Ok, mapping these to the same key is weird, I admit. But good?
+ input.keyPressed = 127;
+ break;
+ case SDLK_TAB:
+ input.keyPressed = 9;
+ break;
+ case SDLK_RETURN:
+ input.keyPressed = 13;
+ break;
+ case SDLK_ESCAPE:
+ input.keyPressed = 27;
+ break;
+ case SDLK_PAGEUP:
+ input.keyPressed = 63276;
+ break;
+ case SDLK_PAGEDOWN:
+ input.keyPressed = 63277;
+ break;
+ case SDLK_END:
+ input.keyPressed = 63275;
+ break;
+ case SDLK_HOME:
+ input.keyPressed = 63273;
+ break;
+ case SDLK_LEFT:
+ input.keyPressed = 63234;
+ break;
+ case SDLK_UP:
+ input.keyPressed = 63232;
+ break;
+ case SDLK_RIGHT:
+ input.keyPressed = 63235;
+ break;
+ case SDLK_DOWN:
+ input.keyPressed = 63233;
+ break;
+ case SDLK_F1:
+ input.keyPressed = 63236;
+ break;
+ case SDLK_F2:
+ input.keyPressed = 63237;
+ break;
+ case SDLK_F3:
+ input.keyPressed = 63238;
+ break;
+ case SDLK_F4:
+ input.keyPressed = 63239;
+ break;
+ case SDLK_F5:
+ input.keyPressed = 63240;
+ break;
+ case SDLK_F6:
+ input.keyPressed = 63241;
+ break;
+ case SDLK_F7:
+ input.keyPressed = 63242;
+ break;
+ case SDLK_F8:
+ input.keyPressed = 63243;
+ break;
+ case SDLK_F9:
+ input.keyPressed = 63244;
+ break;
+ case SDLK_F10:
+ input.keyPressed = 63245;
+ break;
+ case SDLK_F11:
+ input.keyPressed = 63246;
+ break;
+ case SDLK_F12:
+ input.keyPressed = 63247;
+ break;
+ default:
+ input.keyPressed = event.key.keysym.unicode;
+ break;
+ }
+ break;
+
+ case SDL_QUIT:
+ if (reallyWantToQuit) {
+ // The game file has requested that we quit
+ weAreDoneSoQuit = 1;
+ } else {
+ // The request is from elsewhere - ask for confirmation.
+ setGraphicsWindow(false);
+ //fprintf (stderr, "%s %s\n", gameName, getNumberedString(2));
+ if (msgBoxQuestion(gameName, getNumberedString(2))) {
+ weAreDoneSoQuit = 1;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+#endif
+}
+
+int main_loop(char *filename)
+#if 0
+try
+#endif
+{
+ /* Dimensions of our window. */
+ winWidth = 640;
+ winHeight = 480;
+
+ char *sludgeFile;
+
+#if 0
+ time_t t;
+ srand((unsigned) time(&t));
+
+
+ // bundleFolder is used to look for the game file
+ // and later to find the shader programs
+#ifdef __APPLE__
+ // bundleFolder is set in applicationDidFinishLaunching.
+#elif defined __unix__
+ bundleFolder = copyString(DATADIR); // DATADIR is defined in the Makefile.
+#else
+ bundleFolder = copyString(argv[0]);
+ int lastSlash = -1;
+ for (int i = 0; bundleFolder[i]; i ++) {
+ if (bundleFolder[i] == PATHSLASH) lastSlash = i;
+ }
+ bundleFolder[lastSlash + 1] = NULL;
+#endif
+
+#endif
+
+ sludgeFile = filename;
+ if (!sludgeFile) {
+ sludgeFile = joinStrings(bundleFolder, "gamedata.slg");
+ if (!(fileExists(sludgeFile))) {
+ delete sludgeFile;
+ sludgeFile = joinStrings(bundleFolder, "gamedata");
+ if (!(fileExists(sludgeFile))) {
+#if 0
+ sludgeFile = grabFileName();
+#endif
+ }
+ }
+ }
+
+#if 0
+#if defined __unix__ && !(defined __APPLE__)
+ if (!fileExists(sludgeFile)) {
+ fprintf(stderr, "Game file not found.\n");
+ printCmdlineUsage();
+ return 0;
+ }
+#endif
+#endif
+
+ // The player pressed cancel in the file selection dialogue,
+ // so we should quit now.
+ if (!sludgeFile) return 0;
+
+ // OK, so we DO want to start up, then...
+ setGameFilePath(sludgeFile);
+ if (!initSludge(sludgeFile)) return 0;
+
+#if 0
+ /* Initialize the SDL library */
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ msgBox("Startup Error: Couldn't initialize SDL.", SDL_GetError());
+ exit(1);
+ }
+#endif
+ if (gameIcon) {
+#if 0
+ if (SDL_Surface *programIcon = SDL_CreateRGBSurfaceFrom(gameIcon, iconW, iconH, 32, iconW * 4, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000)) {
+ SDL_WM_SetIcon(programIcon, NULL);
+ SDL_FreeSurface(programIcon);
+ }
+#endif
+ delete gameIcon;
+ }
+#if 0
+ // Needed to make menu shortcuts work (on Mac), i.e. Command+Q for quit
+ SDL_putenv((char *)"SDL_ENABLEAPPEVENTS=1");
+
+ setupOpenGLStuff();
+#endif
+
+#ifdef _WIN32
+ SDL_SysWMinfo wmInfo;
+ SDL_VERSION(&wmInfo.version);
+ SDL_GetWMInfo(&wmInfo);
+ hMainWindow = wmInfo.window;
+#endif
+
+ registerWindowForFatal();
+
+ if (!resizeBackdrop(winWidth, winHeight)) return fatal("Couldn't allocate memory for backdrop");
+
+ blankScreen(0, 0, winWidth, winHeight);
+ if (!initPeople()) return fatal("Couldn't initialise people stuff");
+ if (!initFloor()) return fatal("Couldn't initialise floor stuff");
+ if (!initObjectTypes()) return fatal("Couldn't initialise object type stuff");
+ initSpeech();
+ initStatusBar();
+ resetRandW();
+
+#if ALLOW_FILE
+ gameName = getNumberedString(1);
+#endif
+#if 0
+ SDL_WM_SetCaption(gameName, gameName);
+
+ if ((specialSettings & (SPECIAL_MOUSE_1 | SPECIAL_MOUSE_2)) == SPECIAL_MOUSE_1) {
+ // Hide the standard mouse cursor!
+ // This is done in a weird way because there's bugs with SDL_ShowCursor(SDL_DISABLE);
+ SDL_Cursor *cursor = NULL;
+ Uint8 data = 0;
+ SDL_FreeCursor(cursor);
+ cursor = SDL_CreateCursor(&data, &data, 1, 1, 0, 0);
+ SDL_SetCursor(cursor);
+ }
+
+ if (!(specialSettings & SPECIAL_SILENT)) {
+ initSoundStuff(hMainWindow);
+ }
+#endif
+
+ startNewFunctionNum(0, 0, NULL, noStack);
+
+#if 0
+ Init_Timer();
+
+ SDL_EnableUNICODE(1);
+#endif
+ weAreDoneSoQuit = 0;
+ while (!weAreDoneSoQuit) {
+ checkInput();
+ walkAllPeople();
+ handleInput();
+ sludgeDisplay();
+#if 0
+ Wait_Frame();
+#endif
+ }
+
+ debugOut("Bye!\n\n");
+
+ delete[] gamePath;
+
+#if 0
+ killSoundStuff();
+#endif
+
+#if defined(HAVE_GLES2)
+ EGL_Close();
+#endif
+
+#if 0
+ /* Clean up the SDL library */
+ SDL_Quit();
+#endif
+ displayFatal();
+ return (0);
+}
+#if 0
+catch (std::exception &ex) { //NOTE by reference, not value
+ std::cerr << "std::exception caught: " << ex.what() << std::endl;
+ return -1;
+} catch (...) {
+ std::cerr << "Unknown exception was never caught" << std::endl;
+ return -2;
+}
+#endif
diff --git a/engines/sludge/main_loop.h b/engines/sludge/main_loop.h
new file mode 100644
index 0000000000..8fea28eb7a
--- /dev/null
+++ b/engines/sludge/main_loop.h
@@ -0,0 +1,27 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_MAIN_LOOP_H
+#define SLUDGE_MAIN_LOOP_H
+
+int main_loop(char *filename);
+
+#endif
diff --git a/engines/sludge/memwatch.cpp b/engines/sludge/memwatch.cpp
new file mode 100644
index 0000000000..dab57b8bdb
--- /dev/null
+++ b/engines/sludge/memwatch.cpp
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdlib.h>
+#include "allfiles.h"
+
+void *allKnownMem[3000];
+int allKnownNum = 0;
+
+void outputKnownMem() {
+ FILE *debu = fopen("debuTURN.txt", "at");
+
+ fprintf(debu, "%i lumps:", allKnownNum);
+ for (int i = 0; i < allKnownNum; i ++) {
+ fprintf(debu, " %p", allKnownMem[i]);
+ }
+ fprintf(debu, "\n");
+ fclose(debu);
+}
+
+void adding(void *mem) {
+ allKnownMem[allKnownNum] = mem;
+ allKnownNum ++;
+
+ outputKnownMem();
+ if (allKnownNum == 3000) {
+ //db ("Error! Array too full!");
+ exit(1);
+ }
+}
+
+void deleting(void *mem) {
+ allKnownNum --;
+ for (int i = 0; i <= allKnownNum; i ++) {
+ if (allKnownMem[i] == mem) {
+ allKnownMem[i] = allKnownMem[allKnownNum];
+ outputKnownMem();
+ return;
+ }
+ }
+ //db ("Error! Deleted a block which hasn't been allocated!");
+ exit(1);
+}
diff --git a/engines/sludge/memwatch.h b/engines/sludge/memwatch.h
new file mode 100644
index 0000000000..2325b2ea0d
--- /dev/null
+++ b/engines/sludge/memwatch.h
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_MEMWATCH_H
+#define SLUDGE_MEMWATCH_H
+
+void adding(void *);
+void deleting(void *);
+
+#endif
diff --git a/engines/sludge/module.mk b/engines/sludge/module.mk
index 45929cf861..78f41dd631 100644
--- a/engines/sludge/module.mk
+++ b/engines/sludge/module.mk
@@ -1,9 +1,49 @@
MODULE := engines/sludge
MODULE_OBJS := \
+ backdrop.o \
+ bg_effects.o \
+ builtin.o \
+ console.o \
+ cursors.o \
+ debug.o \
detection.o \
+ floor.o \
+ freeze.o \
+ fonttext.o \
+ graphics.o \
+ helpers.o \
+ language.o \
+ line.o \
+ loadsave.o \
+ main_loop.o \
+ moreio.o \
+ movie.o \
+ newfatal.o \
+ objtypes.o \
+ people.o \
+ region.o \
+ savedata.o \
sludge.o \
- console.o
+ sludger.o \
+ sound_openal.o \
+ sprbanks.o \
+ sprites.o \
+ statusba.o \
+ stringy.o \
+ talk.o \
+ thumbnail.o \
+ transition.o \
+ variable.o \
+ zbuffer.o \
+ CommonCode/utf8.o \
+# fileset.o \
+ linuxstuff.o \
+ memwatch.o \
+ shaders.o \
+ timing.o \
+ libwebm/mkvparser.o \
+ libwebm/mkvreader.o \
MODULE_DIRS += \
engines/sludge
diff --git a/engines/sludge/moreio.cpp b/engines/sludge/moreio.cpp
new file mode 100644
index 0000000000..6df41fa7fa
--- /dev/null
+++ b/engines/sludge/moreio.cpp
@@ -0,0 +1,324 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <string.h>
+#include <stdint.h>
+
+#include "allfiles.h"
+#include "moreio.h"
+#include "newfatal.h"
+#include "stringy.h"
+
+#include "debug.h"
+
+#if defined __unix__ && !(defined __APPLE__)
+#include <endian.h>
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define __BIG_ENDIAN__
+#endif
+#endif
+
+bool allowAnyFilename = true;
+
+#if ALLOW_FILE
+int get2bytes(FILE *fp) {
+ int f1, f2;
+
+ f1 = fgetc(fp);
+ f2 = fgetc(fp);
+
+ return (f1 * 256 + f2);
+}
+
+void put2bytes(int numtoput, FILE *fp) {
+ fputc((char)(numtoput / 256), fp);
+ fputc((char)(numtoput % 256), fp);
+}
+
+void writeString(char *s, FILE *fp) {
+ int a, len = strlen(s);
+ put2bytes(len, fp);
+ for (a = 0; a < len; a ++) {
+ fputc(s[a] + 1, fp);
+ }
+}
+
+
+char *readString(FILE *fp) {
+
+ int a, len = get2bytes(fp);
+ //debugOut ("MOREIO: readString - len %i\n", len);
+ char *s = new char[len + 1];
+ if (! checkNew(s)) {
+ return NULL;
+ }
+ for (a = 0; a < len; a ++) {
+ s[a] = (char)(fgetc(fp) - 1);
+ }
+ s[len] = 0;
+ //debugOut ("MOREIO: readString: %s\n", s);
+ return s;
+}
+
+float floatSwap(float f) {
+ union {
+ float f;
+ unsigned char b[4];
+ } dat1, dat2;
+
+ dat1.f = f;
+ dat2.b[0] = dat1.b[3];
+ dat2.b[1] = dat1.b[2];
+ dat2.b[2] = dat1.b[1];
+ dat2.b[3] = dat1.b[0];
+ return dat2.f;
+}
+
+
+float getFloat(FILE *fp) {
+ float f;
+ size_t bytes_read = fread(& f, sizeof(float), 1, fp);
+ if (bytes_read != sizeof(float) && ferror(fp)) {
+ debugOut("Reading error in getFloat.\n");
+ }
+
+#ifdef __BIG_ENDIAN__
+ return floatSwap(f);
+#else
+ return f;
+#endif
+}
+
+void putFloat(float f, FILE *fp) {
+#ifdef __BIG_ENDIAN__
+ f = floatSwap(f);
+#endif
+ fwrite(& f, sizeof(float), 1, fp);
+}
+
+short shortSwap(short s) {
+ unsigned char b1, b2;
+
+ b1 = s & 255;
+ b2 = (s >> 8) & 255;
+
+ return (b1 << 8) + b2;
+}
+
+
+short getSigned(FILE *fp) {
+ short f;
+ size_t bytes_read = fread(& f, sizeof(short), 1, fp);
+ if (bytes_read != sizeof(short) && ferror(fp)) {
+ debugOut("Reading error in getSigned.\n");
+ }
+#ifdef __BIG_ENDIAN__
+ f = shortSwap(f);
+#endif
+ return f;
+}
+
+void putSigned(short f, FILE *fp) {
+#ifdef __BIG_ENDIAN__
+ f = shortSwap(f);
+#endif
+ fwrite(& f, sizeof(short), 1, fp);
+}
+
+
+// The following two functions treat signed integers as unsigned.
+// That's done on purpose.
+
+int32_t get4bytes(FILE *fp) {
+ int f1, f2, f3, f4;
+
+ f1 = fgetc(fp);
+ f2 = fgetc(fp);
+ f3 = fgetc(fp);
+ f4 = fgetc(fp);
+
+ unsigned int x = f1 + f2 * 256 + f3 * 256 * 256 + f4 * 256 * 256 * 256;
+
+ return x;
+
+ /*
+
+ int32_t f;
+ fread (& f, sizeof (int32_t), 1, fp);
+ return f;*/
+}
+
+
+void put4bytes(unsigned int i, FILE *fp) {
+ // fwrite (&i, sizeof (long int), 1, fp);
+ unsigned char f1, f2, f3, f4;
+
+ f4 = i / (256 * 256 * 256);
+ i = i % (256 * 256 * 256);
+ f3 = i / (256 * 256);
+ i = i % (256 * 256);
+ f2 = i / 256;
+ f1 = i % 256;
+
+ fputc(f1, fp);
+ fputc(f2, fp);
+ fputc(f3, fp);
+ fputc(f4, fp);
+}
+#endif
+char *encodeFilename(char *nameIn) {
+ if (! nameIn) return NULL;
+ if (allowAnyFilename) {
+ char *newName = new char[strlen(nameIn) * 2 + 1];
+ if (! checkNew(newName)) return NULL;
+
+ int i = 0;
+ while (*nameIn) {
+ switch (*nameIn) {
+ case '<':
+ newName[i++] = '_';
+ newName[i++] = 'L';
+ break;
+ case '>':
+ newName[i++] = '_';
+ newName[i++] = 'G';
+ break;
+ case '|':
+ newName[i++] = '_';
+ newName[i++] = 'P';
+ break;
+ case '_':
+ newName[i++] = '_';
+ newName[i++] = 'U';
+ break;
+ case '\"':
+ newName[i++] = '_';
+ newName[i++] = 'S';
+ break;
+ case '\\':
+ newName[i++] = '_';
+ newName[i++] = 'B';
+ break;
+ case '/':
+ newName[i++] = '_';
+ newName[i++] = 'F';
+ break;
+ case ':':
+ newName[i++] = '_';
+ newName[i++] = 'C';
+ break;
+ case '*':
+ newName[i++] = '_';
+ newName[i++] = 'A';
+ break;
+ case '?':
+ newName[i++] = '_';
+ newName[i++] = 'Q';
+ break;
+
+ default:
+ newName[i++] = *nameIn;
+ break;
+ }
+ newName[i] = 0;
+ nameIn ++;
+ }
+ return newName;
+ } else {
+ int a;
+ for (a = 0; nameIn[a]; a ++) {
+#ifdef _WIN32
+ if (nameIn[a] == '/') nameIn[a] = '\\';
+#else
+ if (nameIn[a] == '\\') nameIn[a] = '/';
+#endif
+ }
+
+ return copyString(nameIn);
+ }
+}
+
+char *decodeFilename(char *nameIn) {
+ if (allowAnyFilename) {
+ char *newName = new char[strlen(nameIn) + 1];
+ if (! checkNew(newName)) return NULL;
+
+ int i = 0;
+ while (* nameIn) {
+ if (* nameIn == '_') {
+ nameIn ++;
+ switch (* nameIn) {
+ case 'L':
+ newName[i] = '<';
+ nameIn ++;
+ break;
+ case 'G':
+ newName[i] = '>';
+ nameIn ++;
+ break;
+ case 'P':
+ newName[i] = '|';
+ nameIn ++;
+ break;
+ case 'U':
+ newName[i] = '_';
+ nameIn ++;
+ break;
+ case 'S':
+ newName[i] = '\"';
+ nameIn ++;
+ break;
+ case 'B':
+ newName[i] = '\\';
+ nameIn ++;
+ break;
+ case 'F':
+ newName[i] = '/';
+ nameIn ++;
+ break;
+ case 'C':
+ newName[i] = ':';
+ nameIn ++;
+ break;
+ case 'A':
+ newName[i] = '*';
+ nameIn ++;
+ break;
+ case 'Q':
+ newName[i] = '?';
+ nameIn ++;
+ break;
+ default:
+ newName[i] = '_';
+ }
+ } else {
+ newName[i] = *nameIn;
+ nameIn ++;
+ }
+ i ++;
+
+ }
+ newName[i] = 0;
+ return newName;
+ } else {
+ return copyString(nameIn);
+ }
+}
diff --git a/engines/sludge/moreio.h b/engines/sludge/moreio.h
new file mode 100644
index 0000000000..9635ffafcc
--- /dev/null
+++ b/engines/sludge/moreio.h
@@ -0,0 +1,42 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_MOREIO_H
+#define SLUDGE_MOREIO_H
+
+#include <stdint.h>
+
+#if ALLOW_FILE
+int get2bytes(FILE *fp);
+void put2bytes(int numtoput, FILE *fp);
+char *readString(FILE *fp);
+void writeString(char *s, FILE *fp);
+void putFloat(float f, FILE *fp);
+float getFloat(FILE *fp);
+void putSigned(short f, FILE *fp);
+short getSigned(FILE *fp);
+int32_t get4bytes(FILE *fp);
+void put4bytes(uint32_t f, FILE *fp);
+#endif
+char *encodeFilename(char *nameIn);
+char *decodeFilename(char *nameIn);
+
+#endif
diff --git a/engines/sludge/movie.cpp b/engines/sludge/movie.cpp
new file mode 100644
index 0000000000..debec5e9dc
--- /dev/null
+++ b/engines/sludge/movie.cpp
@@ -0,0 +1,1043 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#if 0
+#include <SDL/SDL.h>
+#endif
+
+#include <math.h>
+
+#include "CommonCode/specialsettings.h"
+
+#include "libwebm/mkvreader.hpp"
+#include "libwebm/mkvparser.hpp"
+
+#if 0
+#define VPX_CODEC_DISABLE_COMPAT 1
+#include "vpx/vpx_decoder.h"
+#include "vpx/vp8dx.h"
+#define interface (&vpx_codec_vp8_dx_algo)
+#endif
+
+#include "newfatal.h"
+#include "timing.h"
+#include "graphics.h"
+#include "movie.h"
+#include "shaders.h"
+
+#include "sound.h"
+#include "vorbis/codec.h"
+#define OV_EXCLUDE_STATIC_CALLBACKS
+#include "vorbis/vorbisfile.h"
+
+#if 0
+#include "ogg/ogg.h"
+#include "libvorbis/vorbis_os.h"
+
+#include "AL/alure.h"
+#endif
+
+extern int specialSettings;
+
+// in main.c
+int checkInput();
+extern int weAreDoneSoQuit;
+
+// Sludger.cpp
+bool handleInput();
+
+// sound_openal.cpp
+void playMovieStream(int a);
+#if 0
+int initMovieSound(int f, ALenum format, int audioChannels, ALuint samplerate,
+ ALuint(*callback)(void *userdata, ALubyte *data, ALuint bytes));
+#endif
+
+movieStates movieIsPlaying = nothing;
+
+int movieIsEnding = 0;
+
+float movieAspect = 1.6;
+#if 0
+typedef struct audioBuffers {
+ char *buffer;
+ unsigned int size;
+ audioBuffers *next;
+ Uint32 time_ms;
+} audioBuffers;
+
+typedef struct audioQueue {
+ audioBuffers *first, *last;
+ int size;
+ SDL_mutex *mutex;
+ SDL_cond *cond;
+} audioQueue;
+
+audioQueue audioQ;
+
+Uint32 movieStartTick, movieCurrentTime;
+
+long long audioNsPerByte;
+long long audioNsPlayed;
+long long audioNsBuffered;
+long long audioBufferLen;
+bool movieSoundPlaying = false;
+int movieAudioIndex;
+GLuint yTextureName = 0;
+GLuint uTextureName = 0;
+GLuint vTextureName = 0;
+
+typedef struct videoBuffers {
+ GLubyte *ytex;
+ GLubyte *utex;
+ GLubyte *vtex;
+ videoBuffers *next;
+ GLsizei w, h;
+ Uint32 time_ms;
+} videoBuffers;
+
+typedef struct videoQueue {
+ videoBuffers *first, *last;
+ int size;
+ SDL_mutex *mutex;
+ SDL_cond *cond;
+} videoQueue;
+
+videoQueue videoQ;
+
+
+void audio_queue_init(audioQueue *q) {
+ memset(q, 0, sizeof(audioQueue));
+
+ q->mutex = SDL_CreateMutex();
+ q->cond = SDL_CreateCond();
+
+}
+int audio_queue_put(audioQueue *q, char *buffer, unsigned int size, long long time_ms) {
+
+ audioBuffers *audioBuf = new audioBuffers;
+ if (!audioBuf)
+ return -1;
+ audioBuf->buffer = buffer;
+ audioBuf->next = NULL;
+ audioBuf->size = size;
+ audioBuf->time_ms = time_ms;
+
+ SDL_LockMutex(q->mutex);
+
+ if (!q->last)
+ q->first = audioBuf;
+ else
+ q->last->next = audioBuf;
+ q->last = audioBuf;
+ q->size ++;
+ SDL_CondSignal(q->cond);
+
+ SDL_UnlockMutex(q->mutex);
+
+ return 0;
+}
+inline static int audio_queue_get(audioQueue *q, char **buffer) {
+ int ret = 0;
+
+
+ audioBuffers *audioBuf;
+
+ SDL_LockMutex(q->mutex);
+
+ audioBuf = q->first;
+ if (audioBuf) {
+ // Synch video timer to audio
+ Uint32 tick = SDL_GetTicks() + 100;
+ if (abs((long int)((tick - movieStartTick) - (audioBuf->time_ms))) > 300) {
+ movieStartTick = tick - audioBuf->time_ms;
+ }
+
+ q->first = audioBuf->next;
+ if (!q->first)
+ q->last = NULL;
+ q->size--;
+ *buffer = audioBuf->buffer;
+ ret = audioBuf->size;
+ delete audioBuf;
+ }
+
+ SDL_UnlockMutex(q->mutex);
+
+ return ret;
+}
+
+void video_queue_init(videoQueue *q) {
+ memset(q, 0, sizeof(videoQueue));
+ q->mutex = SDL_CreateMutex();
+ q->cond = SDL_CreateCond();
+}
+int video_queue_put(videoQueue *q, GLubyte *ytex,
+ GLubyte *utex,
+ GLubyte *vtex,
+ GLsizei w, GLsizei h,
+ long long time_ms) {
+
+ videoBuffers *videoBuf = new videoBuffers;
+ if (!videoBuf)
+ return -1;
+ videoBuf->ytex = ytex;
+ videoBuf->utex = utex;
+ videoBuf->vtex = vtex;
+ videoBuf->next = NULL;
+ videoBuf->w = w;
+ videoBuf->h = h;
+ videoBuf->time_ms = time_ms;
+
+ SDL_LockMutex(q->mutex);
+
+ if (!q->last)
+ q->first = videoBuf;
+ else
+ q->last->next = videoBuf;
+ q->last = videoBuf;
+ q->size ++;
+ SDL_CondSignal(q->cond);
+
+ SDL_UnlockMutex(q->mutex);
+ return 0;
+}
+inline static int video_queue_get(videoQueue *q,
+ GLubyte **ytex,
+ GLubyte **utex,
+ GLubyte **vtex,
+ GLsizei *w, GLsizei *h) {
+ videoBuffers *videoBuf;
+ int ret = 0;
+
+ SDL_LockMutex(q->mutex);
+
+ videoBuf = q->first;
+ if (videoBuf) {
+ q->first = videoBuf->next;
+ if (!q->first)
+ q->last = NULL;
+ q->size--;
+ *ytex = videoBuf->ytex;
+ *utex = videoBuf->utex;
+ *vtex = videoBuf->vtex;
+ *w = videoBuf->w;
+ *h = videoBuf->h;
+ ret = 1;
+ delete videoBuf;
+ }
+
+ SDL_UnlockMutex(q->mutex);
+
+ return ret;
+}
+
+#if 0
+static void die_codec(vpx_codec_ctx_t *ctx, const char *s) {
+ //const char *detail = vpx_codec_error_detail(ctx);
+ fatal(s, vpx_codec_error(ctx));
+}
+#endif
+
+void setMovieViewport() {
+ float realAspect = (float) realWinWidth / realWinHeight;
+
+ int vpHeight, vpWidth, vpOffsetX, vpOffsetY;
+ if (realAspect > movieAspect) {
+ vpHeight = realWinHeight;
+ vpWidth = (int)(realWinHeight * movieAspect);
+ vpOffsetY = 0;
+ vpOffsetX = (realWinWidth - vpWidth) / 2;
+ } else {
+ vpWidth = realWinWidth;
+ vpHeight = (int)((float) realWinWidth / movieAspect);
+ vpOffsetY = (realWinHeight - vpHeight) / 2;
+ vpOffsetX = 0;
+ }
+#if 0
+ glViewport(vpOffsetX, vpOffsetY, vpWidth, vpHeight);
+#endif
+ const GLfloat bPMVMatrix[] = {
+ 2.0f / 640.f, .0, .0, .0,
+ .0, -2.0f / 400.f, .0, .0,
+ .0, .0, 1.0f, .0,
+ -1.0, 1.0f, .0, 1.0f
+
+ };
+ for (int i = 0; i < 16; i++) {
+ aPMVMatrix[i] = bPMVMatrix[i];
+ }
+}
+
+static uint64_t xiph_lace_value(unsigned char **np) {
+ uint64_t lace;
+ uint64_t value;
+ unsigned char *p = *np;
+
+ lace = *p++;
+ value = lace;
+ while (lace == 255) {
+ lace = *p++;
+ value += lace;
+ }
+
+ *np = p;
+
+ return value;
+}
+
+vorbis_dsp_state vorbisDspState;
+long long audioChannels;
+
+bool fakeAudio = false;
+
+// send audio to audio device...
+ALuint feedAudio(void *userdata, ALubyte *data, ALuint length) {
+ static char *buffer = NULL;
+ static unsigned int bufOffset = 0;
+ static unsigned int bufSize = 0;
+
+ ALuint got = 0;
+ int bufLen;
+
+ if (! buffer) {
+ bufSize = audio_queue_get(&audioQ, &buffer);
+ bufOffset = 0;
+ if (bufSize <= 0) {
+ bufSize = 0;
+ buffer = NULL;
+ if (! got) {
+ got = audioChannels * 2;
+ memset(data, 0, got);
+ fprintf(stderr, "Faking audio...\n");
+ fakeAudio = true;
+ }
+// SDL_CondSignal(audioQ.cond);
+ return got;
+ }
+ }
+
+ fakeAudio = false;
+
+ if (length > bufSize - bufOffset)
+ bufLen = bufSize - bufOffset;
+ else
+ bufLen = length;
+
+ memcpy(data, buffer + bufOffset, bufLen);
+
+ bufOffset += bufLen;
+ length -= bufLen;
+ got += bufLen;
+
+ if (bufSize <= bufOffset) {
+ buffer = NULL;
+ delete [] buffer;
+ }
+// fprintf (stderr, "Sending %d bytes of audio.\n", got);
+
+ return got;
+}
+#endif
+
+int playMovie(int fileNumber) {
+#if 0
+ if (specialSettings & SPECIAL_SILENT)
+ return 0;
+
+ if (movieIsPlaying) return 0;
+
+ movieSoundPlaying = false;
+
+ vpx_codec_ctx_t codec;
+
+ float pausefade = 1.0;
+
+ using namespace mkvparser;
+
+ MkvReader reader;
+
+ if (reader.Open(fileNumber)) {
+ warning(ERROR_MOVIE_ODDNESS);
+ return 0;
+ }
+
+ long long pos = 0;
+
+ EBMLHeader ebmlHeader;
+
+ ebmlHeader.Parse(&reader, pos);
+
+ mkvparser::Segment *pSegment;
+
+ long long ret = mkvparser::Segment::CreateInstance(&reader, pos, pSegment);
+ if (ret) {
+ fatal("Movie error: Segment::CreateInstance() failed.\n");
+ }
+
+ ret = pSegment->Load();
+ if (ret < 0) {
+ fatal("Movie error: Segment::Load() failed.\n");
+ }
+
+ //const SegmentInfo* const pSegmentInfo = pSegment->GetInfo();
+ //const long long timeCodeScale = pSegmentInfo->GetTimeCodeScale();
+ //const long long duration_ns = pSegmentInfo->GetDuration();
+ //const char* const pTitle = pSegmentInfo->GetTitleAsUTF8();
+ //const char* const pMuxingApp = pSegmentInfo->GetMuxingAppAsUTF8();
+ //const char* const pWritingApp = pSegmentInfo->GetWritingAppAsUTF8();
+
+ const mkvparser::Tracks *pTracks = pSegment->GetTracks();
+
+ unsigned long i = 0;
+ const unsigned long j = pTracks->GetTracksCount();
+
+ enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
+ int videoTrack = -1;
+ int audioTrack = -1;
+ long long audioBitDepth;
+ double audioSampleRate;
+ ogg_packet oggPacket;
+ vorbis_info vorbisInfo;
+ vorbis_comment vorbisComment;
+ vorbis_block vorbisBlock;
+
+ while (i != j) {
+ const Track *const pTrack = pTracks->GetTrackByIndex(i++);
+
+ if (pTrack == NULL)
+ continue;
+
+ const long long trackType = pTrack->GetType();
+ //const unsigned long long trackUid = pTrack->GetUid();
+ //const char* pTrackName = pTrack->GetNameAsUTF8();
+
+ if (trackType == VIDEO_TRACK && videoTrack < 0) {
+ videoTrack = pTrack->GetNumber();
+ const VideoTrack *const pVideoTrack =
+ static_cast<const VideoTrack *>(pTrack);
+
+ const long long width = pVideoTrack->GetWidth();
+ const long long height = pVideoTrack->GetHeight();
+
+ const double rate = pVideoTrack->GetFrameRate();
+
+ if (rate > 0)
+ Init_Special_Timer(rate);
+
+ movieAspect = (float)width / height;
+ }
+
+ if (trackType == AUDIO_TRACK && audioTrack < 0) {
+ audioTrack = pTrack->GetNumber();
+ const AudioTrack *const pAudioTrack =
+ static_cast<const AudioTrack *>(pTrack);
+
+ audioChannels = pAudioTrack->GetChannels();
+ audioBitDepth = pAudioTrack->GetBitDepth();
+ audioSampleRate = pAudioTrack->GetSamplingRate();
+
+ size_t audioHeaderSize;
+ const unsigned char *audioHeader = pAudioTrack->GetCodecPrivate(audioHeaderSize);
+
+ if (audioHeaderSize < 1) {
+ warning("Strange audio track in movie.");
+ audioTrack = -1;
+ continue;
+ }
+
+ unsigned char *p = (unsigned char *)audioHeader;
+
+ unsigned int count = *p++ + 1;
+ if (count != 3) {
+ warning("Strange audio track in movie.");
+ audioTrack = -1;
+ continue;
+ }
+
+ uint64_t sizes[3], total;
+
+ int i = 0;
+ total = 0;
+ while (--count) {
+ sizes[i] = xiph_lace_value(&p);
+ total += sizes[i];
+ i += 1;
+ }
+ sizes[i] = audioHeaderSize - total - (p - audioHeader);
+
+ // initialize vorbis
+ vorbis_info_init(&vorbisInfo);
+ vorbis_comment_init(&vorbisComment);
+ memset(&vorbisDspState, 0, sizeof(vorbisDspState));
+ memset(&vorbisBlock, 0, sizeof(vorbisBlock));
+
+ oggPacket.e_o_s = false;
+ oggPacket.granulepos = 0;
+ oggPacket.packetno = 0;
+ int r;
+ for (int i = 0; i < 3; i++) {
+ oggPacket.packet = p;
+ oggPacket.bytes = sizes[i];
+ oggPacket.b_o_s = oggPacket.packetno == 0;
+ r = vorbis_synthesis_headerin(&vorbisInfo, &vorbisComment, &oggPacket);
+ if (r)
+ fprintf(stderr, "vorbis_synthesis_headerin failed, error: %d", r);
+ oggPacket.packetno++;
+ p += sizes[i];
+ }
+
+ r = vorbis_synthesis_init(&vorbisDspState, &vorbisInfo);
+ if (r)
+ fprintf(stderr, "vorbis_synthesis_init failed, error: %d", r);
+ r = vorbis_block_init(&vorbisDspState, &vorbisBlock);
+ if (r)
+ fprintf(stderr, "vorbis_block_init failed, error: %d", r);
+
+ ALenum audioFormat = alureGetSampleFormat(audioChannels, 16, 0);
+ movieAudioIndex = initMovieSound(fileNumber, audioFormat, audioChannels, (ALuint) audioSampleRate, feedAudio);
+
+ fprintf(stderr, "Movie sound inited.\n");
+ audio_queue_init(&audioQ);
+ audioNsPerByte = (1000000000 / audioSampleRate) / (audioChannels * 2);
+ audioNsBuffered = 0;
+ audioBufferLen = audioChannels * audioSampleRate;
+ }
+ }
+
+ if (videoTrack < 0)
+ fatal("Movie error: No video in movie file.");
+
+ if (audioTrack < 0)
+ fatal("Movie error: No sound found.");
+
+ video_queue_init(&videoQ);
+
+ const unsigned long clusterCount = pSegment->GetCount();
+
+ if (clusterCount == 0) {
+ fatal("Movie error: Segment has no clusters.\n");
+ }
+
+
+ /* Initialize video codec */
+ if (vpx_codec_dec_init(&codec, interface, NULL, 0))
+ die_codec(&codec, "Failed to initialize decoder for movie.");
+
+
+ unsigned char *frame = new unsigned char[256 * 1024];
+ if (! checkNew(frame)) return false;
+
+ const mkvparser::Cluster *pCluster = pSegment->GetFirst();
+
+ setMovieViewport();
+
+ movieIsPlaying = playing;
+ movieIsEnding = 0;
+
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+
+ //const long long timeCode = pCluster->GetTimeCode();
+ long long time_ns = pCluster->GetTime();
+
+ const BlockEntry *pBlockEntry = pCluster->GetFirst();
+
+ if ((pBlockEntry == NULL) || pBlockEntry->EOS()) {
+ pCluster = pSegment->GetNext(pCluster);
+ if ((pCluster == NULL) || pCluster->EOS()) {
+ fatal("Error: No movie found in the movie file.");
+ }
+ pBlockEntry = pCluster->GetFirst();
+ }
+ const Block *pBlock = pBlockEntry->GetBlock();
+ long long trackNum = pBlock->GetTrackNumber();
+ unsigned long tn = static_cast<unsigned long>(trackNum);
+ const Track *pTrack = pTracks->GetTrackByNumber(tn);
+ long long trackType = pTrack->GetType();
+ int frameCount = pBlock->GetFrameCount();
+ time_ns = pBlock->GetTime(pCluster);
+
+ const GLfloat texCoords[] = {
+ 0.0, 0.0,
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 1.0, 1.0
+ };
+
+ const GLfloat vertices[] = {
+ 0.0, 0.0, 0.1,
+ 640.0, 0.0, 0.1,
+ 0.0, 400.0, 0.1,
+ 640.0, 400.0, 0.1
+ };
+
+ const GLfloat vertices1[] = {
+ 7.0, 7.0, 0.1,
+ 17.0, 7.0, 0.1,
+ 7.0, 29.0, 0.1,
+ 17.0, 29.0, 0.1
+ };
+
+ const GLfloat vertices2[] = {
+ 27.0, 7.0, 0.1,
+ 37.0, 7.0, 0.1,
+ 27.0, 29.0, 0.1,
+ 37.0, 29.0, 0.1
+ };
+
+ const GLfloat vertices3[] = {
+ 5.0, 5.0, 0.1,
+ 15.0, 5.0, 0.1,
+ 5.0, 27.0, 0.1,
+ 15.0, 27.0, 0.1
+ };
+
+ const GLfloat vertices4[] = {
+ 25.0, 5.0, 0.1,
+ 35.0, 5.0, 0.1,
+ 25.0, 27.0, 0.1,
+ 35.0, 27.0, 0.1
+ };
+
+ int frameCounter = 0;
+
+ movieStartTick = SDL_GetTicks();
+#ifdef HAVE_GLES2
+ GLuint old_fbo;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&old_fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+#endif
+
+ while (movieIsPlaying) {
+
+ checkInput();
+ if (weAreDoneSoQuit)
+ break;
+ handleInput();
+
+ if (movieIsPlaying && (! movieIsEnding) && (videoQ.size < 100 || audioQ.size < 100)) {
+ // Decode a frame!
+
+ if ((pCluster != NULL) && !pCluster->EOS()) {
+
+ if (frameCounter >= frameCount) {
+
+ pBlockEntry = pCluster->GetNext(pBlockEntry);
+ if ((pBlockEntry == NULL) || pBlockEntry->EOS()) {
+ pCluster = pSegment->GetNext(pCluster);
+ if ((pCluster == NULL) || pCluster->EOS()) {
+ goto movieHasEnded;
+ }
+ pBlockEntry = pCluster->GetFirst();
+ }
+ pBlock = pBlockEntry->GetBlock();
+ trackNum = pBlock->GetTrackNumber();
+ tn = static_cast<unsigned long>(trackNum);
+ pTrack = pTracks->GetTrackByNumber(tn);
+ trackType = pTrack->GetType();
+ frameCount = pBlock->GetFrameCount();
+ time_ns = pBlock->GetTime(pCluster);
+
+ frameCounter = 0;
+ }
+
+ const Block::Frame &theFrame = pBlock->GetFrame(frameCounter);
+ const long size = theFrame.len;
+ // const long long offset = theFrame.pos;
+
+ if (size > sizeof(frame)) {
+ if (frame) delete [] frame;
+ frame = new unsigned char[size];
+ if (! checkNew(frame)) return 0;
+ }
+ /*
+ fprintf (stderr, "Block :%s,%s,%15lld\n",
+ (trackType == VIDEO_TRACK) ? "V" : "A",
+ pBlock->IsKey() ? "I" : "P",
+ time_ns);
+ */
+
+ if (trackNum == videoTrack) {
+
+ theFrame.Read(&reader, frame);
+
+
+ /* Decode the frame */
+ if (vpx_codec_decode(&codec, frame, size, NULL, 0))
+ die_codec(&codec, "Failed to decode frame");
+
+ // Let's decode an image frame!
+ vpx_codec_iter_t iter = NULL;
+ vpx_image_t *img;
+ /* Get frame data */
+ while ((img = vpx_codec_get_frame(&codec, &iter))) {
+ if (img->fmt != VPX_IMG_FMT_I420)
+ fatal("Movie error. The movie is not in I420 colour format, which is the only one I can hanlde at the moment.");
+
+ unsigned int y;
+
+ GLubyte *ytex = NULL;
+ GLubyte *utex = NULL;
+ GLubyte *vtex = NULL;
+
+ if (! ytex) {
+ ytex = new GLubyte [img->d_w * img->d_h];
+ utex = new GLubyte [(img->d_w >> 1) * (img->d_h >> 1)];
+ vtex = new GLubyte [(img->d_w >> 1) * (img->d_h >> 1)];
+ if (!ytex || !utex || !vtex)
+ fatal(ERROR_OUT_OF_MEMORY);
+
+ }
+
+ unsigned char *buf = img->planes[0];
+ for (y = 0; y < img->d_h; y++) {
+ memcpy(ytex + y * img->d_w, buf, img->d_w);
+ buf += img->stride[0];
+ }
+ buf = img->planes[1];
+ for (y = 0; y < img->d_h >> 1; y++) {
+ memcpy(utex + y * (img->d_w >> 1), buf, img->d_w >> 1);
+ buf += img->stride[1];
+ }
+ buf = img->planes[2];
+ for (y = 0; y < img->d_h >> 1; y++) {
+ memcpy(vtex + y * (img->d_w >> 1), buf, img->d_w >> 1);
+ buf += img->stride[2];
+ }
+
+ video_queue_put(&videoQ, ytex, utex, vtex,
+ img->d_w, img->d_h, time_ns / 1000000);
+
+
+ }
+
+ } else if (trackNum == audioTrack) {
+ // Use this Audio Track
+ if (size > 0) {
+ theFrame.Read(&reader, frame);
+ oggPacket.packet = frame;
+ oggPacket.bytes = size;
+ oggPacket.b_o_s = false;
+ oggPacket.packetno++;
+ oggPacket.granulepos = -1;
+ if (! vorbis_synthesis(&vorbisBlock, &oggPacket)) {
+ if (vorbis_synthesis_blockin(&vorbisDspState, &vorbisBlock))
+ fprintf(stderr, "Vorbis Synthesis block in error.\n");
+
+ } else {
+ fprintf(stderr, "Vorbis Synthesis error.\n");
+ }
+
+ float **pcm;
+
+ int numSamples = vorbis_synthesis_pcmout(&vorbisDspState, &pcm);
+
+ if (numSamples > 0) {
+ int word = 2;
+ int sgned = 1;
+ int i, j;
+ long bytespersample = audioChannels * word;
+ vorbis_fpu_control fpu;
+
+ char *buffer = new char[bytespersample * numSamples];
+ if (! checkNew(buffer)) return false;
+
+ /* a tight loop to pack each size */
+ {
+ int val;
+ if (word == 1) {
+ int off = (sgned ? 0 : 128);
+ vorbis_fpu_setround(&fpu);
+ for (j = 0; j < numSamples; j++)
+ for (i = 0; i < audioChannels; i++) {
+ val = vorbis_ftoi(pcm[i][j] * 128.f);
+ if (val > 127)val = 127;
+ else if (val < -128)val = -128;
+ *buffer++ = val + off;
+ }
+ vorbis_fpu_restore(fpu);
+ } else {
+ int off = (sgned ? 0 : 32768);
+
+ if (sgned) {
+
+ vorbis_fpu_setround(&fpu);
+ for (i = 0; i < audioChannels; i++) { /* It's faster in this order */
+ float *src = pcm[i];
+ short *dest = ((short *)buffer) + i;
+ for (j = 0; j < numSamples; j++) {
+ val = vorbis_ftoi(src[j] * 32768.f);
+ if (val > 32767)val = 32767;
+ else if (val < -32768)val = -32768;
+ *dest = val;
+ dest += audioChannels;
+ }
+ }
+ vorbis_fpu_restore(fpu);
+
+ } else {
+
+ vorbis_fpu_setround(&fpu);
+ for (i = 0; i < audioChannels; i++) {
+ float *src = pcm[i];
+ short *dest = ((short *)buffer) + i;
+ for (j = 0; j < numSamples; j++) {
+ val = vorbis_ftoi(src[j] * 32768.f);
+ if (val > 32767)val = 32767;
+ else if (val < -32768)val = -32768;
+ *dest = val + off;
+ dest += audioChannels;
+ }
+ }
+ vorbis_fpu_restore(fpu);
+
+ }
+
+ }
+ }
+
+ vorbis_synthesis_read(&vorbisDspState, numSamples);
+ audioBufferLen = bytespersample * numSamples;
+ audio_queue_put(&audioQ, buffer, audioBufferLen, time_ns / 1000000);
+
+ //fprintf (stderr, "Audio buffered: %lld byte %lld ns\n",audioBufferLen, audioNsPerByte*audioBufferLen);
+
+ if (! movieSoundPlaying && size > 1) {
+ fprintf(stderr, "** starting sound ** \n");
+ playMovieStream(movieAudioIndex);
+ movieSoundPlaying = true;
+ }
+ }
+ }
+ }
+ ++frameCounter;
+
+ } else {
+movieHasEnded:
+ movieIsEnding = 1;
+ }
+ }
+
+ bool videoUpdated = false;
+ // Get a video frame.
+ if (movieIsPlaying == playing) {
+
+ videoBuffers *vB;
+ // Do we have decoded video waiting?
+ if (vB = videoQ.first) {
+ Uint32 tick = SDL_GetTicks() - movieStartTick;
+
+ // Is it time to display the frame yet?
+ if ((tick + 1) < vB->time_ms) {
+ SDL_Delay(1);
+ } else {
+ GLubyte *ytex = NULL;
+ GLubyte *utex = NULL;
+ GLubyte *vtex = NULL;
+ GLsizei w, h;
+
+ if (video_queue_get(&videoQ, &ytex, &utex, &vtex, &w, &h)) {
+
+ if (! yTextureName) glGenTextures(1, &yTextureName);
+ if (! uTextureName) glGenTextures(1, &uTextureName);
+ if (! vTextureName) glGenTextures(1, &vTextureName);
+ glBindTexture(GL_TEXTURE_2D, yTextureName);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, ytex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glBindTexture(GL_TEXTURE_2D, uTextureName);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w >> 1, h >> 1, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, utex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glBindTexture(GL_TEXTURE_2D, vTextureName);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w >> 1, h >> 1, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, vtex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+
+ glBindTexture(GL_TEXTURE_2D, yTextureName);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h,
+ GL_ALPHA, GL_UNSIGNED_BYTE, ytex);
+ glBindTexture(GL_TEXTURE_2D, uTextureName);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w >> 1, h >> 1,
+ GL_ALPHA, GL_UNSIGNED_BYTE, utex);
+ glBindTexture(GL_TEXTURE_2D, vTextureName);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w >> 1, h >> 1,
+ GL_ALPHA, GL_UNSIGNED_BYTE, vtex);
+
+ delete [] ytex;
+ delete [] utex;
+ delete [] vtex;
+ ytex = utex = vtex = NULL;
+ videoUpdated = true;
+
+ }
+ }
+ } else if (movieIsEnding) {
+ // We have reached the end of the movie.
+ movieIsPlaying = nothing;
+ }
+ }
+
+ // Update the screen if there's new video, or if we're paused
+ if (videoUpdated || movieIsPlaying == paused) {
+ // Clear The Screen
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Display the current frame here
+ if (shader.yuv) {
+ glUseProgram(shader.yuv);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, uTextureName);
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, vTextureName);
+ glActiveTexture(GL_TEXTURE0);
+ }
+ glBindTexture(GL_TEXTURE_2D, yTextureName);
+ glEnable(GL_BLEND);
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ setPMVMatrix(shader.yuv);
+ drawQuad(shader.yuv, vertices, 1, texCoords);
+
+ glUseProgram(0);
+
+ if (movieIsPlaying == paused) {
+ pausefade -= 1.0 / 24;
+ if (pausefade < -1.0) pausefade = 1.0;
+
+ // Paused.
+ glEnable(GL_BLEND);
+
+ glUseProgram(shader.color);
+
+ setPMVMatrix(shader.color);
+ setPrimaryColor(0.0f, 0.0f, 0.0f, fabs(pausefade));
+ drawQuad(shader.color, vertices1, 0);
+ drawQuad(shader.color, vertices2, 0);
+ setPrimaryColor(1.0f, 1.0f, 1.0f, fabs(pausefade));
+ drawQuad(shader.color, vertices3, 0);
+ drawQuad(shader.color, vertices4, 0);
+
+ glUseProgram(0);
+
+
+ glDisable(GL_BLEND);
+
+ Wait_Frame();
+ }
+
+ glFlush();
+#if !defined(HAVE_GLES2)
+ SDL_GL_SwapBuffers();
+#else
+ EGL_SwapBuffers();
+#endif
+
+ }
+ videoUpdated = false;
+ }
+
+ // Cleanup
+#ifdef HAVE_GLES2
+ glBindFramebuffer(GL_FRAMEBUFFER, old_fbo);
+
+ movieIsPlaying = nothing;
+ for (int i = 0; i < 10; i++) Wait_Frame();
+ huntKillFreeSound(fileNumber);
+
+
+ if (vpx_codec_destroy(&codec))
+ die_codec(&codec, "Failed to destroy codec");
+
+
+ vorbis_dsp_clear(&vorbisDspState);
+ vorbis_block_clear(&vorbisBlock);
+ vorbis_comment_clear(&vorbisComment);
+ vorbis_info_clear(&vorbisInfo);
+ delete pSegment;
+ deleteTextures(1, &yTextureName);
+ deleteTextures(1, &uTextureName);
+ deleteTextures(1, &vTextureName);
+ yTextureName = uTextureName = vTextureName = 0;
+
+ // Delete any remaining buffers
+ videoBuffers *vB = videoQ.first;
+ while (vB = videoQ.first) {
+ videoQ.first = vB->next;
+ delete [] vB->ytex;
+ delete [] vB->utex;
+ delete [] vB->vtex;
+ delete vB;
+ }
+ videoQ.size = 0;
+
+ audioBuffers *aB = audioQ.first;
+ while (aB = audioQ.first) {
+ audioQ.first = aB->next;
+ delete [] aB->buffer;
+ delete aB;
+ }
+ audioQ.size = 0;
+
+ Init_Timer();
+
+ glViewport(viewportOffsetX, viewportOffsetY, viewportWidth, viewportHeight);
+#endif
+ setPixelCoords(false);
+#endif
+ return 1;
+}
+
+int stopMovie() {
+ int r = movieIsPlaying;
+ movieIsPlaying = nothing;
+ return r;
+}
+
+int pauseMovie() {
+#if 0
+ if (movieIsPlaying == playing) {
+ ALuint source = getSoundSource(movieAudioIndex);
+ if (source) {
+
+ alurePauseSource(source);
+
+ }
+ movieIsPlaying = paused;
+ fprintf(stderr, "** Pausing **\n");
+ } else if (movieIsPlaying == paused) {
+ ALuint source = getSoundSource(movieAudioIndex);
+ if (source) {
+
+ alureResumeSource(source);
+
+ }
+ fprintf(stderr, "** Restarted movie ** sound: %d source: %d\n", movieSoundPlaying, source);
+ movieIsPlaying = playing;
+ }
+#endif
+ return movieIsPlaying;
+}
diff --git a/engines/sludge/movie.h b/engines/sludge/movie.h
new file mode 100644
index 0000000000..460edb6e99
--- /dev/null
+++ b/engines/sludge/movie.h
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_MOVIE_H
+#define SLUDGE_MOVIE_H
+/*
+ movieIsPlaying tracks the state of movie playing
+ */
+enum movieStates {
+ nothing = 0,
+ playing,
+ paused
+};
+extern movieStates movieIsPlaying;
+
+int playMovie(int fileNumber);
+int stopMovie();
+int pauseMovie();
+
+#endif
diff --git a/engines/sludge/newfatal.cpp b/engines/sludge/newfatal.cpp
new file mode 100644
index 0000000000..b3fa74af6c
--- /dev/null
+++ b/engines/sludge/newfatal.cpp
@@ -0,0 +1,158 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#if 0
+#include <SDL/SDL.h>
+
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+#include "common/debug.h"
+
+#include "platform-dependent.h"
+#include "allfiles.h"
+#include "CommonCode/version.h"
+
+#include "sound.h"
+
+#include "stringy.h"
+#include "errors.h"
+#include "graphics.h"
+
+const char emergencyMemoryMessage[] = "Out of memory displaying error message!";
+
+static char *fatalMessage = NULL;
+static char *fatalInfo = joinStrings("Initialisation error! Something went wrong before we even got started!", "");
+
+extern int numResourceNames /* = 0*/;
+extern char * *allResourceNames /*= NULL*/;
+
+int resourceForFatal = -1;
+
+const char *resourceNameFromNum(int i) {
+ if (i == -1) return NULL;
+ if (numResourceNames == 0) return "RESOURCE";
+ if (i < numResourceNames) return allResourceNames[i];
+ return "Unknown resource";
+}
+
+bool hasFatal() {
+ if (fatalMessage) return true;
+ return false;
+}
+
+void displayFatal() {
+ if (fatalMessage) {
+#if 0
+ msgBox("SLUDGE v" TEXT_VERSION " fatal error!", fatalMessage);
+#endif
+ }
+}
+
+void warning(const char *l) {
+#if 0
+ setGraphicsWindow(false);
+ msgBox("SLUDGE v" TEXT_VERSION " non-fatal indigestion report", l);
+#endif
+}
+
+void registerWindowForFatal() {
+ delete fatalInfo;
+ fatalInfo = joinStrings("There's an error with this SLUDGE game! If you're designing this game, please turn on verbose error messages in the project manager and recompile. If not, please contact the author saying where and how this problem occured.", "");
+}
+
+#if 0
+extern SDL_Event quit_event;
+#endif
+
+int inFatal(const char *str) {
+#if ALLOW_FILE
+ FILE *fatFile = fopen("fatal.txt", "wt");
+ if (fatFile) {
+ fprintf(fatFile, "FATAL:\n%s\n", str);
+ fclose(fatFile);
+ }
+#endif
+ fatalMessage = copyString(str);
+ if (fatalMessage == NULL) fatalMessage = copyString("Out of memory");
+
+#if 0
+ killSoundStuff();
+#endif
+
+#if defined(HAVE_GLES2)
+ EGL_Close();
+#endif
+
+#if 0
+ SDL_Quit();
+
+ atexit(displayFatal);
+ exit(1);
+#endif
+}
+
+int checkNew(const void *mem) {
+ if (mem == NULL) {
+ inFatal(ERROR_OUT_OF_MEMORY);
+ return 0;
+ }
+ return 1;
+}
+
+void setFatalInfo(const char *userFunc, const char *BIF) {
+ delete fatalInfo;
+ fatalInfo = new char [strlen(userFunc) + strlen(BIF) + 38];
+ if (fatalInfo) sprintf(fatalInfo, "Currently in this sub: %s\nCalling: %s", userFunc, BIF);
+ debug("%s\n", fatalInfo);
+}
+
+void setResourceForFatal(int n) {
+ resourceForFatal = n;
+}
+
+int fatal(const char *str1) {
+ if (numResourceNames && resourceForFatal != -1) {
+ const char *r = resourceNameFromNum(resourceForFatal);
+ char *newStr = new char[strlen(str1) + strlen(r) + strlen(fatalInfo) + 14];
+ if (checkNew(newStr)) {
+ sprintf(newStr, "%s\nResource: %s\n\n%s", fatalInfo, r, str1);
+ inFatal(newStr);
+ } else fatal(emergencyMemoryMessage);
+ } else {
+ char *newStr = new char[strlen(str1) + strlen(fatalInfo) + 3];
+ if (checkNew(newStr)) {
+ sprintf(newStr, "%s\n\n%s", fatalInfo, str1);
+ inFatal(newStr);
+ } else fatal(emergencyMemoryMessage);
+ }
+ return 0;
+}
+
+int fatal(const char *str1, const char *str2) {
+ char *newStr = new char[strlen(str1) + strlen(str2) + 2];
+ if (checkNew(newStr)) {
+ sprintf(newStr, "%s %s", str1, str2);
+ fatal(newStr);
+ } else fatal(emergencyMemoryMessage);
+ return 0;
+}
diff --git a/engines/sludge/newfatal.h b/engines/sludge/newfatal.h
new file mode 100644
index 0000000000..6fd5b2f0d5
--- /dev/null
+++ b/engines/sludge/newfatal.h
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_NEWFATAL_H
+#define SLUDGE_NEWFATAL_H
+
+#include "errors.h"
+
+bool hasFatal();
+
+int fatal(const char *str);
+int fatal(const char *str1, const char *str2);
+int checkNew(const void *mem);
+void displayFatal();
+void registerWindowForFatal();
+void setFatalInfo(const char *userFunc, const char *BIF);
+void warning(const char *l);
+void setResourceForFatal(int n);
+char *resourceNameFromNum(int i);
+
+#endif
diff --git a/engines/sludge/objtypes.cpp b/engines/sludge/objtypes.cpp
new file mode 100644
index 0000000000..7f19ee74d3
--- /dev/null
+++ b/engines/sludge/objtypes.cpp
@@ -0,0 +1,177 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+#include "objtypes.h"
+#include "variable.h"
+#include "newfatal.h"
+#include "moreio.h"
+#include "fileset.h"
+#include "CommonCode/version.h"
+
+objectType *allObjectTypes = NULL;
+extern char *outputDir;
+
+#define DEBUG_COMBINATIONS 0
+
+bool initObjectTypes() {
+ return true;
+}
+
+objectType *findObjectType(int i) {
+ objectType *huntType = allObjectTypes;
+
+ while (huntType) {
+ if (huntType -> objectNum == i) return huntType;
+ huntType = huntType -> next;
+ }
+
+ return loadObjectType(i);
+}
+
+objectType *loadObjectType(int i) {
+#if ALLOW_FILE
+ int a, nameNum;
+ objectType *newType = new objectType;
+
+ if (checkNew(newType)) {
+ if (openObjectSlice(i)) {
+ nameNum = get2bytes(bigDataFile);
+ newType -> r = (byte) fgetc(bigDataFile);
+ newType -> g = (byte) fgetc(bigDataFile);
+ newType -> b = (byte) fgetc(bigDataFile);
+ newType -> speechGap = fgetc(bigDataFile);
+ newType -> walkSpeed = fgetc(bigDataFile);
+ newType -> wrapSpeech = get4bytes(bigDataFile);
+ newType -> spinSpeed = get2bytes(bigDataFile);
+
+ if (gameVersion >= VERSION(1, 6)) {
+ // aaLoad
+ fgetc(bigDataFile);
+ getFloat(bigDataFile);
+ getFloat(bigDataFile);
+ }
+
+ if (gameVersion >= VERSION(1, 4)) {
+ newType -> flags = get2bytes(bigDataFile);
+ } else {
+ newType -> flags = 0;
+ }
+
+ newType -> numCom = get2bytes(bigDataFile);
+ newType -> allCombis = (newType -> numCom) ? new combination[newType -> numCom] : NULL;
+
+#if DEBUG_COMBINATIONS
+ FILE *callEventLog = fopen("callEventLog.txt", "at");
+ if (callEventLog) {
+ fprintf(callEventLog, "Object type %d has %d combinations... ", i, newType -> numCom);
+ }
+#endif
+
+ for (a = 0; a < newType -> numCom; a ++) {
+ newType -> allCombis[a].withObj = get2bytes(bigDataFile);
+ newType -> allCombis[a].funcNum = get2bytes(bigDataFile);
+#if DEBUG_COMBINATIONS
+ if (callEventLog) {
+ fprintf(callEventLog, "%d(%d) ", newType -> allCombis[a].withObj, newType -> allCombis[a].funcNum);
+ }
+#endif
+ }
+#if DEBUG_COMBINATIONS
+ if (callEventLog) {
+ fprintf(callEventLog, "\n");
+ fclose(callEventLog);
+ }
+#endif
+ finishAccess();
+ newType -> screenName = getNumberedString(nameNum);
+ newType -> objectNum = i;
+ newType -> next = allObjectTypes;
+ allObjectTypes = newType;
+ return newType;
+ }
+ }
+#endif
+ return NULL;
+}
+
+#if ALLOW_FILE
+objectType *loadObjectRef(FILE *fp) {
+ objectType *r = loadObjectType(get2bytes(fp));
+ delete r -> screenName;
+ r -> screenName = readString(fp);
+ return r;
+}
+
+void saveObjectRef(objectType *r, FILE *fp) {
+ put2bytes(r -> objectNum, fp);
+ writeString(r -> screenName, fp);
+}
+#endif
+
+int getCombinationFunction(int withThis, int thisObject) {
+ int i, num = 0;
+ objectType *obj = findObjectType(thisObject);
+
+#if DEBUG_COMBINATIONS
+ FILE *callEventLog = fopen("callEventLog.txt", "at");
+ if (callEventLog) {
+ fprintf(callEventLog, "Combining %d and %d - ", thisObject, withThis);
+ }
+#endif
+
+ for (i = 0; i < obj -> numCom; i ++) {
+ if (obj -> allCombis[i].withObj == withThis) {
+ num = obj -> allCombis[i].funcNum;
+ break;
+ }
+ }
+
+#if DEBUG_COMBINATIONS
+ if (callEventLog) {
+ fprintf(callEventLog, "got function number %d\n", num);
+ fclose(callEventLog);
+ }
+#endif
+
+ return num;
+}
+
+void removeObjectType(objectType *oT) {
+ objectType * * huntRegion = & allObjectTypes;
+
+ while (* huntRegion) {
+ if ((* huntRegion) == oT) {
+// FILE * debuggy2 = fopen ("debug.txt", "at");
+// fprintf (debuggy2, "DELETING OBJECT TYPE: %p %s\n", oT, oT -> screenName);
+// fclose (debuggy2);
+
+ * huntRegion = oT -> next;
+ delete oT -> allCombis;
+ delete oT -> screenName;
+ delete oT;
+ return;
+ } else {
+ huntRegion = & ((* huntRegion) -> next);
+ }
+ }
+ fatal("Can't delete object type: bad pointer");
+}
diff --git a/engines/sludge/objtypes.h b/engines/sludge/objtypes.h
new file mode 100644
index 0000000000..b56b112f94
--- /dev/null
+++ b/engines/sludge/objtypes.h
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_OBJTYPES_H
+#define SLUDGE_OBJTYPES_H
+
+struct combination {
+ int withObj, funcNum;
+};
+
+struct objectType {
+ char *screenName;
+ int objectNum;
+ objectType *next;
+ unsigned char r, g, b;
+ int numCom;
+ int speechGap, walkSpeed, wrapSpeech, spinSpeed;
+ unsigned short int flags;
+ combination *allCombis;
+};
+
+bool initObjectTypes();
+objectType *findObjectType(int i);
+objectType *loadObjectType(int i);
+int getCombinationFunction(int a, int b);
+void removeObjectType(objectType *oT);
+#if ALLOW_FILE
+void saveObjectRef(objectType *r, FILE *fp);
+objectType *loadObjectRef(FILE *fp);
+#endif
+#endif
diff --git a/engines/sludge/people.cpp b/engines/sludge/people.cpp
new file mode 100644
index 0000000000..3853bbe6c6
--- /dev/null
+++ b/engines/sludge/people.cpp
@@ -0,0 +1,1177 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+#include <math.h>
+#include <stdlib.h>
+
+#include "CommonCode/version.h"
+#include "sprites.h"
+#include "sprbanks.h"
+#include "sludger.h"
+#include "objtypes.h"
+#include "region.h"
+#include "people.h"
+#include "talk.h"
+#include "newfatal.h"
+#include "variable.h"
+#include "moreio.h"
+#include "loadsave.h"
+#include "floor.h"
+#include "zbuffer.h"
+#include "sound.h"
+#include "debug.h"
+
+#define ANGLEFIX (180.0 / 3.14157)
+#define ANI_STAND 0
+#define ANI_WALK 1
+#define ANI_TALK 2
+
+extern speechStruct *speech;
+
+extern variableStack *noStack;
+
+extern int ssgVersion;
+
+extern int cameraX, cameraY;
+screenRegion personRegion;
+extern screenRegion *lastRegion;
+extern flor *currentFloor;
+
+extern inputType input;
+onScreenPerson *allPeople = NULL;
+short int scaleHorizon = 75;
+short int scaleDivide = 150;
+extern screenRegion *allScreenRegions;
+
+#define TF_max(a, b) ((a > b) ? a : b)
+
+inline int TF_abs(int a) {
+ return (a > 0) ? a : -a;
+}
+
+void setFrames(onScreenPerson &m, int a) {
+ m.myAnim = m.myPersona -> animation[(a * m.myPersona -> numDirections) + m.direction];
+}
+
+personaAnimation *createPersonaAnim(int num, variableStack *&stacky) {
+ personaAnimation *newP = new personaAnimation;
+ checkNew(newP);
+
+ newP -> numFrames = num;
+ newP -> frames = new animFrame[num];
+ checkNew(newP -> frames);
+
+ int a = num, frameNum, howMany;
+
+ while (a) {
+ a --;
+ newP -> frames[a].noise = 0;
+ if (stacky -> thisVar.varType == SVT_FILE) {
+ newP -> frames[a].noise = stacky -> thisVar.varData.intValue;
+ } else if (stacky -> thisVar.varType == SVT_FUNC) {
+ newP -> frames[a].noise = - stacky -> thisVar.varData.intValue;
+ } else if (stacky -> thisVar.varType == SVT_STACK) {
+ getValueType(frameNum, SVT_INT, stacky -> thisVar.varData.theStack -> first -> thisVar);
+ getValueType(howMany, SVT_INT, stacky -> thisVar.varData.theStack -> first -> next -> thisVar);
+ } else {
+ getValueType(frameNum, SVT_INT, stacky -> thisVar);
+ howMany = 1;
+ }
+ trimStack(stacky);
+ newP -> frames[a].frameNum = frameNum;
+ newP -> frames[a].howMany = howMany;
+ }
+
+ return newP;
+}
+
+personaAnimation *makeNullAnim() {
+ personaAnimation *newAnim = new personaAnimation;
+ if (! checkNew(newAnim)) return NULL;
+
+
+ newAnim -> theSprites = NULL;
+ newAnim -> numFrames = 0;
+ newAnim -> frames = NULL;
+ return newAnim;
+}
+
+personaAnimation *copyAnim(personaAnimation *orig) {
+ int num = orig -> numFrames;
+
+ personaAnimation *newAnim = new personaAnimation;
+ if (! checkNew(newAnim)) return NULL;
+
+ // Copy the easy bits...
+ newAnim -> theSprites = orig -> theSprites;
+ newAnim -> numFrames = num;
+
+ if (num) {
+
+ // Argh! Frames! We need a whole NEW array of animFrame structures...
+
+ newAnim->frames = new animFrame[num];
+ if (! checkNew(newAnim->frames)) return NULL;
+
+ for (int a = 0; a < num; a ++) {
+ newAnim -> frames[a].frameNum = orig -> frames[a].frameNum;
+ newAnim -> frames[a].howMany = orig -> frames[a].howMany;
+ newAnim -> frames[a].noise = orig -> frames[a].noise;
+ }
+ } else {
+ newAnim -> frames = NULL;
+ }
+
+ return newAnim;
+}
+
+void deleteAnim(personaAnimation *orig) {
+
+ if (orig) {
+ if (orig -> numFrames) {
+ delete[] orig -> frames;
+ }
+ delete orig;
+ orig = NULL;
+ }
+}
+
+void turnMeAngle(onScreenPerson *thisPerson, int direc) {
+ int d = thisPerson -> myPersona -> numDirections;
+ thisPerson -> angle = direc;
+ direc += (180 / d) + 180 + thisPerson -> angleOffset;
+ while (direc >= 360) direc -= 360;
+ thisPerson -> direction = (direc * d) / 360;
+}
+
+bool initPeople() {
+ personRegion.sX = 0;
+ personRegion.sY = 0;
+ personRegion.di = -1;
+ allScreenRegions = NULL;
+
+ return true;
+}
+
+void spinStep(onScreenPerson *thisPerson) {
+ int diff = (thisPerson -> angle + 360) - thisPerson -> wantAngle;
+ int eachSlice = thisPerson -> spinSpeed ? thisPerson -> spinSpeed : (360 / thisPerson -> myPersona -> numDirections);
+ while (diff > 180) {
+ diff -= 360;
+ }
+
+ if (diff >= eachSlice) {
+ turnMeAngle(thisPerson, thisPerson -> angle - eachSlice);
+ } else if (diff <= - eachSlice) {
+ turnMeAngle(thisPerson, thisPerson -> angle + eachSlice);
+ } else {
+ turnMeAngle(thisPerson, thisPerson -> wantAngle);
+ thisPerson -> spinning = false;
+ }
+}
+
+void rethinkAngle(onScreenPerson *thisPerson) {
+ int d = thisPerson -> myPersona -> numDirections;
+ int direc = thisPerson -> angle + (180 / d) + 180 + thisPerson -> angleOffset;
+ while (direc >= 360) direc -= 360;
+ thisPerson -> direction = (direc * d) / 360;
+}
+
+bool turnPersonToFace(int thisNum, int direc) {
+ onScreenPerson *thisPerson = findPerson(thisNum);
+ if (thisPerson) {
+ if (thisPerson -> continueAfterWalking) abortFunction(thisPerson -> continueAfterWalking);
+ thisPerson -> continueAfterWalking = NULL;
+ thisPerson -> walking = false;
+ thisPerson -> spinning = false;
+ turnMeAngle(thisPerson, direc);
+ setFrames(* thisPerson, (thisPerson == speech->currentTalker) ? ANI_TALK : ANI_STAND);
+ return true;
+ }
+ return false;
+}
+
+bool setPersonExtra(int thisNum, int extra) {
+ onScreenPerson *thisPerson = findPerson(thisNum);
+ if (thisPerson) {
+ thisPerson -> extra = extra;
+ if (extra & EXTRA_NOSCALE) thisPerson -> scale = 1;
+ return true;
+ }
+ return false;
+}
+
+void setScale(short int h, short int d) {
+ scaleHorizon = h;
+ scaleDivide = d;
+}
+
+void moveAndScale(onScreenPerson &me, float x, float y) {
+ me.x = x;
+ me.y = y;
+ if (!(me.extra & EXTRA_NOSCALE) && scaleDivide) me.scale = (me.y - scaleHorizon) / scaleDivide;
+}
+
+onScreenPerson *findPerson(int v) {
+ onScreenPerson *thisPerson = allPeople;
+ while (thisPerson) {
+ if (v == thisPerson -> thisType -> objectNum) break;
+ thisPerson = thisPerson -> next;
+ }
+ return thisPerson;
+}
+
+void movePerson(int x, int y, int objNum) {
+ onScreenPerson *moveMe = findPerson(objNum);
+ if (moveMe) moveAndScale(* moveMe, x, y);
+}
+
+void setShown(bool h, int ob) {
+ onScreenPerson *moveMe = findPerson(ob);
+ if (moveMe) moveMe -> show = h;
+}
+
+enum drawModes {
+ drawModeNormal,
+ drawModeTransparent1,
+ drawModeTransparent2,
+ drawModeTransparent3,
+ drawModeDark1,
+ drawModeDark2,
+ drawModeDark3,
+ drawModeBlack,
+ drawModeShadow1,
+ drawModeShadow2,
+ drawModeShadow3,
+ drawModeFoggy1,
+ drawModeFoggy2,
+ drawModeFoggy3,
+ drawModeFoggy4,
+ drawModeGlow1,
+ drawModeGlow2,
+ drawModeGlow3,
+ drawModeGlow4,
+ drawModeInvisible,
+ numDrawModes
+};
+
+void setMyDrawMode(onScreenPerson *moveMe, int h) {
+ switch (h) {
+ case drawModeTransparent3:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 0;
+ moveMe->transparency = 64;
+ break;
+ case drawModeTransparent2:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 0;
+ moveMe->transparency = 128;
+ break;
+ case drawModeTransparent1:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 0;
+ moveMe->transparency = 192;
+ break;
+ case drawModeInvisible:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 0;
+ moveMe->transparency = 254;
+ break;
+ case drawModeDark1:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 192;
+ moveMe->transparency = 0;
+ break;
+ case drawModeDark2:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 128;
+ moveMe->transparency = 0;
+ break;
+ case drawModeDark3:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 64;
+ moveMe->transparency = 0;
+ break;
+ case drawModeBlack:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 255;
+ moveMe->transparency = 0;
+ break;
+ case drawModeShadow1:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 255;
+ moveMe->transparency = 64;
+ break;
+ case drawModeShadow2:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 255;
+ moveMe->transparency = 128;
+ break;
+ case drawModeShadow3:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 255;
+ moveMe->transparency = 192;
+ break;
+ case drawModeFoggy3:
+ moveMe->r = moveMe->g = moveMe->b = 128;
+ moveMe->colourmix = 192;
+ moveMe->transparency = 0;
+ break;
+ case drawModeFoggy2:
+ moveMe->r = moveMe->g = moveMe->b = 128;
+ moveMe->colourmix = 128;
+ moveMe->transparency = 0;
+ break;
+ case drawModeFoggy1:
+ moveMe->r = moveMe->g = moveMe->b = 128;
+ moveMe->colourmix = 64;
+ moveMe->transparency = 0;
+ break;
+ case drawModeFoggy4:
+ moveMe->r = moveMe->g = moveMe->b = 128;
+ moveMe->colourmix = 255;
+ moveMe->transparency = 0;
+ break;
+ case drawModeGlow3:
+ moveMe->r = moveMe->g = moveMe->b = 255;
+ moveMe->colourmix = 192;
+ moveMe->transparency = 0;
+ break;
+ case drawModeGlow2:
+ moveMe->r = moveMe->g = moveMe->b = 255;
+ moveMe->colourmix = 128;
+ moveMe->transparency = 0;
+ break;
+ case drawModeGlow1:
+ moveMe->r = moveMe->g = moveMe->b = 255;
+ moveMe->colourmix = 64;
+ moveMe->transparency = 0;
+ break;
+ case drawModeGlow4:
+ moveMe->r = moveMe->g = moveMe->b = 255;
+ moveMe->colourmix = 255;
+ moveMe->transparency = 0;
+ break;
+ default:
+ moveMe->r = moveMe->g = moveMe->b = 0;
+ moveMe->colourmix = 0;
+ moveMe->transparency = 0;
+ break;
+ }
+
+}
+
+void setDrawMode(int h, int ob) {
+ onScreenPerson *moveMe = findPerson(ob);
+ if (! moveMe) return;
+
+ setMyDrawMode(moveMe, h);
+}
+
+void setPersonTransparency(int ob, unsigned char x) {
+ onScreenPerson *moveMe = findPerson(ob);
+ if (! moveMe) return;
+
+ if (x > 254) x = 254;
+ moveMe->transparency = x;
+}
+
+void setPersonColourise(int ob, unsigned char r, unsigned char g, unsigned char b, unsigned char colourmix) {
+ onScreenPerson *moveMe = findPerson(ob);
+ if (! moveMe) return;
+
+ moveMe->r = r;
+ moveMe->g = g;
+ moveMe->b = b;
+ moveMe->colourmix = colourmix;
+}
+
+
+
+extern screenRegion *overRegion;
+
+void shufflePeople() {
+ onScreenPerson * * thisReference = & allPeople;
+ onScreenPerson *A, * B;
+
+ if (! allPeople) return;
+
+ while ((* thisReference) -> next) {
+ float y1 = (* thisReference) -> y;
+ if ((* thisReference) -> extra & EXTRA_FRONT) y1 += 1000;
+
+ float y2 = (* thisReference) -> next -> y;
+ if ((* thisReference) -> next -> extra & EXTRA_FRONT) y2 += 1000;
+
+ if (y1 > y2) {
+ A = (* thisReference);
+ B = (* thisReference) -> next;
+ A -> next = B -> next;
+ B -> next = A;
+ (* thisReference) = B;
+ } else {
+ thisReference = & ((* thisReference) -> next);
+ }
+ }
+}
+
+
+
+void drawPeople() {
+ shufflePeople();
+
+ onScreenPerson *thisPerson = allPeople;
+ personaAnimation *myAnim = NULL;
+ overRegion = NULL;
+
+ while (thisPerson) {
+ if (thisPerson -> show) {
+ myAnim = thisPerson -> myAnim;
+ if (myAnim != thisPerson -> lastUsedAnim) {
+ thisPerson -> lastUsedAnim = myAnim;
+ thisPerson -> frameNum = 0;
+ thisPerson -> frameTick = myAnim -> frames[0].howMany;
+ if (myAnim -> frames[thisPerson -> frameNum].noise > 0) {
+ startSound(myAnim -> frames[thisPerson -> frameNum].noise, false);
+ thisPerson -> frameNum ++;
+ thisPerson -> frameNum %= thisPerson -> myAnim -> numFrames;
+ thisPerson -> frameTick = thisPerson -> myAnim -> frames[thisPerson -> frameNum].howMany;
+ } else if (myAnim -> frames[thisPerson -> frameNum].noise) {
+ startNewFunctionNum(- myAnim -> frames[thisPerson -> frameNum].noise, 0, NULL, noStack);
+ thisPerson -> frameNum ++;
+ thisPerson -> frameNum %= thisPerson -> myAnim -> numFrames;
+ thisPerson -> frameTick = thisPerson -> myAnim -> frames[thisPerson -> frameNum].howMany;
+ }
+ }
+ int fNumSign = myAnim -> frames[thisPerson -> frameNum].frameNum;
+ int m = fNumSign < 0;
+ int fNum = abs(fNumSign);
+ if (fNum >= myAnim -> theSprites -> bank.total) {
+ fNum = 0;
+ m = 2 - m;
+ }
+ if (m != 2) {
+ bool r = false;
+ r = scaleSprite(myAnim->theSprites->bank.sprites[fNum], myAnim -> theSprites -> bank.myPalette, thisPerson, m);
+ if (r) {
+ if (thisPerson -> thisType -> screenName[0]) {
+ if (personRegion.thisType != thisPerson -> thisType) lastRegion = NULL;
+ personRegion.thisType = thisPerson -> thisType;
+ overRegion = & personRegion;
+ }
+ }
+ }
+ }
+ if (! -- thisPerson -> frameTick) {
+ thisPerson -> frameNum ++;
+ thisPerson -> frameNum %= thisPerson -> myAnim -> numFrames;
+ thisPerson -> frameTick = thisPerson -> myAnim -> frames[thisPerson -> frameNum].howMany;
+ if (thisPerson -> show && myAnim && myAnim -> frames) {
+ if (myAnim -> frames[thisPerson -> frameNum].noise > 0) {
+ startSound(myAnim -> frames[thisPerson -> frameNum].noise, false);
+ thisPerson -> frameNum ++;
+ thisPerson -> frameNum %= thisPerson -> myAnim -> numFrames;
+ thisPerson -> frameTick = thisPerson -> myAnim -> frames[thisPerson -> frameNum].howMany;
+ } else if (myAnim -> frames[thisPerson -> frameNum].noise) {
+ startNewFunctionNum(- myAnim -> frames[thisPerson -> frameNum].noise, 0, NULL, noStack);
+ thisPerson -> frameNum ++;
+ thisPerson -> frameNum %= thisPerson -> myAnim -> numFrames;
+ thisPerson -> frameTick = thisPerson -> myAnim -> frames[thisPerson -> frameNum].howMany;
+ }
+ }
+ }
+ thisPerson = thisPerson -> next;
+ }
+}
+
+void makeTalker(onScreenPerson &me) {
+ setFrames(me, ANI_TALK);
+}
+
+void makeSilent(onScreenPerson &me) {
+ setFrames(me, ANI_STAND);
+}
+
+bool handleClosestPoint(int &setX, int &setY, int &setPoly) {
+ int gotX = 320, gotY = 200, gotPoly = -1, i, j, xTest1, yTest1,
+ xTest2, yTest2, closestX, closestY, oldJ, currentDistance = 0xFFFFF,
+ thisDistance;
+
+// FILE * dbug = fopen ("debug_closest.txt", "at");
+// fprintf (dbug, "\nGetting closest point to %i, %i\n", setX, setY);
+
+ for (i = 0; i < currentFloor -> numPolygons; i ++) {
+ oldJ = currentFloor -> polygon[i].numVertices - 1;
+ for (j = 0; j < currentFloor -> polygon[i].numVertices; j ++) {
+// fprintf (dbug, "Polygon %i, line %i... ", i, j);
+ xTest1 = currentFloor -> vertex[currentFloor -> polygon[i].vertexID[j]].x;
+ yTest1 = currentFloor -> vertex[currentFloor -> polygon[i].vertexID[j]].y;
+ xTest2 = currentFloor -> vertex[currentFloor -> polygon[i].vertexID[oldJ]].x;
+ yTest2 = currentFloor -> vertex[currentFloor -> polygon[i].vertexID[oldJ]].y;
+ closestPointOnLine(closestX, closestY, xTest1, yTest1, xTest2, yTest2, setX, setY);
+// fprintf (dbug, "closest point is %i, %i... ", closestX, closestY);
+ xTest1 = setX - closestX;
+ yTest1 = setY - closestY;
+ thisDistance = xTest1 * xTest1 + yTest1 * yTest1;
+// fprintf (dbug, "Distance squared %i\n", thisDistance);
+
+ if (thisDistance < currentDistance) {
+// fprintf (dbug, "** We have a new winner! **\n");
+
+ currentDistance = thisDistance;
+ gotX = closestX;
+ gotY = closestY;
+ gotPoly = i;
+ }
+ oldJ = j;
+ }
+ }
+// fclose (dbug);
+
+ if (gotPoly == -1) return false;
+ setX = gotX;
+ setY = gotY;
+ setPoly = gotPoly;
+
+ return true;
+}
+
+bool doBorderStuff(onScreenPerson *moveMe) {
+ if (moveMe -> inPoly == moveMe -> walkToPoly) {
+ moveMe -> inPoly = -1;
+ moveMe -> thisStepX = moveMe -> walkToX;
+ moveMe -> thisStepY = moveMe -> walkToY;
+ } else {
+ // The section in which we need to be next...
+ int newPoly = currentFloor -> matrix[moveMe -> inPoly][moveMe -> walkToPoly];
+ if (newPoly == -1) return false;
+
+ // Grab the index of the second matching corner...
+ int ID, ID2;
+ if (! getMatchingCorners(currentFloor -> polygon[moveMe -> inPoly], currentFloor -> polygon[newPoly], ID, ID2))
+ return fatal("Not a valid floor plan!");
+
+ // Remember that we're walking to the new polygon...
+ moveMe -> inPoly = newPoly;
+
+ // Calculate the destination position on the coincidantal line...
+ int x1 = moveMe -> x, y1 = moveMe -> y;
+ int x2 = moveMe -> walkToX, y2 = moveMe -> walkToY;
+ int x3 = currentFloor -> vertex[ID].x, y3 = currentFloor -> vertex[ID].y;
+ int x4 = currentFloor -> vertex[ID2].x, y4 = currentFloor -> vertex[ID2].y;
+
+ int xAB = x1 - x2;
+ int yAB = y1 - y2;
+ int xCD = x4 - x3;
+ int yCD = y4 - y3;
+
+ double m = (yAB * (x3 - x1) - xAB * (y3 - y1));
+ m /= ((xAB * yCD) - (yAB * xCD));
+
+ if (m > 0 && m < 1) {
+ moveMe -> thisStepX = x3 + m * xCD;
+ moveMe -> thisStepY = y3 + m * yCD;
+ } else {
+ int dx13 = x1 - x3, dx14 = x1 - x4, dx23 = x2 - x3, dx24 = x2 - x4;
+ int dy13 = y1 - y3, dy14 = y1 - y4, dy23 = y2 - y3, dy24 = y2 - y4;
+
+ dx13 *= dx13;
+ dx14 *= dx14;
+ dx23 *= dx23;
+ dx24 *= dx24;
+ dy13 *= dy13;
+ dy14 *= dy14;
+ dy23 *= dy23;
+ dy24 *= dy24;
+
+ if (sqrt((double) dx13 + dy13) + sqrt((double) dx23 + dy23) <
+ sqrt((double) dx14 + dy14) + sqrt((double) dx24 + dy24)) {
+ moveMe -> thisStepX = x3;
+ moveMe -> thisStepY = y3;
+ } else {
+ moveMe -> thisStepX = x4;
+ moveMe -> thisStepY = y4;
+ }
+ }
+ }
+
+ float yDiff = moveMe -> thisStepY - moveMe -> y;
+ float xDiff = moveMe -> x - moveMe -> thisStepX;
+ if (xDiff || yDiff) {
+ moveMe -> wantAngle = 180 + ANGLEFIX * atan2(xDiff, yDiff * 2);
+ moveMe -> spinning = true;
+ }
+
+ setFrames(* moveMe, ANI_WALK);
+ return true;
+}
+
+bool walkMe(onScreenPerson *thisPerson, bool move = true) {
+ float xDiff, yDiff, maxDiff, s;
+
+ for (;;) {
+ xDiff = thisPerson -> thisStepX - thisPerson -> x;
+ yDiff = (thisPerson -> thisStepY - thisPerson -> y) * 2;
+ s = thisPerson -> scale * thisPerson -> walkSpeed;
+ if (s < 0.2) s = 0.2;
+
+ maxDiff = (TF_abs(xDiff) >= TF_abs(yDiff)) ? TF_abs(xDiff) : TF_abs(yDiff);
+
+ if (TF_abs(maxDiff) > s) {
+ if (thisPerson -> spinning) {
+ spinStep(thisPerson);
+ setFrames(* thisPerson, ANI_WALK);
+ }
+ s = maxDiff / s;
+ if (move)
+ moveAndScale(* thisPerson,
+ thisPerson -> x + xDiff / s,
+ thisPerson -> y + yDiff / (s * 2));
+ return true;
+ }
+
+ if (thisPerson -> inPoly == -1) {
+ if (thisPerson -> directionWhenDoneWalking != -1) {
+ thisPerson -> wantAngle = thisPerson -> directionWhenDoneWalking;
+ thisPerson -> spinning = true;
+ spinStep(thisPerson);
+ }
+ break;
+ }
+ if (! doBorderStuff(thisPerson)) break;
+ }
+
+ thisPerson -> walking = false;
+ setFrames(* thisPerson, ANI_STAND);
+ moveAndScale(* thisPerson,
+ thisPerson -> walkToX,
+ thisPerson -> walkToY);
+ return false;
+}
+
+bool makeWalkingPerson(int x, int y, int objNum, loadedFunction *func, int di) {
+ if (x == 0 && y == 0) return false;
+ if (currentFloor -> numPolygons == 0) return false;
+ onScreenPerson *moveMe = findPerson(objNum);
+ if (! moveMe) return false;
+
+ if (moveMe -> continueAfterWalking) abortFunction(moveMe -> continueAfterWalking);
+ moveMe -> continueAfterWalking = NULL;
+ moveMe -> walking = true;
+ moveMe -> directionWhenDoneWalking = di;
+
+ moveMe -> walkToX = x;
+ moveMe -> walkToY = y;
+ moveMe -> walkToPoly = inFloor(x, y);
+ if (moveMe -> walkToPoly == -1) {
+ if (! handleClosestPoint(moveMe -> walkToX, moveMe -> walkToY, moveMe -> walkToPoly)) return false;
+ }
+
+ moveMe -> inPoly = inFloor(moveMe -> x, moveMe -> y);
+ if (moveMe -> inPoly == -1) {
+ int xxx = moveMe -> x, yyy = moveMe -> y;
+ if (! handleClosestPoint(xxx, yyy, moveMe -> inPoly)) return false;
+ }
+
+ doBorderStuff(moveMe);
+ if (walkMe(moveMe, false) || moveMe -> spinning) {
+ moveMe -> continueAfterWalking = func;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool stopPerson(int o) {
+ onScreenPerson *moveMe = findPerson(o);
+ if (moveMe)
+ if (moveMe -> continueAfterWalking) {
+ abortFunction(moveMe -> continueAfterWalking);
+ moveMe -> continueAfterWalking = NULL;
+ moveMe -> walking = false;
+ moveMe -> spinning = false;
+ setFrames(* moveMe, ANI_STAND);
+ return true;
+ }
+ return false;
+}
+
+bool forceWalkingPerson(int x, int y, int objNum, loadedFunction *func, int di) {
+ if (x == 0 && y == 0) return false;
+ onScreenPerson *moveMe = findPerson(objNum);
+ if (! moveMe) return false;
+
+ if (moveMe -> continueAfterWalking) abortFunction(moveMe -> continueAfterWalking);
+ moveMe -> walking = true;
+ moveMe -> continueAfterWalking = NULL;
+ moveMe -> directionWhenDoneWalking = di;
+
+ moveMe -> walkToX = x;
+ moveMe -> walkToY = y;
+
+ // Let's pretend the start and end points are both in the same
+ // polygon (which one isn't important)
+ moveMe -> inPoly = 0;
+ moveMe -> walkToPoly = 0;
+
+ doBorderStuff(moveMe);
+ if (walkMe(moveMe) || moveMe -> spinning) {
+ moveMe -> continueAfterWalking = func;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void jumpPerson(int x, int y, int objNum) {
+ if (x == 0 && y == 0) return;
+ onScreenPerson *moveMe = findPerson(objNum);
+ if (! moveMe) return;
+ if (moveMe -> continueAfterWalking) abortFunction(moveMe -> continueAfterWalking);
+ moveMe -> continueAfterWalking = NULL;
+ moveMe -> walking = false;
+ moveMe -> spinning = false;
+ moveAndScale(* moveMe, x, y);
+}
+
+bool floatCharacter(int f, int objNum) {
+ onScreenPerson *moveMe = findPerson(objNum);
+ if (! moveMe) return false;
+ moveMe -> floaty = f;
+ return true;
+}
+
+bool setCharacterWalkSpeed(int f, int objNum) {
+ if (f <= 0) return false;
+ onScreenPerson *moveMe = findPerson(objNum);
+ if (! moveMe) return false;
+ moveMe -> walkSpeed = f;
+ return true;
+}
+
+void walkAllPeople() {
+ onScreenPerson *thisPerson = allPeople;
+
+ while (thisPerson) {
+ if (thisPerson -> walking) {
+ walkMe(thisPerson);
+ } else if (thisPerson -> spinning) {
+ spinStep(thisPerson);
+ setFrames(* thisPerson, ANI_STAND);
+ }
+ if ((! thisPerson -> walking) && (! thisPerson -> spinning) && thisPerson -> continueAfterWalking) {
+ restartFunction(thisPerson -> continueAfterWalking);
+ thisPerson -> continueAfterWalking = NULL;
+ }
+ thisPerson = thisPerson -> next;
+ }
+}
+
+bool addPerson(int x, int y, int objNum, persona *p) {
+ onScreenPerson *newPerson = new onScreenPerson;
+ if (! checkNew(newPerson)) return false;
+
+ // EASY STUFF
+ newPerson -> thisType = loadObjectType(objNum);
+ newPerson -> scale = 1;
+ newPerson -> extra = 0;
+ newPerson -> continueAfterWalking = NULL;
+ moveAndScale(* newPerson, x, y);
+ newPerson -> frameNum = 0;
+ newPerson -> walkToX = x;
+ newPerson -> walkToY = y;
+ newPerson -> walking = false;
+ newPerson -> spinning = false;
+ newPerson -> show = true;
+ newPerson -> direction = 0;
+ newPerson -> angle = 180;
+ newPerson -> wantAngle = 180;
+ newPerson -> angleOffset = 0;
+ newPerson -> floaty = 0;
+ newPerson -> walkSpeed = newPerson -> thisType -> walkSpeed;
+ newPerson -> myAnim = NULL;
+ newPerson -> spinSpeed = newPerson -> thisType -> spinSpeed;
+ newPerson -> r = 0;
+ newPerson -> g = 0;
+ newPerson -> b = 0;
+ newPerson -> colourmix = 0;
+ newPerson -> transparency = 0;
+ newPerson -> myPersona = p;
+
+ setFrames(* newPerson, ANI_STAND);
+
+ // HEIGHT (BASED ON 1st FRAME OF 1st ANIMATION... INC. SPECIAL CASES)
+ int fNumSigned = p -> animation[0] -> frames[0].frameNum;
+ int fNum = abs(fNumSigned);
+ if (fNum >= p -> animation[0] -> theSprites -> bank.total) {
+ if (fNumSigned < 0) {
+ newPerson -> height = 5;
+ } else {
+ newPerson -> height = p -> animation[0] -> theSprites -> bank.sprites[0].yhot + 5;
+ }
+ } else {
+ newPerson -> height = p -> animation[0] -> theSprites -> bank.sprites[fNum].yhot + 5;
+ }
+
+ // NOW ADD IT IN THE RIGHT PLACE
+ onScreenPerson * * changethat = & allPeople;
+
+ while (((* changethat) != NULL) && ((* changethat) -> y < y))
+ changethat = & ((* changethat) -> next);
+
+ newPerson -> next = (* changethat);
+ (* changethat) = newPerson;
+
+ return (bool)(newPerson -> thisType != NULL);
+}
+
+int timeForAnim(personaAnimation *fram) {
+ int total = 0;
+ for (int a = 0; a < fram -> numFrames; a ++) {
+ total += fram -> frames[a].howMany;
+ }
+ return total;
+}
+
+void animatePerson(int obj, personaAnimation *fram) { // Set a new SINGLE animation
+ onScreenPerson *moveMe = findPerson(obj);
+ if (moveMe) {
+ if (moveMe -> continueAfterWalking) abortFunction(moveMe -> continueAfterWalking);
+ moveMe -> continueAfterWalking = NULL;
+ moveMe -> walking = false;
+ moveMe -> spinning = false;
+ moveMe -> myAnim = fram;
+ }
+}
+
+void animatePerson(int obj, persona *per) { // Set a new costume
+ onScreenPerson *moveMe = findPerson(obj);
+ if (moveMe) {
+ // if (moveMe -> continueAfterWalking) abortFunction (moveMe -> continueAfterWalking);
+ // moveMe -> continueAfterWalking = NULL;
+ // moveMe -> walking = false;
+ moveMe -> spinning = false;
+ moveMe -> myPersona = per;
+ rethinkAngle(moveMe);
+ if (moveMe-> walking) {
+ setFrames(* moveMe, ANI_WALK);
+ } else {
+ setFrames(* moveMe, ANI_STAND);
+ }
+ }
+}
+
+void killAllPeople() {
+ onScreenPerson *killPeople;
+ while (allPeople) {
+ if (allPeople -> continueAfterWalking) abortFunction(allPeople -> continueAfterWalking);
+ allPeople -> continueAfterWalking = NULL;
+ killPeople = allPeople;
+ allPeople = allPeople -> next;
+ removeObjectType(killPeople -> thisType);
+ delete killPeople;
+ }
+}
+
+void killMostPeople() {
+ onScreenPerson *killPeople;
+ onScreenPerson * * lookyHere = & allPeople;
+
+ while (* lookyHere) {
+ if ((* lookyHere) -> extra & EXTRA_NOREMOVE) {
+ lookyHere = & (* lookyHere) -> next;
+ } else {
+ killPeople = (* lookyHere);
+
+ // Change last pointer to NEXT in the list instead
+ (* lookyHere) = killPeople -> next;
+
+ // Gone from the list... now free some memory
+ if (killPeople -> continueAfterWalking) abortFunction(killPeople -> continueAfterWalking);
+ killPeople -> continueAfterWalking = NULL;
+ removeObjectType(killPeople -> thisType);
+ delete killPeople;
+ }
+ }
+}
+
+void removeOneCharacter(int i) {
+ onScreenPerson *p = findPerson(i);
+
+ if (p) {
+ if (overRegion == &personRegion && overRegion->thisType == p->thisType) {
+ overRegion = NULL;
+ }
+
+ if (p -> continueAfterWalking) abortFunction(p -> continueAfterWalking);
+ p -> continueAfterWalking = NULL;
+ onScreenPerson * * killPeople;
+
+ for (killPeople = & allPeople;
+ * killPeople != p;
+ killPeople = & ((* killPeople) -> next)) {
+ ;
+ }
+
+ * killPeople = p -> next;
+ removeObjectType(p -> thisType);
+ delete p;
+ }
+}
+
+#if ALLOW_FILE
+bool saveAnim(personaAnimation *p, FILE *fp) {
+ put2bytes(p -> numFrames, fp);
+ if (p -> numFrames) {
+ put4bytes(p -> theSprites -> ID, fp);
+
+ for (int a = 0; a < p -> numFrames; a ++) {
+ put4bytes(p -> frames[a].frameNum, fp);
+ put4bytes(p -> frames[a].howMany, fp);
+ put4bytes(p -> frames[a].noise, fp);
+ }
+ }
+ return true;
+}
+
+bool loadAnim(personaAnimation *p, FILE *fp) {
+ p -> numFrames = get2bytes(fp);
+
+ if (p -> numFrames) {
+ int a = get4bytes(fp);
+ p -> frames = new animFrame[p -> numFrames];
+ if (! checkNew(p -> frames)) return false;
+ p -> theSprites = loadBankForAnim(a);
+
+ for (a = 0; a < p -> numFrames; a ++) {
+ p -> frames[a].frameNum = get4bytes(fp);
+ p -> frames[a].howMany = get4bytes(fp);
+ if (ssgVersion >= VERSION(2, 0)) {
+ p -> frames[a].noise = get4bytes(fp);
+ } else {
+ p -> frames[a].noise = 0;
+ }
+ }
+ } else {
+ p -> theSprites = NULL;
+ p -> frames = NULL;
+ }
+ return true;
+}
+/*
+void debugCostume (char * message, persona * cossy) {
+ FILE * db = fopen ("debuTURN.txt", "at");
+ fprintf (db, " %s costume with %i directions...\n", message, cossy -> numDirections);
+ for (int a = 0; a < cossy -> numDirections * 3; a ++) {
+ fprintf (db, " %i frames:", cossy -> animation[a] -> numFrames);
+ for (int b = 0; b < cossy -> animation[a] -> numFrames; b ++) {
+ fprintf (db, " %i", cossy -> animation[a] -> frames[b]);
+ }
+ fprintf (db, "\n");
+
+ }
+ fclose (db);
+}
+*/
+bool saveCostume(persona *cossy, FILE *fp) {
+ int a;
+ put2bytes(cossy -> numDirections, fp);
+ for (a = 0; a < cossy -> numDirections * 3; a ++) {
+ if (! saveAnim(cossy -> animation[a], fp)) return false;
+ }
+// debugCostume ("Saved", cossy);
+ return true;
+}
+
+bool loadCostume(persona *cossy, FILE *fp) {
+ int a;
+ cossy -> numDirections = get2bytes(fp);
+ cossy -> animation = new personaAnimation * [cossy -> numDirections * 3];
+ if (! checkNew(cossy -> animation)) return false;
+ for (a = 0; a < cossy -> numDirections * 3; a ++) {
+ cossy -> animation[a] = new personaAnimation;
+ if (! checkNew(cossy -> animation[a])) return false;
+
+ if (! loadAnim(cossy -> animation[a], fp)) return false;
+ }
+// debugCostume ("Loaded", cossy);
+ return true;
+}
+
+bool savePeople(FILE *fp) {
+ onScreenPerson *me = allPeople;
+ int countPeople = 0, a;
+
+ putSigned(scaleHorizon, fp);
+ putSigned(scaleDivide, fp);
+
+ while (me) {
+ countPeople ++;
+ me = me -> next;
+ }
+
+ put2bytes(countPeople, fp);
+
+ me = allPeople;
+ for (a = 0; a < countPeople; a ++) {
+
+ putFloat(me -> x, fp);
+ putFloat(me -> y, fp);
+
+ saveCostume(me -> myPersona, fp);
+ saveAnim(me -> myAnim, fp);
+ fputc(me -> myAnim == me -> lastUsedAnim, fp);
+
+ putFloat(me -> scale, fp);
+
+ put2bytes(me -> extra, fp);
+ put2bytes(me -> height, fp);
+ put2bytes(me -> walkToX, fp);
+ put2bytes(me -> walkToY, fp);
+ put2bytes(me -> thisStepX, fp);
+ put2bytes(me -> thisStepY, fp);
+ put2bytes(me -> frameNum, fp);
+ put2bytes(me -> frameTick, fp);
+ put2bytes(me -> walkSpeed, fp);
+ put2bytes(me -> spinSpeed, fp);
+ putSigned(me -> floaty, fp);
+ fputc(me -> show, fp);
+ fputc(me -> walking, fp);
+ fputc(me -> spinning, fp);
+ if (me -> continueAfterWalking) {
+ fputc(1, fp);
+ saveFunction(me -> continueAfterWalking, fp);
+ } else {
+ fputc(0, fp);
+ }
+ put2bytes(me -> direction, fp);
+ put2bytes(me -> angle, fp);
+ put2bytes(me -> angleOffset, fp);
+ put2bytes(me -> wantAngle, fp);
+ putSigned(me -> directionWhenDoneWalking, fp);
+ putSigned(me -> inPoly, fp);
+ putSigned(me -> walkToPoly, fp);
+
+ fputc(me -> r, fp);
+ fputc(me -> g, fp);
+ fputc(me -> b, fp);
+ fputc(me -> colourmix, fp);
+ fputc(me -> transparency, fp);
+
+ saveObjectRef(me -> thisType, fp);
+
+ me = me -> next;
+ }
+ return true;
+}
+
+bool loadPeople(FILE *fp) {
+ onScreenPerson * * pointy = & allPeople;
+ onScreenPerson *me;
+
+ scaleHorizon = getSigned(fp);
+ scaleDivide = getSigned(fp);
+
+ int countPeople = get2bytes(fp);
+ int a;
+
+ allPeople = NULL;
+ for (a = 0; a < countPeople; a ++) {
+ me = new onScreenPerson;
+ if (! checkNew(me)) return false;
+
+ me -> myPersona = new persona;
+ if (! checkNew(me -> myPersona)) return false;
+
+ me -> myAnim = new personaAnimation;
+ if (! checkNew(me -> myAnim)) return false;
+
+ me -> x = getFloat(fp);
+ me -> y = getFloat(fp);
+
+ loadCostume(me -> myPersona, fp);
+ loadAnim(me -> myAnim, fp);
+
+ me -> lastUsedAnim = fgetc(fp) ? me -> myAnim : NULL;
+
+ me -> scale = getFloat(fp);
+
+ me -> extra = get2bytes(fp);
+ me -> height = get2bytes(fp);
+ me -> walkToX = get2bytes(fp);
+ me -> walkToY = get2bytes(fp);
+ me -> thisStepX = get2bytes(fp);
+ me -> thisStepY = get2bytes(fp);
+ me -> frameNum = get2bytes(fp);
+ me -> frameTick = get2bytes(fp);
+ me -> walkSpeed = get2bytes(fp);
+ me -> spinSpeed = get2bytes(fp);
+ me -> floaty = getSigned(fp);
+ me -> show = fgetc(fp);
+ me -> walking = fgetc(fp);
+ me -> spinning = fgetc(fp);
+ if (fgetc(fp)) {
+ me -> continueAfterWalking = loadFunction(fp);
+ if (! me -> continueAfterWalking) return false;
+ } else {
+ me -> continueAfterWalking = NULL;
+ }
+ me -> direction = get2bytes(fp);
+ me -> angle = get2bytes(fp);
+ if (ssgVersion >= VERSION(2, 0)) {
+ me -> angleOffset = get2bytes(fp);
+ } else {
+ me -> angleOffset = 0;
+ }
+ me -> wantAngle = get2bytes(fp);
+ me -> directionWhenDoneWalking = getSigned(fp);
+ me -> inPoly = getSigned(fp);
+ me -> walkToPoly = getSigned(fp);
+ if (ssgVersion >= VERSION(2, 0)) {
+ me -> r = fgetc(fp);
+ me -> g = fgetc(fp);
+ me -> b = fgetc(fp);
+ me -> colourmix = fgetc(fp);
+ me -> transparency = fgetc(fp);
+ } else {
+ setMyDrawMode(me, get2bytes(fp));
+ }
+ me -> thisType = loadObjectRef(fp);
+
+ // Anti-aliasing settings
+ if (ssgVersion >= VERSION(1, 6)) {
+ if (ssgVersion < VERSION(2, 0)) {
+ // aaLoad
+ fgetc(fp);
+ getFloat(fp);
+ getFloat(fp);
+ }
+ }
+
+ me -> next = NULL;
+ * pointy = me;
+ pointy = & (me -> next);
+ }
+// db ("End of loadPeople");
+ return true;
+}
+#endif
diff --git a/engines/sludge/people.h b/engines/sludge/people.h
new file mode 100644
index 0000000000..fbc8fd7902
--- /dev/null
+++ b/engines/sludge/people.h
@@ -0,0 +1,134 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_PEOPLE_H
+#define SLUDGE_PEOPLE_H
+
+struct animFrame {
+ int frameNum, howMany;
+ int noise;
+};
+
+#define EXTRA_FRONT 1
+#define EXTRA_FIXEDSIZE 2
+#define EXTRA_NOSCALE 2 // Alternative name
+#define EXTRA_NOZB 4
+#define EXTRA_FIXTOSCREEN 8
+#define EXTRA_NOLITE 16
+#define EXTRA_NOREMOVE 32
+#define EXTRA_RECTANGULAR 64
+
+struct personaAnimation {
+ struct loadedSpriteBank *theSprites;
+ animFrame *frames;
+ int numFrames;
+};
+
+struct persona {
+ personaAnimation * *animation;
+ int numDirections;
+};
+
+struct onScreenPerson {
+ float x, y;
+ int height, floaty, walkSpeed;
+ float scale;
+ onScreenPerson *next;
+ int walkToX, walkToY, thisStepX, thisStepY, inPoly, walkToPoly;
+ bool walking, spinning;
+ struct loadedFunction *continueAfterWalking;
+ personaAnimation *myAnim;
+ personaAnimation *lastUsedAnim;
+ persona *myPersona;
+ int frameNum, frameTick, angle, wantAngle, angleOffset;
+ bool show;
+ int direction, directionWhenDoneWalking;
+ struct objectType *thisType;
+ int extra, spinSpeed;
+ unsigned char r, g, b, colourmix, transparency;
+};
+
+// Initialisation and creation
+
+bool initPeople();
+bool addPerson(int x, int y, int objNum, persona *p);
+
+// Draw to screen and to backdrop
+
+void drawPeople();
+void freezePeople(int, int);
+
+// Removalisationisms
+
+void killAllPeople();
+void killMostPeople();
+void removeOneCharacter(int i);
+
+// Things which affect or use all characters
+
+onScreenPerson *findPerson(int v);
+void setScale(short int h, short int d);
+
+// Things which affect one character
+
+void makeTalker(onScreenPerson &me);
+void makeSilent(onScreenPerson &me);
+void setShown(bool h, int ob);
+void setDrawMode(int h, int ob);
+void setPersonTransparency(int ob, unsigned char x);
+void setPersonColourise(int ob, unsigned char r, unsigned char g, unsigned char b, unsigned char colourmix);
+
+// Moving 'em
+
+void movePerson(int x, int y, int objNum);
+bool makeWalkingPerson(int x, int y, int objNum, struct loadedFunction *func, int di);
+bool forceWalkingPerson(int x, int y, int objNum, struct loadedFunction *func, int di);
+void jumpPerson(int x, int y, int objNum);
+void walkAllPeople();
+bool turnPersonToFace(int thisNum, int direc);
+bool stopPerson(int o);
+bool floatCharacter(int f, int objNum);
+bool setCharacterWalkSpeed(int f, int objNum);
+
+// Animating 'em
+
+void animatePerson(int obj, personaAnimation *);
+void animatePerson(int obj, persona *per);
+personaAnimation *createPersonaAnim(int num, struct variableStack *&stacky);
+inline void setBankFile(personaAnimation *newP, loadedSpriteBank *sB) {
+ newP -> theSprites = sB;
+}
+bool setPersonExtra(int f, int newSetting);
+int timeForAnim(personaAnimation *fram);
+personaAnimation *copyAnim(personaAnimation *orig);
+personaAnimation *makeNullAnim();
+void deleteAnim(personaAnimation *orig);
+
+// Loading and saving
+#if ALLOW_FILE
+bool saveAnim(personaAnimation *p, FILE *fp);
+bool loadAnim(personaAnimation *p, FILE *fp);
+bool savePeople(FILE *fp);
+bool loadPeople(FILE *fp);
+bool saveCostume(persona *cossy, FILE *fp);
+bool loadCostume(persona *cossy, FILE *fp);
+#endif
+#endif
diff --git a/engines/sludge/platform-dependent.h b/engines/sludge/platform-dependent.h
new file mode 100644
index 0000000000..973e20f4b5
--- /dev/null
+++ b/engines/sludge/platform-dependent.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_PLATEFORM_DEPENDENT_H
+#define SLUDGE_PLATEFORM_DEPENDENT_H
+/* These are the functions which have different versions for
+ * the different operating systems.
+ */
+#include <stdint.h>
+
+char *grabFileName();
+int showSetupWindow();
+
+void msgBox(const char *head, const char *msg);
+int msgBoxQuestion(const char *head, const char *msg);
+
+void changeToUserDir();
+uint32_t launch(char *filename);
+
+bool defaultUserFullScreen();
+
+#endif
diff --git a/engines/sludge/region.cpp b/engines/sludge/region.cpp
new file mode 100644
index 0000000000..3cd7164d51
--- /dev/null
+++ b/engines/sludge/region.cpp
@@ -0,0 +1,164 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+#include "objtypes.h"
+#include "region.h"
+#include "newfatal.h"
+#include "sludger.h"
+#include "moreio.h"
+#include "backdrop.h"
+
+screenRegion *allScreenRegions = NULL;
+screenRegion *overRegion = NULL;
+extern inputType input;
+extern int cameraX, cameraY;
+
+void showBoxes() {
+ screenRegion *huntRegion = allScreenRegions;
+
+ while (huntRegion) {
+ drawVerticalLine(huntRegion -> x1, huntRegion -> y1, huntRegion -> y2);
+ drawVerticalLine(huntRegion -> x2, huntRegion -> y1, huntRegion -> y2);
+ drawHorizontalLine(huntRegion -> x1, huntRegion -> y1, huntRegion -> x2);
+ drawHorizontalLine(huntRegion -> x1, huntRegion -> y2, huntRegion -> x2);
+ huntRegion = huntRegion -> next;
+ }
+}
+
+void removeScreenRegion(int objectNum) {
+ screenRegion * * huntRegion = & allScreenRegions;
+ screenRegion *killMe;
+
+ while (* huntRegion) {
+ if ((* huntRegion) -> thisType -> objectNum == objectNum) {
+ killMe = * huntRegion;
+ * huntRegion = killMe -> next;
+ removeObjectType(killMe -> thisType);
+ if (killMe == overRegion) overRegion = NULL;
+ delete killMe;
+ killMe = NULL;
+ } else {
+ huntRegion = & ((* huntRegion) -> next);
+ }
+ }
+}
+
+#if ALLOW_FILE
+void saveRegions(FILE *fp) {
+ int numRegions = 0;
+ screenRegion *thisRegion = allScreenRegions;
+ while (thisRegion) {
+ thisRegion = thisRegion -> next;
+ numRegions ++;
+ }
+ put2bytes(numRegions, fp);
+ thisRegion = allScreenRegions;
+ while (thisRegion) {
+ put2bytes(thisRegion -> x1, fp);
+ put2bytes(thisRegion -> y1, fp);
+ put2bytes(thisRegion -> x2, fp);
+ put2bytes(thisRegion -> y2, fp);
+ put2bytes(thisRegion -> sX, fp);
+ put2bytes(thisRegion -> sY, fp);
+ put2bytes(thisRegion -> di, fp);
+ saveObjectRef(thisRegion -> thisType, fp);
+
+ thisRegion = thisRegion -> next;
+ }
+}
+
+void loadRegions(FILE *fp) {
+ int numRegions = get2bytes(fp);
+
+ screenRegion *newRegion;
+ screenRegion * * pointy = & allScreenRegions;
+
+ while (numRegions --) {
+ newRegion = new screenRegion;
+ * pointy = newRegion;
+ pointy = & (newRegion -> next);
+
+ newRegion -> x1 = get2bytes(fp);
+ newRegion -> y1 = get2bytes(fp);
+ newRegion -> x2 = get2bytes(fp);
+ newRegion -> y2 = get2bytes(fp);
+ newRegion -> sX = get2bytes(fp);
+ newRegion -> sY = get2bytes(fp);
+ newRegion -> di = get2bytes(fp);
+ newRegion -> thisType = loadObjectRef(fp);
+ }
+ * pointy = NULL;
+}
+#endif
+void killAllRegions() {
+ screenRegion *killRegion;
+ while (allScreenRegions) {
+ killRegion = allScreenRegions;
+ allScreenRegions = allScreenRegions -> next;
+ removeObjectType(killRegion -> thisType);
+ delete killRegion;
+ }
+ overRegion = NULL;
+}
+
+bool addScreenRegion(int x1, int y1, int x2, int y2, int sX, int sY, int di, int objectNum) {
+ screenRegion *newRegion = new screenRegion;
+ if (! checkNew(newRegion)) return false;
+ newRegion -> di = di;
+ newRegion -> x1 = x1;
+ newRegion -> y1 = y1;
+ newRegion -> x2 = x2;
+ newRegion -> y2 = y2;
+ newRegion -> sX = sX;
+ newRegion -> sY = sY;
+ newRegion -> thisType = loadObjectType(objectNum);
+ newRegion -> next = allScreenRegions;
+ allScreenRegions = newRegion;
+ return (bool)(newRegion -> thisType != NULL);
+}
+
+void getOverRegion() {
+ screenRegion *thisRegion = allScreenRegions;
+ while (thisRegion) {
+ if ((input.mouseX >= thisRegion -> x1 - cameraX) && (input.mouseY >= thisRegion -> y1 - cameraY) &&
+ (input.mouseX <= thisRegion -> x2 - cameraX) && (input.mouseY <= thisRegion -> y2 - cameraY)) {
+ overRegion = thisRegion;
+ return;
+ }
+ thisRegion = thisRegion -> next;
+ }
+ overRegion = NULL;
+ return;
+}
+
+screenRegion *getRegionForObject(int obj) {
+ screenRegion *thisRegion = allScreenRegions;
+
+ while (thisRegion) {
+ if (obj == thisRegion -> thisType -> objectNum) {
+ return thisRegion;
+ }
+ thisRegion = thisRegion -> next;
+ }
+
+ return NULL;
+}
diff --git a/engines/sludge/region.h b/engines/sludge/region.h
new file mode 100644
index 0000000000..4c4843f2b7
--- /dev/null
+++ b/engines/sludge/region.h
@@ -0,0 +1,42 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_REGION_H
+#define SLUDGE_REGION_H
+
+struct screenRegion {
+ int x1, y1, x2, y2, sX, sY, di;
+ objectType *thisType;
+ screenRegion *next;
+};
+
+bool addScreenRegion(int x1, int y1, int x2, int y2, int, int, int, int objectNum);
+void getOverRegion();
+screenRegion *getRegionForObject(int obj);
+void removeScreenRegion(int objectNum);
+void killAllRegions();
+#if ALLOW_FILE
+void loadRegions(FILE *);
+void saveRegions(FILE *);
+#endif
+void showBoxes();
+
+#endif
diff --git a/engines/sludge/savedata.cpp b/engines/sludge/savedata.cpp
new file mode 100644
index 0000000000..e8075ccf96
--- /dev/null
+++ b/engines/sludge/savedata.cpp
@@ -0,0 +1,268 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "allfiles.h"
+#include "debug.h"
+#include "variable.h"
+#include "newfatal.h"
+#include "moreio.h"
+
+#define LOAD_ERROR "Can't load custom data...\n\n"
+
+unsigned short saveEncoding = false;
+char encode1 = 0;
+char encode2 = 0;
+
+extern char *gamePath;
+
+/*
+void loadSaveDebug (char * com) {
+ FILE * ffpp = fopen ("debuggy.txt", "at");
+ fprintf (ffpp, "%s\n", com);
+ fclose (ffpp);
+}
+
+void loadSaveDebug (char com) {
+ FILE * ffpp = fopen ("debuggy.txt", "at");
+ fprintf (ffpp, "%c\n", com);
+ fclose (ffpp);
+}
+
+void loadSaveDebug (int com) {
+ FILE * ffpp = fopen ("debuggy.txt", "at");
+ fprintf (ffpp, "%d\n", com);
+ fclose (ffpp);
+}
+*/
+
+#if ALLOW_FILE
+void writeStringEncoded(const char *s, FILE *fp) {
+ int a, len = strlen(s);
+
+ put2bytes(len, fp);
+ for (a = 0; a < len; a ++) {
+ fputc(s[a] ^ encode1, fp);
+ encode1 += encode2;
+ }
+}
+
+char *readStringEncoded(FILE *fp) {
+ int a, len = get2bytes(fp);
+ char *s = new char[len + 1];
+ if (! checkNew(s)) return NULL;
+ for (a = 0; a < len; a ++) {
+ s[a] = (char)(fgetc(fp) ^ encode1);
+ encode1 += encode2;
+ }
+ s[len] = 0;
+ return s;
+}
+
+char *readTextPlain(FILE *fp) {
+ int32_t startPos;
+
+ int stringSize = 0;
+ bool keepGoing = true;
+ char gotChar;
+ char *reply;
+
+ startPos = ftell(fp);
+
+ while (keepGoing) {
+ gotChar = (char) fgetc(fp);
+ if ((gotChar == '\n') || (feof(fp))) {
+ keepGoing = false;
+ } else {
+ stringSize ++;
+ }
+ }
+
+ if ((stringSize == 0) && (feof(fp))) {
+ return NULL;
+ } else {
+ fseek(fp, startPos, SEEK_SET);
+ reply = new char[stringSize + 1];
+ if (reply == NULL) return NULL;
+ size_t bytes_read = fread(reply, stringSize, 1, fp);
+ if (bytes_read != stringSize && ferror(fp)) {
+ debugOut("Reading error in readTextPlain.\n");
+ }
+ fgetc(fp); // Skip the newline character
+ reply[stringSize] = 0;
+ }
+
+ return reply;
+}
+#endif
+
+bool fileToStack(char *filename, stackHandler *sH) {
+#if ALLOW_FILE
+ variable stringVar;
+ stringVar.varType = SVT_NULL;
+ const char *checker = saveEncoding ? "[Custom data (encoded)]\r\n" : "[Custom data (ASCII)]\n";
+
+ FILE *fp = fopen(filename, "rb");
+ if (! fp) {
+ char currentDir[1000];
+ if (! getcwd(currentDir, 998)) {
+ debugOut("Can't get current directory.\n");
+ }
+
+ if (chdir(gamePath)) {
+ debugOut("Error: Failed changing to directory %s\n", gamePath);
+ }
+ fp = fopen(filename, "rb");
+ if (chdir(currentDir)) {
+ debugOut("Error: Failed changing to directory %s\n", currentDir);
+ }
+
+ if (! fp) {
+ return fatal("No such file", filename);
+ }
+ }
+
+ encode1 = (unsigned char) saveEncoding & 255;
+ encode2 = (unsigned char)(saveEncoding >> 8);
+
+ while (* checker) {
+ if (fgetc(fp) != * checker) {
+ fclose(fp);
+ return fatal(LOAD_ERROR "This isn't a SLUDGE custom data file:", filename);
+ }
+ checker ++;
+ }
+
+ if (saveEncoding) {
+ char *checker = readStringEncoded(fp);
+ if (strcmp(checker, "UN�LO�CKED")) {
+ fclose(fp);
+ return fatal(LOAD_ERROR "The current file encoding setting does not match the encoding setting used when this file was created:", filename);
+ }
+ delete checker;
+ checker = NULL;
+ }
+
+
+ for (;;) {
+ if (saveEncoding) {
+ char i = fgetc(fp) ^ encode1;
+
+ if (feof(fp)) break;
+ switch (i) {
+ case 0: {
+ char *g = readStringEncoded(fp);
+ makeTextVar(stringVar, g);
+ delete g;
+ }
+ break;
+
+ case 1:
+ setVariable(stringVar, SVT_INT, get4bytes(fp));
+ break;
+
+ case 2:
+ setVariable(stringVar, SVT_INT, fgetc(fp));
+ break;
+
+ default:
+ fatal(LOAD_ERROR "Corrupt custom data file:", filename);
+ fclose(fp);
+ return false;
+ }
+ } else {
+ char *line = readTextPlain(fp);
+ if (! line) break;
+ makeTextVar(stringVar, line);
+ }
+
+ if (sH -> first == NULL) {
+ // Adds to the TOP of the array... oops!
+ if (! addVarToStackQuick(stringVar, sH -> first)) return false;
+ sH -> last = sH -> first;
+ } else {
+ // Adds to the END of the array... much better
+ if (! addVarToStackQuick(stringVar, sH -> last -> next)) return false;
+ sH -> last = sH -> last -> next;
+ }
+ }
+ fclose(fp);
+#endif
+ return true;
+}
+
+bool stackToFile(char *filename, const variable &from) {
+#if ALLOW_FILE
+ FILE *fp = fopen(filename, saveEncoding ? "wb" : "wt");
+ if (! fp) return fatal("Can't create file", filename);
+
+ variableStack *hereWeAre = from.varData.theStack -> first;
+
+ encode1 = (unsigned char) saveEncoding & 255;
+ encode2 = (unsigned char)(saveEncoding >> 8);
+
+ if (saveEncoding) {
+ fprintf(fp, "[Custom data (encoded)]\r\n");
+ writeStringEncoded("UN�LO�CKED", fp);
+ } else {
+ fprintf(fp, "[Custom data (ASCII)]\n");
+ }
+
+ while (hereWeAre) {
+ if (saveEncoding) {
+ switch (hereWeAre -> thisVar.varType) {
+ case SVT_STRING:
+ fputc(encode1, fp);
+ writeStringEncoded(hereWeAre -> thisVar.varData.theString, fp);
+ break;
+
+ case SVT_INT:
+ // Small enough to be stored as a char
+ if (hereWeAre -> thisVar.varData.intValue >= 0 && hereWeAre -> thisVar.varData.intValue < 256) {
+ fputc(2 ^ encode1, fp);
+ fputc(hereWeAre -> thisVar.varData.intValue, fp);
+ } else {
+ fputc(1 ^ encode1, fp);
+ put4bytes(hereWeAre -> thisVar.varData.intValue, fp);
+ }
+ break;
+
+ default:
+ fatal("Can't create an encoded custom data file containing anything other than numbers and strings", filename);
+ fclose(fp);
+ return false;
+ }
+ } else {
+ char *makeSureItsText = getTextFromAnyVar(hereWeAre -> thisVar);
+ if (makeSureItsText == NULL) break;
+ fprintf(fp, "%s\n", makeSureItsText);
+ delete makeSureItsText;
+ }
+
+ hereWeAre = hereWeAre -> next;
+ }
+ fclose(fp);
+#endif
+ return true;
+}
diff --git a/engines/sludge/savedata.h b/engines/sludge/savedata.h
new file mode 100644
index 0000000000..19821cc0ee
--- /dev/null
+++ b/engines/sludge/savedata.h
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_SAVEDATA_H
+#define SLUDGE_SAVEDATA_H
+
+bool fileToStack(char *filename, stackHandler *sH);
+bool stackToFile(char *filename, const variable &from);
+
+#endif
diff --git a/engines/sludge/scale.frag b/engines/sludge/scale.frag
new file mode 100644
index 0000000000..13558714ee
--- /dev/null
+++ b/engines/sludge/scale.frag
@@ -0,0 +1,60 @@
+/*
+ Inspired by a shader by guest(r) - guest.r@gmail.com - that was found at
+ http://www.razyboard.com/system/morethread-smart-texture-mag-filter-for-ogl2-and-dosbox-pete_bernert-266904-5689051-0.html
+*/
+
+uniform sampler2D Texture;
+uniform sampler2D lightTexture;
+uniform bool antialias;
+uniform bool useLightTexture;
+uniform float scale;
+
+varying vec2 varCoord0;
+varying vec2 varCoord1;
+
+varying vec4 color;
+varying vec4 secondaryColor;
+
+void main()
+{
+ vec4 c11 = texture2D(Texture, varCoord0.xy);
+
+ if (antialias) {
+ vec2 fw = fwidth(varCoord0.xy)*scale;
+
+ vec2 sd1 = vec2( fw.x,fw.y);
+ vec2 sd2 = vec2(-fw.x,fw.y);
+
+ vec4 s00 = texture2D(Texture, varCoord0.xy-sd1);
+ vec4 s20 = texture2D(Texture, varCoord0.xy-sd2);
+ vec4 s22 = texture2D(Texture, varCoord0.xy+sd1);
+ vec4 s02 = texture2D(Texture, varCoord0.xy+sd2);
+
+ vec4 dt = vec4(1.0,1.0,1.0,1.0);
+
+ float m1=dot(abs(s00-s22),dt)+0.0001;
+ float m2=dot(abs(s02-s20),dt)+0.0001;
+
+ vec4 temp1 = m2*(s00 + s22) + m1*(s02 + s20);
+
+ // gl_FragColor = (temp1/(m1+m2)) * 0.5;
+ c11 = c11*0.333333 + (temp1/(m1+m2)) * 0.333333;
+ }
+ /*
+ else {
+ gl_FragColor = c11;
+ }*/
+
+ //if (gl_FragColor.a<0.001) discard;
+
+ vec3 col;
+ if (useLightTexture) {
+ vec4 texture1 = texture2D (lightTexture, varCoord1.xy);
+ col = texture1.rgb * c11.rgb;
+ } else {
+ col = color.rgb * c11.rgb;
+ }
+ col += vec3(secondaryColor);
+ gl_FragColor = vec4 (col, color.a * c11.a);
+}
+
diff --git a/engines/sludge/scale.vert b/engines/sludge/scale.vert
new file mode 100644
index 0000000000..ff6aa05660
--- /dev/null
+++ b/engines/sludge/scale.vert
@@ -0,0 +1,25 @@
+attribute vec4 myVertex;
+attribute vec2 myUV0;
+attribute vec2 myUV1;
+
+uniform mat4 myPMVMatrix;
+uniform vec4 myColor;
+uniform vec4 mySecondaryColor;
+
+varying vec2 varCoord0;
+varying vec2 varCoord1;
+
+varying vec4 color;
+varying vec4 secondaryColor;
+
+void main()
+{
+ gl_Position = myPMVMatrix * myVertex;
+ varCoord0 = myUV0.st;
+
+ // Light
+ varCoord1 = myUV1.st;
+
+ color = myColor;
+ secondaryColor = mySecondaryColor;
+}
diff --git a/engines/sludge/scale_noaa.frag b/engines/sludge/scale_noaa.frag
new file mode 100644
index 0000000000..bf7aad7adf
--- /dev/null
+++ b/engines/sludge/scale_noaa.frag
@@ -0,0 +1,31 @@
+uniform sampler2D Texture;
+uniform sampler2D lightTexture;
+uniform bool antialias;
+uniform bool useLightTexture;
+uniform float scale;
+
+varying vec2 varCoord0;
+varying vec2 varCoord1;
+
+varying vec4 color;
+varying vec4 secondaryColor;
+
+void main()
+{
+ vec4 c11 = texture2D(Texture, varCoord0.xy);
+
+ //gl_FragColor = c11;
+
+ //if (gl_FragColor.a<0.001) discard;
+
+ vec3 col;
+ if (useLightTexture) {
+ vec4 texture1 = texture2D (lightTexture, varCoord1.xy);
+ col = texture1.rgb * c11.rgb;
+ } else {
+ col = color.rgb * c11.rgb;
+ }
+ col += vec3(secondaryColor);
+ gl_FragColor = vec4 (col, color.a * c11.a);
+}
+
diff --git a/engines/sludge/shaders.cpp b/engines/sludge/shaders.cpp
new file mode 100644
index 0000000000..fd4ca30aa2
--- /dev/null
+++ b/engines/sludge/shaders.cpp
@@ -0,0 +1,178 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "debug.h"
+#include "stringy.h"
+#include "shaders.h"
+#include "graphics.h"
+
+extern char *bundleFolder;
+
+//Function from: http://www.evl.uic.edu/aej/594/code/ogl.cpp
+//Read in a textfile (GLSL program)
+// we need to pass it as a string to the GLSL driver
+char *shaderFileRead(const char *name) {
+ FILE *fp;
+ char *content = NULL;
+ char *fn = joinStrings(bundleFolder, name);
+
+ int count = 0;
+
+ if (fn != NULL) {
+
+ fp = fopen(fn, "rt");
+
+ if (fp != NULL) {
+
+ fseek(fp, 0, SEEK_END);
+ count = ftell(fp);
+ rewind(fp);
+
+ if (count > 0) {
+ content = (char *)malloc(sizeof(char) * (count + 1));
+ count = fread(content, sizeof(char), count, fp);
+ content[count] = '\0';
+ }
+ fclose(fp);
+
+ }
+ }
+
+ delete fn;
+
+ return content;
+}
+
+static void
+printShaderInfoLog(GLuint shader) {
+ GLint infologLength = 0;
+ GLint charsWritten = 0;
+ char *infoLog;
+
+#if 0
+ printOpenGLError(); // Check for OpenGL errors
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infologLength);
+ printOpenGLError(); // Check for OpenGL errors
+#endif
+
+ if (infologLength > 0) {
+ infoLog = new char [infologLength];
+ if (infoLog == NULL) {
+ debugOut("ERROR: Could not allocate InfoLog buffer");
+ return;
+ }
+#if 0
+ glGetShaderInfoLog(shader, infologLength, &charsWritten, infoLog);
+ debugOut("Shader InfoLog:\n%s\n\n", infoLog);
+#endif
+ delete[] infoLog;
+ }
+ printOpenGLError(); // Check for OpenGL errors
+}
+
+/* Print out the information log for a program object */
+static void
+printProgramInfoLog(GLuint program) {
+ GLint infologLength = 0;
+ GLint charsWritten = 0;
+ char *infoLog;
+#if 0
+ printOpenGLError(); // Check for OpenGL errors
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infologLength);
+ printOpenGLError(); // Check for OpenGL errors
+#endif
+ if (infologLength > 0) {
+ infoLog = new char [infologLength];
+ if (infoLog == NULL) {
+ debugOut("ERROR: Could not allocate InfoLog buffer");
+ return;
+ }
+#if 0
+ glGetProgramInfoLog(program, infologLength, &charsWritten, infoLog);
+ debugOut("Program InfoLog:\n%s\n\n", infoLog);
+#endif
+ delete[] infoLog;
+ }
+ printOpenGLError(); // Check for OpenGL errors
+}
+
+int buildShaders(const char *vertexShader, const char *fragmentShader) {
+ GLuint VS, FS, prog;
+ GLint vertCompiled, fragCompiled;
+ GLint linked;
+#if 0
+ // Create Shader Objects
+ VS = glCreateShader(GL_VERTEX_SHADER);
+ FS = glCreateShader(GL_FRAGMENT_SHADER);
+
+ // Load source code strings into shaders
+ glShaderSource(VS, 1, &vertexShader, NULL);
+ glShaderSource(FS, 1, &fragmentShader, NULL);
+#endif
+ debugOut("Compiling vertex shader... \n");
+#if 0
+ // Compile vertex shader and print log
+ glCompileShader(VS);
+ printOpenGLError();
+ glGetShaderiv(VS, GL_COMPILE_STATUS, &vertCompiled);
+ printShaderInfoLog(VS);
+#endif
+ debugOut("\nCompiling fragment shader... \n");
+#if 0
+ // Compile fragment shader and print log
+ glCompileShader(FS);
+ printOpenGLError();
+ glGetShaderiv(FS, GL_COMPILE_STATUS, &fragCompiled);
+ printShaderInfoLog(FS);
+#endif
+ if (!vertCompiled || !fragCompiled)
+ return 0;
+
+ debugOut("\nShaders compiled. \n");
+
+#if 0
+ // Create a program object and attach the two compiled shaders
+ prog = glCreateProgram();
+ glAttachShader(prog, VS);
+ glAttachShader(prog, FS);
+
+ // Clean up
+ glDeleteShader(VS);
+ glDeleteShader(FS);
+
+ // Link the program and print log
+ glLinkProgram(prog);
+ printOpenGLError();
+ glGetProgramiv(prog, GL_LINK_STATUS, &linked);
+ printProgramInfoLog(prog);
+#endif
+ if (!linked)
+ return 0;
+
+ debugOut("Shader program linked. \n");
+
+ return prog;
+}
+
+
diff --git a/engines/sludge/shaders.h b/engines/sludge/shaders.h
new file mode 100644
index 0000000000..f62808b8d7
--- /dev/null
+++ b/engines/sludge/shaders.h
@@ -0,0 +1,37 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_SHADERS_H
+#define SLUDGE_SHADERS_H
+
+#if 0
+#if !defined(HAVE_GLES2)
+#include "GLee.h"
+#else
+#include <GLES2/gl2.h>
+#endif
+#endif
+
+char *shaderFileRead(const char *fn);
+int buildShaders(const char *vertexShader, const char *fragmentShader);
+
+#endif
+
diff --git a/engines/sludge/sludge.cpp b/engines/sludge/sludge.cpp
index 9a910b20dc..12d0bbf288 100644
--- a/engines/sludge/sludge.cpp
+++ b/engines/sludge/sludge.cpp
@@ -29,7 +29,8 @@
#include "engines/util.h"
#include "sludge/sludge.h"
-
+#include "main_loop.h"
+
namespace Sludge {
SludgeEngine::SludgeEngine(OSystem *syst, const SludgeGameDescription *gameDesc)
@@ -65,7 +66,7 @@ Common::Error SludgeEngine::run() {
_console = new SludgeConsole(this);
// debug log
- debug("SludgeEngine::init");
+ main_loop("Welcome.slg");
return Common::kNoError;
}
diff --git a/engines/sludge/sludger.cpp b/engines/sludge/sludger.cpp
new file mode 100644
index 0000000000..d73c12867d
--- /dev/null
+++ b/engines/sludge/sludger.cpp
@@ -0,0 +1,1563 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#if 0
+#if defined __unix__ && !(defined __APPLE__)
+#include <png.h>
+#else
+#include <libpng/png.h>
+#endif
+
+#include <SDL/SDL.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <iconv.h>
+#endif
+
+#include "allfiles.h"
+#include "platform-dependent.h"
+#include "CommonCode/version.h"
+#include "sludger.h"
+#include "backdrop.h"
+#include "cursors.h"
+#include "colours.h"
+#include "objtypes.h"
+#include "region.h"
+#include "sprites.h"
+#include "sprbanks.h"
+#include "people.h"
+#include "talk.h"
+#include "newfatal.h"
+#include "stringy.h"
+#include "moreio.h"
+#include "statusba.h"
+#include "builtin.h"
+#include "fonttext.h"
+#include "freeze.h"
+#include "floor.h"
+#include "zbuffer.h"
+#include "sound.h"
+#include "loadsave.h"
+#include "fileset.h"
+#include "transition.h"
+#include "language.h"
+#include "CommonCode/specialsettings.h"
+#include "graphics.h"
+
+#include "debug.h"
+
+
+extern personaAnimation *mouseCursorAnim;
+extern spritePalette pastePalette;
+extern int dialogValue;
+extern unsigned int sceneWidth, sceneHeight;
+extern char *launchMe;
+extern variable *launchResult;
+
+extern bool reallyWantToQuit;
+
+int numBIFNames = 0;
+char * *allBIFNames = NULL;
+int numUserFunc = 0;
+char * *allUserFunc = NULL;
+int numResourceNames = 0;
+char * *allResourceNames = NULL;
+int selectedLanguage = 0;
+int languageNum = -1;
+
+unsigned char *gameIcon = NULL;
+int iconW = 0, iconH = 0;
+
+unsigned char *gameLogo = NULL;
+int logoW = 0, logoH = 0;
+
+int gameVersion;
+int specialSettings;
+FILETIME fileTime;
+extern int desiredfps;
+bool captureAllKeys = false;
+
+unsigned char brightnessLevel = 255;
+
+eventHandlers mainHandlers;
+eventHandlers *currentEvents = & mainHandlers;
+
+extern HWND hMainWindow;
+extern screenRegion *overRegion;
+extern speechStruct *speech;
+extern statusStuff *nowStatus;
+extern loadedFunction *saverFunc;
+
+loadedFunction *allRunningFunctions = NULL;
+screenRegion *lastRegion = NULL;
+variableStack *noStack = NULL;
+char *loadNow = NULL;
+inputType input;
+variable *globalVars;
+int numGlobals;
+
+const char *sludgeText[] = { "?????", "RETURN", "BRANCH", "BR_ZERO", "SET_GLOBAL",
+ "SET_LOCAL", "LOAD_GLOBAL", "LOAD_LOCAL",
+ "PLUS", "MINUS", "MULT", "DIVIDE",
+ "AND", "OR", "EQUALS", "NOT_EQ", "MODULUS", "LOAD_VALUE",
+ "LOAD_BUILT", "LOAD_FUNC", "CALLIT", "LOAD_STRING", "LOAD_FILE",
+ "LOAD_OBJTYPE", "NOT", "LOAD_NULL", "STACK_PUSH",
+ "LESSTHAN", "MORETHAN", "NEGATIVE", "U", "LESS_EQUAL", "MORE_EQUAL",
+ "INC_LOCAL", "DEC_LOCAL", "INC_GLOBAL", "DEC_GLOBAL", "INDEXSET", "INDEXGET",
+ "INC_INDEX", "DEC_INDEX", "QUICK_PUSH"
+ };
+#if ALLOW_FILE
+void loadHandlers(FILE *fp) {
+ currentEvents -> leftMouseFunction = get2bytes(fp);
+ currentEvents -> leftMouseUpFunction = get2bytes(fp);
+ currentEvents -> rightMouseFunction = get2bytes(fp);
+ currentEvents -> rightMouseUpFunction = get2bytes(fp);
+ currentEvents -> moveMouseFunction = get2bytes(fp);
+ currentEvents -> focusFunction = get2bytes(fp);
+ currentEvents -> spaceFunction = get2bytes(fp);
+}
+
+void saveHandlers(FILE *fp) {
+ put2bytes(currentEvents -> leftMouseFunction, fp);
+ put2bytes(currentEvents -> leftMouseUpFunction, fp);
+ put2bytes(currentEvents -> rightMouseFunction, fp);
+ put2bytes(currentEvents -> rightMouseUpFunction, fp);
+ put2bytes(currentEvents -> moveMouseFunction, fp);
+ put2bytes(currentEvents -> focusFunction, fp);
+ put2bytes(currentEvents -> spaceFunction, fp);
+}
+
+FILE *openAndVerify(char *filename, char extra1, char extra2, const char *er, int &fileVersion) {
+ FILE *fp = fopen(filename, "rb");
+ if (! fp) {
+ fatal("Can't open file", filename);
+ return NULL;
+ }
+ bool headerBad = false;
+ if (fgetc(fp) != 'S') headerBad = true;
+ if (fgetc(fp) != 'L') headerBad = true;
+ if (fgetc(fp) != 'U') headerBad = true;
+ if (fgetc(fp) != 'D') headerBad = true;
+ if (fgetc(fp) != extra1) headerBad = true;
+ if (fgetc(fp) != extra2) headerBad = true;
+ if (headerBad) {
+ fatal(er, filename);
+ return NULL;
+ }
+ char c;
+ c = fgetc(fp);
+ putchar(c);
+ while (c = fgetc(fp)) {
+ putchar(c);
+ }
+
+ int majVersion = fgetc(fp);
+ printf("$majVersion %i\n", majVersion);
+ int minVersion = fgetc(fp);
+ printf("$minVersion %i\n", minVersion);
+ fileVersion = majVersion * 256 + minVersion;
+
+ char txtVer[120];
+
+ if (fileVersion > WHOLE_VERSION) {
+ sprintf(txtVer, ERROR_VERSION_TOO_LOW_2, majVersion, minVersion);
+ fatal(ERROR_VERSION_TOO_LOW_1, txtVer);
+ return NULL;
+ } else if (fileVersion < MINIM_VERSION) {
+ sprintf(txtVer, ERROR_VERSION_TOO_HIGH_2, majVersion, minVersion);
+ fatal(ERROR_VERSION_TOO_HIGH_1, txtVer);
+ return NULL;
+ }
+ return fp;
+}
+#endif
+
+bool initSludge(char *filename) {
+ int a = 0;
+ mouseCursorAnim = makeNullAnim();
+
+#if ALLOW_FILE
+ FILE *fp = openAndVerify(filename, 'G', 'E', ERROR_BAD_HEADER, gameVersion);
+ if (! fp) return false;
+
+ char c = fgetc(fp);
+ putchar(c);
+ if (c) {
+ numBIFNames = get2bytes(fp);
+ printf("numBIFNames %i\n", numBIFNames);
+ allBIFNames = new char *[numBIFNames];
+ if (! checkNew(allBIFNames)) return false;
+
+ for (int fn = 0; fn < numBIFNames; fn ++) {
+ allBIFNames[fn] = readString(fp);
+ printf("%s\n", allBIFNames[fn]);
+ }
+ numUserFunc = get2bytes(fp);
+ printf("numUserFunc %i\n", numUserFunc);
+ allUserFunc = new char *[numUserFunc];
+ if (! checkNew(allUserFunc)) return false;
+
+ for (int fn = 0; fn < numUserFunc; fn ++) {
+ allUserFunc[fn] = readString(fp);
+ printf("%s\n", allUserFunc[fn]);
+ }
+ if (gameVersion >= VERSION(1, 3)) {
+ numResourceNames = get2bytes(fp);
+ printf("numResourceNames %i\n", numResourceNames);
+ allResourceNames = new char *[numResourceNames];
+ if (! checkNew(allResourceNames)) return false;
+
+ for (int fn = 0; fn < numResourceNames; fn ++) {
+ allResourceNames[fn] = readString(fp);
+ printf("%s\n", allResourceNames[fn]);
+ }
+ }
+ }
+
+ winWidth = get2bytes(fp);
+ printf("winWidth : %i\n", winWidth);
+ winHeight = get2bytes(fp);
+ printf("winHeight : %i\n", winHeight);
+ specialSettings = fgetc(fp);
+ printf("specialSettings : %i\n", specialSettings);
+ desiredfps = 1000 / fgetc(fp);
+
+ delete[] readString(fp); // Unused - was used for registration purposes.
+
+ size_t bytes_read = fread(& fileTime, sizeof(FILETIME), 1, fp);
+ if (bytes_read != sizeof(FILETIME) && ferror(fp)) {
+ debugOut("Reading error in initSludge.\n");
+ }
+
+ char *dataFol = (gameVersion >= VERSION(1, 3)) ? readString(fp) : joinStrings("", "");
+ printf("dataFol : %s\n", dataFol);
+
+ gameSettings.numLanguages = (gameVersion >= VERSION(1, 3)) ? (fgetc(fp)) : 0;
+ printf("numLanguages : %c\n", gameSettings.numLanguages);
+ makeLanguageTable(fp);
+
+ if (gameVersion >= VERSION(1, 6)) {
+ fgetc(fp);
+ // aaLoad
+ fgetc(fp);
+ getFloat(fp);
+ getFloat(fp);
+ }
+
+ char *checker = readString(fp);
+ printf("checker : %s\n", checker);
+
+ if (strcmp(checker, "okSoFar")) return fatal(ERROR_BAD_HEADER, filename);
+ delete checker;
+ checker = NULL;
+
+ unsigned char customIconLogo = fgetc(fp);
+ putchar(customIconLogo);
+
+ if (customIconLogo & 1) {
+ // There is an icon - read it!
+ int n;
+
+ long file_pointer = ftell(fp);
+
+ png_structp png_ptr;
+ png_infop info_ptr, end_info;
+
+ int fileIsPNG = true;
+
+ // Is this a PNG file?
+
+ char tmp[10];
+ bytes_read = fread(tmp, 1, 8, fp);
+ if (bytes_read != 8 && ferror(fp)) {
+ debugOut("Reading error in initSludge.\n");
+ }
+ if (png_sig_cmp((png_byte *) tmp, 0, 8)) {
+ // No, it's old-school HSI
+ fileIsPNG = false;
+ fseek(fp, file_pointer, SEEK_SET);
+
+ iconW = get2bytes(fp);
+ iconH = get2bytes(fp);
+ } else {
+ // Read the PNG header
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
+ return false;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ return false;
+ }
+ png_init_io(png_ptr, fp); // Tell libpng which file to read
+ png_set_sig_bytes(png_ptr, 8); // 8 bytes already read
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, compression_type, filter_method;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ iconW = width;
+ iconH = height;
+
+ if (bit_depth < 8) png_set_packing(png_ptr);
+ png_set_expand(png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr);
+ if (bit_depth == 16) png_set_strip_16(png_ptr);
+
+ png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
+
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ }
+
+ gameIcon = new unsigned char [iconW * iconH * 4];
+ if (! gameIcon) return fatal("Can't reserve memory for game icon.");
+
+ int32_t transCol = 63519;
+ Uint8 *p = (Uint8 *) gameIcon;
+
+ if (fileIsPNG) {
+ unsigned char *row_pointers[iconH];
+ for (int i = 0; i < iconH; i++)
+ row_pointers[i] = p + 4 * i * iconW;
+
+ png_read_image(png_ptr, (png_byte **) row_pointers);
+ png_read_end(png_ptr, NULL);
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ } else {
+
+ for (int t2 = 0; t2 < iconH; t2 ++) {
+ int t1 = 0;
+ while (t1 < iconW) {
+ unsigned short c = (unsigned short) get2bytes(fp);
+ if (c & 32) {
+ n = fgetc(fp) + 1;
+ c -= 32;
+ } else {
+ n = 1;
+ }
+ while (n --) {
+ *p++ = (Uint8) redValue(c);
+ *p++ = (Uint8) greenValue(c);
+ *p++ = (Uint8) blueValue(c);
+ *p++ = (Uint8)(c == transCol) ? 0 : 255;
+
+ t1++;
+ }
+ }
+ }
+ }
+ }
+
+ if (customIconLogo & 2) {
+ // There is an logo - read it!
+ int n;
+
+ long file_pointer = ftell(fp);
+
+ png_structp png_ptr;
+ png_infop info_ptr, end_info;
+
+ int fileIsPNG = true;
+
+ // Is this a PNG file?
+
+ char tmp[10];
+ bytes_read = fread(tmp, 1, 8, fp);
+ if (bytes_read != 8 && ferror(fp)) {
+ debugOut("Reading error in initSludge.\n");
+ }
+ if (png_sig_cmp((png_byte *) tmp, 0, 8)) {
+ // No, it's old-school HSI
+ fileIsPNG = false;
+ fseek(fp, file_pointer, SEEK_SET);
+
+ logoW = get2bytes(fp);
+ logoH = get2bytes(fp);
+ } else {
+ // Read the PNG header
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
+ return false;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ return false;
+ }
+ png_init_io(png_ptr, fp); // Tell libpng which file to read
+ png_set_sig_bytes(png_ptr, 8); // 8 bytes already read
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, compression_type, filter_method;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ logoW = width;
+ logoH = height;
+
+ if (bit_depth < 8) png_set_packing(png_ptr);
+ png_set_expand(png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr);
+ if (bit_depth == 16) png_set_strip_16(png_ptr);
+#ifdef WIN32
+ // Windows wants a BGR bitmap
+ if (color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+ png_set_bgr(png_ptr);
+#endif
+
+ png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
+
+ png_read_update_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ }
+
+ if ((logoW != 310) || (logoH != 88)) return fatal("Game logo have wrong dimensions. (Should be 310x88)");
+
+ gameLogo = new unsigned char [logoW * logoH * 4];
+ if (! gameLogo) return fatal("Can't reserve memory for game logo.");
+
+ // int32_t transCol = 63519;
+ Uint8 *p = (Uint8 *) gameLogo;
+
+ if (fileIsPNG) {
+ unsigned char *row_pointers[logoH];
+ for (int i = 0; i < logoH; i++)
+ row_pointers[i] = p + 4 * i * logoW;
+
+ png_read_image(png_ptr, (png_byte **) row_pointers);
+ png_read_end(png_ptr, NULL);
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ } else {
+
+ for (int t2 = 0; t2 < logoH; t2 ++) {
+ int t1 = 0;
+ while (t1 < logoW) {
+ unsigned short c = (unsigned short) get2bytes(fp);
+ if (c & 32) {
+ n = fgetc(fp) + 1;
+ c -= 32;
+ } else {
+ n = 1;
+ }
+ while (n --) {
+#ifdef WIN32
+ // Windows wants a BGR bitmap
+ *p++ = (Uint8) blueValue(c);
+ *p++ = (Uint8) greenValue(c);
+ *p++ = (Uint8) redValue(c);
+#else
+ *p++ = (Uint8) redValue(c);
+ *p++ = (Uint8) greenValue(c);
+ *p++ = (Uint8) blueValue(c);
+#endif
+ *p++ = (Uint8) /*(c == transCol) ? 0 :*/ 255;
+
+ t1++;
+ }
+ }
+ }
+ }
+ }
+
+ numGlobals = get2bytes(fp);
+ printf("numGlobals : %i\n", numGlobals);
+
+ globalVars = new variable[numGlobals];
+ if (! checkNew(globalVars)) return false;
+ for (a = 0; a < numGlobals; a ++) initVarNew(globalVars[a]);
+
+ // Get the original (untranslated) name of the game and convert it to Unicode.
+ // We use this to find saved preferences and saved games.
+ setFileIndices(fp, gameSettings.numLanguages, 0);
+ char *gameNameOrig = getNumberedString(1);
+
+ char *gameName = encodeFilename(gameNameOrig);
+
+ delete gameNameOrig;
+
+ changeToUserDir();
+
+#ifdef _WIN32
+ mkdir(gameName);
+#else
+ mkdir(gameName, 0000777);
+#endif
+
+ if (chdir(gameName)) return fatal("This game's preference folder is inaccessible!\nI can't access the following directory (maybe there's a file with the same name, or maybe it's read-protected):", gameName);
+
+ delete [] gameName;
+
+ // Get user settings
+ readIniFile(filename);
+
+ // There's no startup window on Linux and respecting this
+ // option from the ini file would disable commandline options.
+#if defined __unix__ && !(defined __APPLE__)
+ if (! showSetupWindow()) return 0;
+ saveIniFile(filename);
+#else
+ if (! gameSettings.noStartWindow) {
+ if (! showSetupWindow()) return 0;
+ saveIniFile(filename);
+ }
+#endif
+
+ // Now set file indices properly to the chosen language.
+ languageNum = getLanguageForFileB();
+ if (languageNum < 0) return fatal("Can't find the translation data specified!");
+ setFileIndices(NULL, gameSettings.numLanguages, languageNum);
+
+ if (dataFol[0]) {
+ char *dataFolder = encodeFilename(dataFol);
+#ifdef _WIN32
+ mkdir(dataFolder);
+#else
+ mkdir(dataFolder, 0000777);
+#endif
+
+ if (chdir(dataFolder)) return fatal("This game's data folder is inaccessible!\nI can't access the following directory (maybe there's a file with the same name, or maybe it's read-protected):", dataFolder);
+
+ delete dataFolder;
+ }
+
+ positionStatus(10, winHeight - 15);
+#endif
+ return true;
+}
+
+extern int cameraX, cameraY;
+extern float cameraZoom;
+
+bool checkColourChange(bool reset) {
+#if 0
+ static GLuint oldPixel;
+ static GLuint pixel;
+ glReadPixels((GLint)(viewportOffsetX + input.mouseX * viewportWidth / ((float)winWidth / cameraZoom)),
+ (GLint)(viewportOffsetY + (((float)winHeight / cameraZoom) - input.mouseY)*viewportHeight / ((float)winHeight / cameraZoom)),
+ 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
+
+ if (reset || oldPixel != pixel) {
+ oldPixel = pixel;
+ return true;
+ }
+#endif
+ return false;
+}
+void sludgeDisplay() {
+#if 0
+#if defined(HAVE_GLES2)
+#ifndef GL_DEPTH24_STENCIL8
+#define GL_DEPTH24_STENCIL8 0x88F0
+#endif
+ // create an FBO
+ static GLuint fbo = 0;
+ static GLuint fbo_tex = 0;
+ static GLuint fbo_rbo = 0;
+ static float fbo_tex_w, fbo_tex_h;
+ static GLuint fbo_shad, fbo_vert, fbo_frag;
+ if (fbo == 0) {
+ // create FBO
+ int width = 1;
+ while (width < realWinWidth) width *= 2;
+ int height = 1;
+ while (height < realWinHeight) height *= 2;
+ glGenFramebuffers(1, &fbo);
+ glGenTextures(1, &fbo_tex);
+ glGenRenderbuffers(1, &fbo_rbo);
+ glBindTexture(GL_TEXTURE_2D, fbo_tex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindRenderbuffer(GL_RENDERBUFFER, fbo_rbo);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_tex, 0);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo_rbo);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo_rbo);
+ GLenum ret = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ printf("Screen is %dx%d, FBO(%dx%d) Status = 0x%04X\n", realWinWidth, realWinHeight, width, height, ret);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ fbo_tex_w = (float)realWinWidth / width;
+ fbo_tex_h = (float)realWinHeight / height;
+ // create shader for blitting the fbo...
+ const char _blit_vsh[] = " \n\t" \
+ "attribute highp vec2 aPosition; \n\t" \
+ "attribute highp vec2 aTexCoord; \n\t" \
+ "varying mediump vec2 vTexCoord; \n\t" \
+ "void main(){ \n\t" \
+ "gl_Position = vec4(aPosition.x, aPosition.y, 0.0, 1.0);\n\t" \
+ "vTexCoord = aTexCoord; \n\t" \
+ "} \n\t";
+
+ const char _blit_fsh[] = " \n\t" \
+ "uniform sampler2D uTex; \n\t" \
+ "varying mediump vec2 vTexCoord; \n\t" \
+ "void main(){ \n\t" \
+ "gl_FragColor = texture2D(uTex, vTexCoord); \n\t" \
+ "} \n\t";
+
+ GLint success;
+ fbo_frag = glCreateShader(GL_FRAGMENT_SHADER);
+ const char *src[1];
+ src[0] = _blit_fsh;
+ glShaderSource(fbo_frag, 1, src, NULL);
+ glCompileShader(fbo_frag);
+ glGetShaderiv(fbo_frag, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ printf("Failed to produce default fragment shader.\n");
+ }
+ fbo_vert = glCreateShader(GL_VERTEX_SHADER);
+ src[0] = _blit_vsh;
+ glShaderSource(fbo_vert, 1, src, NULL);
+ glCompileShader(fbo_vert);
+ glGetShaderiv(fbo_vert, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ printf("Failed to produce default vertex shader.\n");
+ }
+ fbo_shad = glCreateProgram();
+ glBindAttribLocation(fbo_shad, 0, "aPosition");
+ glBindAttribLocation(fbo_shad, 1, "aTexCoord");
+ glAttachShader(fbo_shad, fbo_frag);
+ glAttachShader(fbo_shad, fbo_vert);
+ glLinkProgram(fbo_shad);
+ glGetProgramiv(fbo_shad, GL_LINK_STATUS, &success);
+ if (!success) {
+ printf("Failed to link default program.\n");
+ }
+ glUniform1i(glGetUniformLocation(fbo_shad, "uTex"), 0);
+
+ }
+#endif
+
+ glDepthMask(GL_TRUE);
+// glClearColor(0.5, 0.5, 1.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen
+ glDepthMask(GL_FALSE);
+
+ drawBackDrop(); // Draw the room
+ drawZBuffer(cameraX, cameraY, false);
+
+ glEnable(GL_DEPTH_TEST);
+ drawPeople(); // Then add any moving characters...
+ glDisable(GL_DEPTH_TEST);
+ viewSpeech(); // ...and anything being said
+ drawStatusBar();
+ displayCursor();
+
+ if (brightnessLevel < 255) fixBrightness(); // This is for transitionLevel special effects
+
+ glFlush();
+#if !defined(HAVE_GLES2)
+ SDL_GL_SwapBuffers();
+#else
+ if (fbo) {
+ // blit the FBO now
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ GLuint old_prog;
+ glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&old_prog);
+ glUseProgram(fbo_shad);
+ glViewport(0, 0, realWinWidth, realWinHeight);
+ const float vert[] = {
+ -1.0, -1.0, +0.0, +0.0,
+ +1.0, -1.0, fbo_tex_w, +0.0,
+ -1.0, +1.0, +0.0, fbo_tex_h,
+ +1.0, +1.0, fbo_tex_w, fbo_tex_h
+ };
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, fbo_tex);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (float *)vert);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (float *)vert + 2);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glUseProgram(old_prog);
+ glViewport(viewportOffsetX, viewportOffsetY, viewportWidth, viewportHeight);
+ }
+ EGL_SwapBuffers();
+ if (fbo) {
+ // Rebind FBO now
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ }
+#endif
+#endif
+}
+
+void pauseFunction(loadedFunction *fun) {
+ loadedFunction * * huntAndDestroy = & allRunningFunctions;
+ while (* huntAndDestroy) {
+ if (fun == * huntAndDestroy) {
+ (* huntAndDestroy) = (* huntAndDestroy) -> next;
+ fun->next = NULL;
+ } else {
+ huntAndDestroy = & (* huntAndDestroy) -> next;
+ }
+ }
+}
+
+void restartFunction(loadedFunction *fun) {
+ fun -> next = allRunningFunctions;
+ allRunningFunctions = fun;
+}
+
+void killSpeechTimers() {
+ loadedFunction *thisFunction = allRunningFunctions;
+
+ while (thisFunction) {
+ if (thisFunction -> freezerLevel == 0 && thisFunction -> isSpeech && thisFunction -> timeLeft) {
+ thisFunction -> timeLeft = 0;
+ thisFunction -> isSpeech = false;
+ }
+ thisFunction = thisFunction -> next;
+ }
+
+ killAllSpeech();
+}
+
+void completeTimers() {
+ loadedFunction *thisFunction = allRunningFunctions;
+
+ while (thisFunction) {
+ if (thisFunction -> freezerLevel == 0) thisFunction -> timeLeft = 0;
+ thisFunction = thisFunction -> next;
+ }
+}
+
+void finishFunction(loadedFunction *fun) {
+ int a;
+
+ pauseFunction(fun);
+ if (fun -> stack) fatal(ERROR_NON_EMPTY_STACK);
+ delete fun -> compiledLines;
+ for (a = 0; a < fun -> numLocals; a ++) unlinkVar(fun -> localVars[a]);
+ delete fun -> localVars;
+ unlinkVar(fun -> reg);
+ delete fun;
+ fun = NULL;
+}
+
+void abortFunction(loadedFunction *fun) {
+ int a;
+
+ pauseFunction(fun);
+ while (fun -> stack) trimStack(fun -> stack);
+ delete fun -> compiledLines;
+ for (a = 0; a < fun -> numLocals; a ++) unlinkVar(fun -> localVars[a]);
+ delete fun -> localVars;
+ unlinkVar(fun -> reg);
+ if (fun -> calledBy) abortFunction(fun -> calledBy);
+ delete fun;
+ fun = NULL;
+}
+
+int cancelAFunction(int funcNum, loadedFunction *myself, bool &killedMyself) {
+ int n = 0;
+ killedMyself = false;
+
+ loadedFunction *fun = allRunningFunctions;
+ while (fun) {
+ if (fun -> originalNumber == funcNum) {
+ fun -> cancelMe = true;
+ n ++;
+ if (fun == myself) killedMyself = true;
+ }
+ fun = fun -> next;
+ }
+ return n;
+}
+
+void freezeSubs() {
+ loadedFunction *thisFunction = allRunningFunctions;
+
+ while (thisFunction) {
+ if (thisFunction -> unfreezable) {
+ //msgBox ("SLUDGE debugging bollocks!", "Trying to freeze an unfreezable function!");
+ } else {
+ thisFunction -> freezerLevel ++;
+ }
+ thisFunction = thisFunction -> next;
+ }
+}
+
+void unfreezeSubs() {
+ loadedFunction *thisFunction = allRunningFunctions;
+
+ while (thisFunction) {
+ if (thisFunction -> freezerLevel) thisFunction -> freezerLevel --;
+ thisFunction = thisFunction -> next;
+ }
+}
+
+
+bool continueFunction(loadedFunction *fun) {
+ bool keepLooping = true;
+ bool advanceNow;
+ unsigned int param;
+ sludgeCommand com;
+
+ if (fun -> cancelMe) {
+ abortFunction(fun);
+ return true;
+ }
+
+// if (numBIFNames) newDebug ("*** Function:", allUserFunc[fun -> originalNumber]);
+
+ //debugOut ("SLUDGER: continueFunction\n");
+
+ while (keepLooping) {
+ advanceNow = true;
+ param = fun -> compiledLines[fun -> runThisLine].param;
+ com = fun -> compiledLines[fun -> runThisLine].theCommand;
+// fprintf (stderr, "com: %d param: %d (%s)\n", com, param,
+// (com < numSludgeCommands) ? sludgeText[com] : ERROR_UNKNOWN_MCODE); fflush(stderr);
+
+ if (numBIFNames) {
+ setFatalInfo(
+ (fun -> originalNumber < numUserFunc) ? allUserFunc[fun -> originalNumber] : "Unknown user function",
+ (com < numSludgeCommands) ? sludgeText[com] : ERROR_UNKNOWN_MCODE);
+// newDebug (
+// (com < numSludgeCommands) ? sludgeText[com] : "Unknown SLUDGE machine code",
+// param);
+ }
+
+ //debugOut ("SLUDGER: continueFunction - in da loop: %s\n", sludgeText[com]);
+
+ switch (com) {
+ case SLU_RETURN:
+ if (fun -> calledBy) {
+ loadedFunction *returnTo = fun -> calledBy;
+ if (fun -> returnSomething) copyVariable(fun -> reg, returnTo -> reg);
+ finishFunction(fun);
+ fun = returnTo;
+ restartFunction(fun);
+ } else {
+ finishFunction(fun);
+ advanceNow = false; // So we don't do anything else with "fun"
+ keepLooping = false; // So we drop out of the loop
+ }
+ break;
+
+ case SLU_CALLIT:
+ switch (fun -> reg.varType) {
+ case SVT_FUNC:
+ pauseFunction(fun);
+ if (numBIFNames) setFatalInfo(
+ (fun -> originalNumber < numUserFunc) ? allUserFunc[fun -> originalNumber] : "Unknown user function",
+ (fun -> reg.varData.intValue < numUserFunc) ? allUserFunc[fun -> reg.varData.intValue] : "Unknown user function");
+
+ if (! startNewFunctionNum(fun -> reg.varData.intValue, param, fun, fun -> stack)) return false;
+ fun = allRunningFunctions;
+ advanceNow = false; // So we don't do anything else with "fun"
+ break;
+
+ case SVT_BUILT: {
+ builtReturn br = callBuiltIn(fun -> reg.varData.intValue, param, fun);
+
+ switch (br) {
+ case BR_ERROR:
+ return fatal("Unknown error. This shouldn't happen. Please notify the SLUDGE developers.");
+
+ case BR_PAUSE:
+ pauseFunction(fun);
+ // No break!
+
+ case BR_KEEP_AND_PAUSE:
+ keepLooping = false;
+ break;
+
+ case BR_ALREADY_GONE:
+ keepLooping = false;
+ advanceNow = false;
+ break;
+
+ case BR_CALLAFUNC: {
+ int i = fun -> reg.varData.intValue;
+ setVariable(fun -> reg, SVT_INT, 1);
+ pauseFunction(fun);
+ if (numBIFNames) setFatalInfo(
+ (fun -> originalNumber < numUserFunc) ? allUserFunc[fun -> originalNumber] : "Unknown user function",
+ (i < numUserFunc) ? allUserFunc[i] : "Unknown user function");
+ if (! startNewFunctionNum(i, 0, fun, noStack, false)) return false;
+ fun = allRunningFunctions;
+ advanceNow = false; // So we don't do anything else with "fun"
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ default:
+ return fatal(ERROR_CALL_NONFUNCTION);
+ }
+ break;
+
+ // These all grab things and shove 'em into the register
+
+ case SLU_LOAD_NULL:
+ setVariable(fun -> reg, SVT_NULL, 0);
+ break;
+
+ case SLU_LOAD_FILE:
+ setVariable(fun -> reg, SVT_FILE, param);
+ break;
+
+ case SLU_LOAD_VALUE:
+ setVariable(fun -> reg, SVT_INT, param);
+ break;
+
+ case SLU_LOAD_LOCAL:
+ if (! copyVariable(fun -> localVars[param], fun -> reg)) return false;
+ break;
+
+ case SLU_AND:
+ setVariable(fun -> reg, SVT_INT, getBoolean(fun -> reg) && getBoolean(fun -> stack -> thisVar));
+ trimStack(fun -> stack);
+ break;
+
+ case SLU_OR:
+ setVariable(fun -> reg, SVT_INT, getBoolean(fun -> reg) || getBoolean(fun -> stack -> thisVar));
+ trimStack(fun -> stack);
+ break;
+
+ case SLU_LOAD_FUNC:
+ setVariable(fun -> reg, SVT_FUNC, param);
+ break;
+
+ case SLU_LOAD_BUILT:
+ setVariable(fun -> reg, SVT_BUILT, param);
+ break;
+
+ case SLU_LOAD_OBJTYPE:
+ setVariable(fun -> reg, SVT_OBJTYPE, param);
+ break;
+
+ case SLU_UNREG:
+ if (dialogValue != 1) fatal(ERROR_HACKER);
+ break;
+
+ case SLU_LOAD_STRING:
+ if (! loadStringToVar(fun -> reg, param)) {
+ return false;
+ }
+ break;
+
+ case SLU_INDEXGET:
+ case SLU_INCREMENT_INDEX:
+ case SLU_DECREMENT_INDEX:
+ switch (fun -> stack -> thisVar.varType) {
+ case SVT_NULL:
+ if (com == SLU_INDEXGET) {
+ setVariable(fun -> reg, SVT_NULL, 0);
+ trimStack(fun -> stack);
+ } else {
+ return fatal(ERROR_INCDEC_UNKNOWN);
+ }
+ break;
+
+ case SVT_FASTARRAY:
+ case SVT_STACK:
+ if (fun -> stack -> thisVar.varData.theStack -> first == NULL) {
+ return fatal(ERROR_INDEX_EMPTY);
+ } else {
+ int ii;
+ if (! getValueType(ii, SVT_INT, fun -> reg)) return false;
+ variable *grab = (fun -> stack -> thisVar.varType == SVT_FASTARRAY) ?
+ fastArrayGetByIndex(fun -> stack -> thisVar.varData.fastArray, ii)
+ :
+ stackGetByIndex(fun -> stack -> thisVar.varData.theStack -> first, ii);
+
+ trimStack(fun -> stack);
+
+ if (! grab) {
+ setVariable(fun -> reg, SVT_NULL, 0);
+ } else {
+ int ii;
+ switch (com) {
+ case SLU_INCREMENT_INDEX:
+ if (! getValueType(ii, SVT_INT, * grab)) return false;
+ setVariable(fun -> reg, SVT_INT, ii);
+ grab -> varData.intValue = ii + 1;
+ break;
+
+ case SLU_DECREMENT_INDEX:
+ if (! getValueType(ii, SVT_INT, * grab)) return false;
+ setVariable(fun -> reg, SVT_INT, ii);
+ grab -> varData.intValue = ii - 1;
+ break;
+
+ default:
+ if (! copyVariable(* grab, fun -> reg)) return false;
+ }
+ }
+ }
+ break;
+
+ default:
+ return fatal(ERROR_INDEX_NONSTACK);
+ }
+ break;
+
+ case SLU_INDEXSET:
+ switch (fun -> stack -> thisVar.varType) {
+ case SVT_STACK:
+ if (fun -> stack -> thisVar.varData.theStack -> first == NULL) {
+ return fatal(ERROR_INDEX_EMPTY);
+ } else {
+ int ii;
+ if (! getValueType(ii, SVT_INT, fun -> reg)) return false;
+ if (! stackSetByIndex(fun -> stack -> thisVar.varData.theStack -> first, ii, fun -> stack -> next -> thisVar)) {
+ return false;
+ }
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ }
+ break;
+
+ case SVT_FASTARRAY: {
+ int ii;
+ if (! getValueType(ii, SVT_INT, fun -> reg)) return false;
+ variable *v = fastArrayGetByIndex(fun -> stack -> thisVar.varData.fastArray, ii);
+ if (v == NULL) return fatal("Not within bounds of fast array.");
+ if (! copyVariable(fun -> stack -> next -> thisVar, * v)) return false;
+ trimStack(fun -> stack);
+ trimStack(fun -> stack);
+ }
+ break;
+
+ default:
+ return fatal(ERROR_INDEX_NONSTACK);
+ }
+ break;
+
+ // What can we do with the register? Well, we can copy it into a local
+ // variable, a global or onto the stack...
+
+ case SLU_INCREMENT_LOCAL: {
+ int ii;
+ if (! getValueType(ii, SVT_INT, fun -> localVars[param])) return false;
+ setVariable(fun -> reg, SVT_INT, ii);
+ setVariable(fun -> localVars[param], SVT_INT, ii + 1);
+ }
+ break;
+
+ case SLU_INCREMENT_GLOBAL: {
+ int ii;
+ if (! getValueType(ii, SVT_INT, globalVars[param])) return false;
+ setVariable(fun -> reg, SVT_INT, ii);
+ setVariable(globalVars[param], SVT_INT, ii + 1);
+ }
+ break;
+
+ case SLU_DECREMENT_LOCAL: {
+ int ii;
+ if (! getValueType(ii, SVT_INT, fun -> localVars[param])) return false;
+ setVariable(fun -> reg, SVT_INT, ii);
+ setVariable(fun -> localVars[param], SVT_INT, ii - 1);
+ }
+ break;
+
+ case SLU_DECREMENT_GLOBAL: {
+ int ii;
+ if (! getValueType(ii, SVT_INT, globalVars[param])) return false;
+ setVariable(fun -> reg, SVT_INT, ii);
+ setVariable(globalVars[param], SVT_INT, ii - 1);
+ }
+ break;
+
+ case SLU_SET_LOCAL:
+ if (! copyVariable(fun -> reg, fun -> localVars[param])) return false;
+ break;
+
+ case SLU_SET_GLOBAL:
+// newDebug (" Copying TO global variable", param);
+// newDebug (" Global type at the moment", globalVars[param].varType);
+ if (! copyVariable(fun -> reg, globalVars[param])) return false;
+// newDebug (" New type", globalVars[param].varType);
+ break;
+
+ case SLU_LOAD_GLOBAL:
+// newDebug (" Copying FROM global variable", param);
+// newDebug (" Global type at the moment", globalVars[param].varType);
+ if (! copyVariable(globalVars[param], fun -> reg)) return false;
+ break;
+
+ case SLU_STACK_PUSH:
+ if (! addVarToStack(fun -> reg, fun -> stack)) return false;
+ break;
+
+ case SLU_QUICK_PUSH:
+ if (! addVarToStackQuick(fun -> reg, fun -> stack)) return false;
+ break;
+
+ case SLU_NOT:
+ setVariable(fun -> reg, SVT_INT, ! getBoolean(fun -> reg));
+ break;
+
+ case SLU_BR_ZERO:
+ if (! getBoolean(fun -> reg)) {
+ advanceNow = false;
+ fun -> runThisLine = param;
+ }
+ break;
+
+ case SLU_BRANCH:
+ advanceNow = false;
+ fun -> runThisLine = param;
+ break;
+
+ case SLU_NEGATIVE: {
+ int i;
+ if (! getValueType(i, SVT_INT, fun -> reg)) return false;
+ setVariable(fun -> reg, SVT_INT, -i);
+ }
+ break;
+
+ // All these things rely on there being somet' on the stack
+
+ case SLU_MULT:
+ case SLU_PLUS:
+ case SLU_MINUS:
+ case SLU_MODULUS:
+ case SLU_DIVIDE:
+ case SLU_EQUALS:
+ case SLU_NOT_EQ:
+ case SLU_LESSTHAN:
+ case SLU_MORETHAN:
+ case SLU_LESS_EQUAL:
+ case SLU_MORE_EQUAL:
+ if (fun -> stack) {
+ int firstValue, secondValue;
+
+ switch (com) {
+ case SLU_PLUS:
+ addVariablesInSecond(fun -> stack -> thisVar, fun -> reg);
+ trimStack(fun -> stack);
+ break;
+
+ case SLU_EQUALS:
+ compareVariablesInSecond(fun -> stack -> thisVar, fun -> reg);
+ trimStack(fun -> stack);
+ break;
+
+ case SLU_NOT_EQ:
+ compareVariablesInSecond(fun -> stack -> thisVar, fun -> reg);
+ trimStack(fun -> stack);
+ fun -> reg.varData.intValue = ! fun -> reg.varData.intValue;
+ break;
+
+ default:
+ if (! getValueType(firstValue, SVT_INT, fun -> stack -> thisVar)) return false;
+ if (! getValueType(secondValue, SVT_INT, fun -> reg)) return false;
+ trimStack(fun -> stack);
+
+ switch (com) {
+ case SLU_MULT:
+ setVariable(fun -> reg, SVT_INT, firstValue * secondValue);
+ break;
+
+ case SLU_MINUS:
+ setVariable(fun -> reg, SVT_INT, firstValue - secondValue);
+ break;
+
+ case SLU_MODULUS:
+ setVariable(fun -> reg, SVT_INT, firstValue % secondValue);
+ break;
+
+ case SLU_DIVIDE:
+ setVariable(fun -> reg, SVT_INT, firstValue / secondValue);
+ break;
+
+ case SLU_LESSTHAN:
+ setVariable(fun -> reg, SVT_INT, firstValue < secondValue);
+ break;
+
+ case SLU_MORETHAN:
+ setVariable(fun -> reg, SVT_INT, firstValue > secondValue);
+ break;
+
+ case SLU_LESS_EQUAL:
+ setVariable(fun -> reg, SVT_INT, firstValue <= secondValue);
+ break;
+
+ case SLU_MORE_EQUAL:
+ setVariable(fun -> reg, SVT_INT, firstValue >= secondValue);
+ break;
+
+ default:
+ break;
+ }
+ }
+ } else {
+ return fatal(ERROR_NOSTACK);
+ }
+ break;
+
+ default:
+ return fatal(ERROR_UNKNOWN_CODE);
+ }
+
+ if (advanceNow) fun -> runThisLine ++;
+
+ }
+ return true;
+}
+
+
+bool runSludge() {
+
+ loadedFunction *thisFunction = allRunningFunctions;
+ loadedFunction *nextFunction;
+
+ while (thisFunction) {
+ nextFunction = thisFunction -> next;
+
+ if (! thisFunction -> freezerLevel) {
+ if (thisFunction -> timeLeft) {
+ if (thisFunction -> timeLeft < 0) {
+ if (! stillPlayingSound(findInSoundCache(speech -> lastFile))) {
+ thisFunction -> timeLeft = 0;
+ }
+ } else if (! -- (thisFunction -> timeLeft)) {
+ }
+ } else {
+ if (thisFunction -> isSpeech) {
+ thisFunction -> isSpeech = false;
+ killAllSpeech();
+ }
+ if (! continueFunction(thisFunction))
+ return false;
+ }
+ }
+
+ thisFunction = nextFunction;
+ }
+
+ if (loadNow) {
+ if (loadNow[0] == ':') {
+ saveGame(loadNow + 1);
+ setVariable(saverFunc -> reg, SVT_INT, 1);
+ } else {
+ if (! loadGame(loadNow)) return false;
+ }
+ delete loadNow;
+ loadNow = NULL;
+ }
+
+ return true;
+}
+
+bool loadFunctionCode(loadedFunction *newFunc) {
+#if ALLOW_FILE
+ printf("\nCurrent address: %li\n", ftell(bigDataFile));
+ unsigned int numLines, numLinesRead;
+
+ if (! openSubSlice(newFunc -> originalNumber)) return false;
+
+ printf("Load function code\n");
+
+ newFunc -> unfreezable = fgetc(bigDataFile);
+ numLines = get2bytes(bigDataFile);
+ printf("numLines: %i\n", numLines);
+ newFunc -> numArgs = get2bytes(bigDataFile);
+ printf("numArgs: %i\n", newFunc -> numArgs);
+ newFunc -> numLocals = get2bytes(bigDataFile);
+ printf("numLocals: %i\n", newFunc -> numLocals);
+ newFunc -> compiledLines = new lineOfCode[numLines];
+ if (! checkNew(newFunc -> compiledLines)) return false;
+
+ for (numLinesRead = 0; numLinesRead < numLines; numLinesRead ++) {
+ newFunc -> compiledLines[numLinesRead].theCommand = (sludgeCommand) fgetc(bigDataFile);
+ newFunc -> compiledLines[numLinesRead].param = get2bytes(bigDataFile);
+ printf("command line %i: %i\n", numLinesRead, newFunc->compiledLines[numLinesRead].theCommand);
+ }
+ finishAccess();
+
+ // Now we need to reserve memory for the local variables
+ newFunc -> localVars = new variable[newFunc -> numLocals];
+ if (! checkNew(newFunc -> localVars)) return false;
+ for (int a = 0; a < newFunc -> numLocals; a ++) {
+ initVarNew(newFunc -> localVars[a]);
+ }
+#endif
+
+ return true;
+}
+
+int startNewFunctionNum(unsigned int funcNum, unsigned int numParamsExpected, loadedFunction *calledBy, variableStack *&vStack, bool returnSommet) {
+ loadedFunction *newFunc = new loadedFunction;
+ checkNew(newFunc);
+ newFunc -> originalNumber = funcNum;
+
+ loadFunctionCode(newFunc);
+
+ if (newFunc -> numArgs != (int)numParamsExpected) return fatal("Wrong number of parameters!");
+ if (newFunc -> numArgs > newFunc -> numLocals) return fatal("More arguments than local variable space!");
+
+ // Now, lets copy the parameters from the calling function's stack...
+
+ while (numParamsExpected) {
+ numParamsExpected --;
+ if (vStack == NULL) return fatal("Corrupted file! The stack's empty and there were still parameters expected");
+ copyVariable(vStack -> thisVar, newFunc -> localVars[numParamsExpected]);
+ trimStack(vStack);
+ }
+
+ newFunc -> cancelMe = false;
+ newFunc -> timeLeft = 0;
+ newFunc -> returnSomething = returnSommet;
+ newFunc -> calledBy = calledBy;
+ newFunc -> stack = NULL;
+ newFunc -> freezerLevel = 0;
+ newFunc -> runThisLine = 0;
+ newFunc -> isSpeech = 0;
+ initVarNew(newFunc -> reg);
+
+ restartFunction(newFunc);
+ return 1;
+}
+
+int lastFramesPerSecond = -1;
+int thisFramesPerSecond = -1;
+#if 0
+Uint32 lastSeconds = 0;
+#endif
+
+bool handleInput() {
+ static int l = 0;
+#if 0
+ static Uint32 theTime;
+
+ theTime = SDL_GetTicks() / 1000;
+ if (lastSeconds != theTime) {
+ lastSeconds = theTime;
+ lastFramesPerSecond = thisFramesPerSecond;
+ thisFramesPerSecond = 1;
+ } else {
+ thisFramesPerSecond ++;
+ }
+// lastFramesPerSecond = theTime.wSecond;
+#endif
+ if (launchMe) {
+ if (l) {
+ // Still paused because of spawned thingy...
+ } else {
+ l = 1;
+
+ setVariable(* launchResult, SVT_INT, 0/*launch(launchMe) > 31*/); //TODO:false value
+ launchMe = NULL;
+ launchResult = NULL;
+ }
+ return true;
+ } else {
+ l = 0;
+ }
+
+ if (! overRegion) getOverRegion();
+
+ if (input.justMoved) {
+ if (currentEvents -> moveMouseFunction) {
+ if (! startNewFunctionNum(currentEvents -> moveMouseFunction, 0, NULL, noStack)) return false;
+ }
+ }
+ input.justMoved = false;
+
+ if (lastRegion != overRegion && currentEvents -> focusFunction) {
+ variableStack *tempStack = new variableStack;
+ if (! checkNew(tempStack)) return false;
+
+ initVarNew(tempStack -> thisVar);
+ if (overRegion) {
+ setVariable(tempStack -> thisVar, SVT_OBJTYPE, overRegion -> thisType -> objectNum);
+ } else {
+ setVariable(tempStack -> thisVar, SVT_INT, 0);
+ }
+ tempStack -> next = NULL;
+ if (! startNewFunctionNum(currentEvents -> focusFunction, 1, NULL, tempStack)) return false;
+ }
+ if (input.leftRelease && currentEvents -> leftMouseUpFunction) {
+ if (! startNewFunctionNum(currentEvents -> leftMouseUpFunction, 0, NULL, noStack)) return false;
+ }
+ if (input.rightRelease && currentEvents -> rightMouseUpFunction) {
+ if (! startNewFunctionNum(currentEvents -> rightMouseUpFunction, 0, NULL, noStack)) return false;
+ }
+ if (input.leftClick && currentEvents -> leftMouseFunction)
+ if (! startNewFunctionNum(currentEvents -> leftMouseFunction, 0, NULL, noStack)) return false;
+ if (input.rightClick && currentEvents -> rightMouseFunction) {
+ if (! startNewFunctionNum(currentEvents -> rightMouseFunction, 0, NULL, noStack)) return false;
+ }
+ if (input.keyPressed && currentEvents -> spaceFunction) {
+ char *tempString = NULL;
+ switch (input.keyPressed) {
+ case 127:
+ tempString = copyString("BACKSPACE");
+ break;
+ case 9:
+ tempString = copyString("TAB");
+ break;
+ case 13:
+ tempString = copyString("ENTER");
+ break;
+ case 27:
+ tempString = copyString("ESCAPE");
+ break;
+ /*
+ case 1112: tempString = copyString ("ALT+F1"); break;
+ case 1113: tempString = copyString ("ALT+F2"); break;
+ case 1114: tempString = copyString ("ALT+F3"); break;
+ case 1115: tempString = copyString ("ALT+F4"); break;
+ case 1116: tempString = copyString ("ALT+F5"); break;
+ case 1117: tempString = copyString ("ALT+F6"); break;
+ case 1118: tempString = copyString ("ALT+F7"); break;
+ case 1119: tempString = copyString ("ALT+F8"); break;
+ case 1120: tempString = copyString ("ALT+F9"); break;
+ case 1121: tempString = copyString ("ALT+F10"); break;
+ case 1122: tempString = copyString ("ALT+F11"); break;
+ case 1123: tempString = copyString ("ALT+F12"); break;
+
+ case 2019: tempString = copyString ("PAUSE"); break;
+ */
+ case 63276:
+ tempString = copyString("PAGE UP");
+ break;
+ case 63277:
+ tempString = copyString("PAGE DOWN");
+ break;
+ case 63275:
+ tempString = copyString("END");
+ break;
+ case 63273:
+ tempString = copyString("HOME");
+ break;
+ case 63234:
+ tempString = copyString("LEFT");
+ break;
+ case 63232:
+ tempString = copyString("UP");
+ break;
+ case 63235:
+ tempString = copyString("RIGHT");
+ break;
+ case 63233:
+ tempString = copyString("DOWN");
+ break;
+ /*
+ case 2045: tempString = copyString ("INSERT"); break;
+ case 2046: tempString = copyString ("DELETE"); break;
+ */
+ case 63236:
+ tempString = copyString("F1");
+ break;
+ case 63237:
+ tempString = copyString("F2");
+ break;
+ case 63238:
+ tempString = copyString("F3");
+ break;
+ case 63239:
+ tempString = copyString("F4");
+ break;
+ case 63240:
+ tempString = copyString("F5");
+ break;
+ case 63241:
+ tempString = copyString("F6");
+ break;
+ case 63242:
+ tempString = copyString("F7");
+ break;
+ case 63243:
+ tempString = copyString("F8");
+ break;
+ case 63244:
+ tempString = copyString("F9");
+ break;
+ case 63245:
+ tempString = copyString("F10");
+ break;
+ case 63246:
+ tempString = copyString("F11");
+ break;
+ case 63247:
+ tempString = copyString("F12");
+ break;
+
+ default:
+ if (input.keyPressed >= 256) {
+ //if (captureAllKeys) {
+ tempString = copyString("ABCDEF");
+ sprintf(tempString, "%i", input.keyPressed);
+ //}
+ } else {
+ tempString = copyString(" ");
+ tempString[0] = input.keyPressed;
+ }
+ }
+
+ if (tempString) {
+ variableStack *tempStack = new variableStack;
+ if (! checkNew(tempStack)) return false;
+ initVarNew(tempStack -> thisVar);
+ makeTextVar(tempStack -> thisVar, tempString);
+ delete tempString;
+ tempString = NULL;
+ tempStack -> next = NULL;
+ if (! startNewFunctionNum(currentEvents -> spaceFunction, 1, NULL, tempStack)) return false;
+ }
+ }
+ input.rightClick = false;
+ input.leftClick = false;
+ input.rightRelease = false;
+ input.leftRelease = false;
+ input.keyPressed = 0;
+ lastRegion = overRegion;
+ return runSludge();
+}
diff --git a/engines/sludge/sludger.h b/engines/sludge/sludger.h
new file mode 100644
index 0000000000..fb9734a995
--- /dev/null
+++ b/engines/sludge/sludger.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGER_H
+#define SLUDGER_H
+
+#include <stdint.h>
+
+#include "variable.h"
+#include "csludge.h"
+#include "language.h"
+
+#ifndef _WIN32
+typedef struct _FILETIME {
+ uint32_t dwLowDateTime;
+ uint32_t dwHighDateTime;
+} FILETIME;
+#endif
+
+
+struct eventHandlers {
+ int leftMouseFunction;
+ int leftMouseUpFunction;
+ int rightMouseFunction;
+ int rightMouseUpFunction;
+ int moveMouseFunction;
+ int focusFunction;
+ int spaceFunction;
+};
+
+struct lineOfCode {
+ sludgeCommand theCommand;
+ int32_t param;
+};
+
+struct loadedFunction {
+ int originalNumber;
+ lineOfCode *compiledLines;
+ int numLocals, timeLeft, numArgs;
+ variable *localVars;
+ variableStack *stack;
+ variable reg;
+ unsigned int runThisLine;
+ loadedFunction *calledBy;
+ loadedFunction *next;
+ bool returnSomething, isSpeech, unfreezable, cancelMe;
+ unsigned char freezerLevel;
+};
+
+struct inputType {
+ bool leftClick, rightClick, justMoved, leftRelease, rightRelease;
+ int mouseX, mouseY, keyPressed;
+};
+
+extern unsigned char *gameIcon;
+extern int iconW, iconH;
+
+
+bool initSludge(char *);
+void sludgeDisplay();
+int startNewFunctionNum(unsigned int, unsigned int, loadedFunction *, variableStack*&, bool = true);
+bool handleInput();
+void restartFunction(loadedFunction *fun);
+bool loadFunctionCode(loadedFunction *newFunc);
+#if ALLOW_FILE
+void loadHandlers(FILE *fp);
+void saveHandlers(FILE *fp);
+#endif
+void finishFunction(loadedFunction *fun);
+void abortFunction(loadedFunction *fun);
+#if ALLOW_FILE
+FILE *openAndVerify(char *filename, char extra1, char extra2, const char *er, int &fileVersion);
+#endif
+void freezeSubs();
+void unfreezeSubs();
+void completeTimers();
+void killSpeechTimers();
+int cancelAFunction(int funcNum, loadedFunction *myself, bool &killedMyself);
+
+#endif
diff --git a/engines/sludge/sound.h b/engines/sludge/sound.h
new file mode 100644
index 0000000000..61f834ba90
--- /dev/null
+++ b/engines/sludge/sound.h
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "variable.h"
+#ifdef _WIN32
+#include <windows.h>
+#else
+#define HWND void *
+#endif
+
+
+// Sound list stuff
+struct soundList {
+ int sound;
+ struct soundList *next;
+ struct soundList *prev;
+ int cacheIndex;
+ int vol;
+};
+soundList *deleteSoundFromList(soundList *s);
+void playSoundList(soundList *s);
+
+
+
+// GENERAL...
+bool initSoundStuff(HWND);
+void killSoundStuff();
+
+// MUSIC...
+bool playMOD(int, int, int);
+void stopMOD(int);
+void setMusicVolume(int a, int v);
+void setDefaultMusicVolume(int v);
+
+// SAMPLES...
+int cacheSound(int f);
+bool startSound(int, bool = false);
+void huntKillSound(int a);
+void huntKillFreeSound(int filenum);
+void setSoundVolume(int a, int v);
+void setDefaultSoundVolume(int v);
+void setSoundLoop(int a, int s, int e);
+bool stillPlayingSound(int ch);
+bool getSoundCacheStack(stackHandler *sH);
+int findInSoundCache(int a);
+
+#if ALLOW_FILE
+void debugSounds();
+void loadSounds(FILE *fp);
+void saveSounds(FILE *fp);
+#endif
+
+unsigned int getSoundSource(int index);
diff --git a/engines/sludge/sound_bass.cpp b/engines/sludge/sound_bass.cpp
new file mode 100644
index 0000000000..65d8c93df9
--- /dev/null
+++ b/engines/sludge/sound_bass.cpp
@@ -0,0 +1,391 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdint.h>
+
+#include "allfiles.h"
+#include "newfatal.h"
+#include "bass.h"
+#include "sound.h"
+#include "moreio.h"
+#include "fileset.h"
+
+#define MAX_SAMPLES 8
+#define MAX_MODS 3
+#define EFFECT_CHANNELS 8
+#define TOTAL_CHANNELS 32
+
+bool soundOK = false;
+
+struct soundThing {
+ HSAMPLE sample;
+ int fileLoaded, vol;
+ int mostRecentChannel;
+ bool looping;
+};
+
+DWORD mod[MAX_MODS];
+
+soundThing soundCache[MAX_SAMPLES];
+
+int defVol = 128;
+int defSoundVol = 255;
+
+char *loadEntireFileToMemory(FILE *inputFile, uint32_t size) {
+ char *allData = new char[size];
+ if (! allData) return NULL;
+ fread(allData, size, 1, inputFile);
+ finishAccess();
+
+ return allData;
+}
+
+void stopMOD(int i) {
+ if (mod[i]) {
+ BASS_ChannelStop(mod[i]);
+ BASS_MusicFree(mod[i]);
+ mod[i] = NULL;
+ }
+}
+
+int findInSoundCache(int a) {
+ int i;
+ for (i = 0; i < MAX_SAMPLES; i ++) {
+ if (soundCache[i].fileLoaded == a) return i;
+ }
+ return -1;
+}
+
+void huntKillSound(int filenum) {
+ int gotSlot = findInSoundCache(filenum);
+ if (gotSlot == -1) return;
+ soundCache[gotSlot].looping = false;
+ BASS_SampleStop(soundCache[gotSlot].sample);
+}
+
+void freeSound(int a) {
+ BASS_SampleFree(soundCache[a].sample);
+ soundCache[a].sample = NULL;
+ soundCache[a].fileLoaded = -1;
+ soundCache[a].looping = false;
+}
+
+void huntKillFreeSound(int filenum) {
+ int gotSlot = findInSoundCache(filenum);
+ if (gotSlot != -1) freeSound(gotSlot);
+}
+
+bool initSoundStuff(HWND hwnd) {
+ if (HIWORD(BASS_GetVersion()) != BASSVERSION) {
+ warning(WARNING_BASS_WRONG_VERSION);
+ return false;
+ }
+
+ if (!BASS_Init(-1, 44100, 0, hwnd, NULL)) {
+ warning(WARNING_BASS_FAIL);
+ return false;
+ }
+
+ int a;
+ for (a = 0; a < MAX_SAMPLES; a ++) {
+ soundCache[a].sample = NULL;
+ soundCache[a].fileLoaded = -1;
+ }
+
+ BASS_SetConfig(BASS_CONFIG_GVOL_MUSIC, 10000);
+ BASS_SetConfig(BASS_CONFIG_GVOL_SAMPLE, 10000);
+ BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, 10000);
+ return soundOK = true;
+}
+
+void killSoundStuff() {
+ if (soundOK) {
+ int a;
+ for (a = 0; a < MAX_MODS; a ++) stopMOD(a);
+ for (a = 0; a < MAX_SAMPLES; a ++) freeSound(a);
+ BASS_Free();
+ }
+}
+
+bool playMOD(int f, int a, int fromTrack) {
+ if (soundOK) {
+
+ stopMOD(a);
+
+ setResourceForFatal(f);
+ uint32_t length = openFileFromNum(f);
+ if (length == 0) return NULL;
+
+ char *memImage;
+ memImage = loadEntireFileToMemory(bigDataFile, length);
+ if (! memImage) return fatal(ERROR_MUSIC_MEMORY_LOW);
+
+ mod[a] = BASS_MusicLoad(true, memImage, 0, length, BASS_MUSIC_LOOP | BASS_MUSIC_RAMP/*|BASS_MUSIC_PRESCAN needed too if we're going to set the position in bytes*/, 0);
+ delete memImage;
+
+ if (! mod[a]) {
+
+ } else {
+ setMusicVolume(a, defVol);
+
+ if (! BASS_ChannelPlay(mod[a], true))
+ debugOut("playMOD: Error %d!\n", BASS_ErrorGetCode());
+
+ BASS_ChannelSetPosition(mod[a], MAKELONG(fromTrack, 0), BASS_POS_MUSIC_ORDER);
+ BASS_ChannelFlags(mod[a], BASS_SAMPLE_LOOP | BASS_MUSIC_RAMP, BASS_SAMPLE_LOOP | BASS_MUSIC_RAMP);
+ }
+ setResourceForFatal(-1);
+ }
+ return true;
+}
+
+void setMusicVolume(int a, int v) {
+ int ret;
+ if (soundOK && mod[a]) {
+ ret = BASS_ChannelSetAttribute(mod[a], BASS_ATTRIB_VOL, (float) v / 256);
+ if (! ret) {
+ debugOut("setMusicVolume: Error %d\n", BASS_ErrorGetCode());
+ }
+ }
+}
+
+void setDefaultMusicVolume(int v) {
+ defVol = v;
+}
+
+void setSoundVolume(int a, int v) {
+ if (soundOK) {
+ int ch = findInSoundCache(a);
+ if (ch != -1) {
+ if (BASS_ChannelIsActive(soundCache[ch].mostRecentChannel)) {
+ BASS_ChannelSetAttribute(soundCache[ch].mostRecentChannel, BASS_ATTRIB_VOL, (float) v / 256);
+ }
+ }
+ }
+}
+
+bool stillPlayingSound(int ch) {
+ if (soundOK)
+ if (ch != -1)
+ if (soundCache[ch].fileLoaded != -1)
+ if (BASS_ChannelIsActive(soundCache[ch].mostRecentChannel) != BASS_ACTIVE_STOPPED)
+ return true;
+ return false;
+}
+
+void setSoundLoop(int a, int s, int e) {
+// if (soundOK) {
+// int ch = findInSoundCache (a);
+// if (ch != -1) {
+// int en = FSOUND_Sample_GetLength (soundCache[ch].sample);
+// if (e < 1 || e >= en) e = en - 1;
+// if (s < 0 || s >= e) s = 0;
+//
+// FSOUND_Sample_SetLoopPoints (soundCache[ch].sample, s, e);
+// }
+// }
+}
+
+void setDefaultSoundVolume(int v) {
+ defSoundVol = v;
+}
+
+int emptySoundSlot = 0;
+
+int findEmptySoundSlot() {
+ int t;
+ for (t = 0; t < MAX_SAMPLES; t ++) {
+ emptySoundSlot ++;
+ emptySoundSlot %= MAX_SAMPLES;
+ if (! soundCache[emptySoundSlot].sample)
+ return emptySoundSlot;
+ }
+
+ // Argh! They're all playing! Let's trash the oldest that's not looping...
+
+ for (t = 0; t < MAX_SAMPLES; t ++) {
+ emptySoundSlot ++;
+ emptySoundSlot %= MAX_SAMPLES;
+ if (! soundCache[emptySoundSlot].looping) return emptySoundSlot;
+ }
+
+ // Holy crap, they're all looping! What's this twat playing at?
+
+ emptySoundSlot ++;
+ emptySoundSlot %= MAX_SAMPLES;
+ return emptySoundSlot;
+}
+
+int guessSoundFree = 0;
+
+/*
+void soundWarning (char * t, int i) {
+ FILE * u = fopen ("soundlog.txt", "at");
+ fprintf (u, "%s: %i\n", t, i);
+ fclose (u);
+}
+*/
+
+bool forceRemoveSound() {
+ for (int a = 0; a < 8; a ++) {
+ if (soundCache[a].fileLoaded != -1 && ! stillPlayingSound(a)) {
+// soundWarning ("Deleting silent sound", a);
+ freeSound(a);
+ return 1;
+ }
+ }
+
+ for (int a = 0; a < 8; a ++) {
+ if (soundCache[a].fileLoaded != -1) {
+// soundWarning ("Deleting playing sound", a);
+ freeSound(a);
+ return 1;
+ }
+ }
+// soundWarning ("Cache is empty!", 0);
+ return 0;
+}
+
+int cacheSound(int f) {
+ setResourceForFatal(f);
+
+ if (! soundOK) return 0;
+
+ int a = findInSoundCache(f);
+ if (a != -1) return a;
+ if (f == -2) return -1;
+ a = findEmptySoundSlot();
+ freeSound(a);
+
+ uint32_t length = openFileFromNum(f);
+ if (! length) return -1;
+
+ char *memImage;
+
+ bool tryAgain = true;
+
+ while (tryAgain) {
+ memImage = loadEntireFileToMemory(bigDataFile, length);
+ tryAgain = memImage == NULL;
+ if (tryAgain) {
+ if (! forceRemoveSound()) {
+ fatal(ERROR_SOUND_MEMORY_LOW);
+ return -1;
+ }
+ }
+ }
+
+ for (;;) {
+// soundWarning (" Trying to load sound into slot", a);
+ soundCache[a].sample = BASS_SampleLoad(true, memImage, 0, length, 65535, 0);
+
+ if (soundCache[a].sample) {
+ soundCache[a].fileLoaded = f;
+ delete memImage;
+ setResourceForFatal(-1);
+ return a;
+ }
+
+ warning(ERROR_SOUND_ODDNESS);
+ soundCache[a].sample = NULL;
+ soundCache[a].fileLoaded = -1;
+ soundCache[a].looping = false;
+ return -1;
+ }
+}
+
+bool startSound(int f, bool loopy) {
+ if (soundOK) {
+ int a = cacheSound(f);
+ if (a == -1) return false;
+
+ soundCache[a].looping = loopy;
+ soundCache[a].vol = defSoundVol;
+
+ soundCache[a].mostRecentChannel = BASS_SampleGetChannel(soundCache[a].sample, false);
+ if (soundCache[a].mostRecentChannel) {
+ BASS_ChannelPlay(soundCache[a].mostRecentChannel, true);
+ BASS_ChannelSetAttribute(soundCache[a].mostRecentChannel, BASS_ATTRIB_VOL, defSoundVol);
+ if (loopy) {
+ BASS_ChannelFlags(soundCache[a].mostRecentChannel, BASS_SAMPLE_LOOP, BASS_SAMPLE_LOOP); // set LOOP flag
+ }
+ }
+
+ }
+ return true;
+}
+
+/*
+void debugSounds () {
+ FILE * fp = fopen ("newdebug.txt", "at");
+ if (fp) {
+ for (int aa = 0; aa < 32; aa ++) {
+ if (aa == EFFECT_CHANNELS) fprintf (fp, "|");
+ fprintf (fp, FSOUND_IsPlaying (aa) ? "#" : ".");
+ }
+ fprintf (fp, "\n");
+ fclose (fp);
+ }
+}
+// */
+
+void saveSounds(FILE *fp) {
+ if (soundOK) {
+ for (int i = 0; i < MAX_SAMPLES; i ++) {
+ if (soundCache[i].looping) {
+ fputc(1, fp);
+ put2bytes(soundCache[i].fileLoaded, fp);
+ put2bytes(soundCache[i].vol, fp);
+ }
+ }
+ }
+ fputc(0, fp);
+ put2bytes(defSoundVol, fp);
+ put2bytes(defVol, fp);
+}
+
+void loadSounds(FILE *fp) {
+ for (int i = 0; i < MAX_SAMPLES; i ++) freeSound(i);
+
+ while (fgetc(fp)) {
+ int fileLoaded = get2bytes(fp);
+ defSoundVol = get2bytes(fp);
+ startSound(fileLoaded, 1);
+ }
+
+ defSoundVol = get2bytes(fp);
+ defVol = get2bytes(fp);
+}
+
+bool getSoundCacheStack(stackHandler *sH) {
+ variable newFileHandle;
+ newFileHandle.varType = SVT_NULL;
+
+ for (int a = 0; a < MAX_SAMPLES; a ++) {
+ if (soundCache[a].fileLoaded != -1) {
+ setVariable(newFileHandle, SVT_FILE, soundCache[a].fileLoaded);
+ if (! addVarToStackQuick(newFileHandle, sH -> first)) return false;
+ if (sH -> last == NULL) sH -> last = sH -> first;
+ }
+ }
+ return true;
+}
+
diff --git a/engines/sludge/sound_nosound.cpp b/engines/sludge/sound_nosound.cpp
new file mode 100644
index 0000000000..0731d232d2
--- /dev/null
+++ b/engines/sludge/sound_nosound.cpp
@@ -0,0 +1,135 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdint.h>
+
+#include "allfiles.h"
+#include "newfatal.h"
+#include "sound.h"
+#include "moreio.h"
+#include "fileset.h"
+
+bool soundOK = false;
+
+int defVol = 128;
+int defSoundVol = 255;
+
+#if 0
+char *loadEntireFileToMemory(FILE *inputFile, uint32_t size) {
+ char *allData = new char[size];
+ if (! allData) return NULL;
+ fread(allData, size, 1, inputFile);
+ finishAccess();
+
+ return allData;
+}
+#endif
+
+int findInSoundCache(int a) {
+//#pragma unused(a)
+ return -1;
+}
+
+void stopMOD(int i) {
+//#pragma unused(i)
+}
+
+void huntKillSound(int filenum) {
+//#pragma unused(filenum)
+}
+
+void huntKillFreeSound(int filenum) {
+//#pragma unused(filenum)
+}
+
+bool initSoundStuff(HWND hwnd) {
+// #pragma unused(hwnd)
+ return false;
+}
+
+void killSoundStuff() {
+}
+
+bool playMOD(int f, int a, int fromTrack) {
+//#pragma unused (f,a,fromTrack)
+ return true;
+}
+
+void setMusicVolume(int a, int v) {
+//#pragma unused (a,v)
+}
+
+void setDefaultMusicVolume(int v) {
+ defVol = v;
+}
+
+void setSoundVolume(int a, int v) {
+//#pragma unused (a,v)
+}
+
+bool stillPlayingSound(int ch) {
+//#pragma unused (ch)
+ return false;
+}
+
+void setSoundLoop(int a, int s, int e) {
+//#pragma unused (a,s,e)
+}
+
+void setDefaultSoundVolume(int v) {
+ defSoundVol = v;
+}
+
+bool forceRemoveSound() {
+ return 0;
+}
+
+int cacheSound(int f) {
+//#pragma unused (f)
+ return 0;
+}
+
+bool startSound(int f, bool loopy) {
+//#pragma unused (f,loopy)
+ return true;
+}
+
+#if ALLOW_FILE
+void saveSounds(FILE *fp) {
+ fputc(0, fp);
+ put2bytes(defSoundVol, fp);
+ put2bytes(defVol, fp);
+}
+
+void loadSounds(FILE *fp) {
+ while (fgetc(fp)) {
+ get2bytes(fp);
+ get2bytes(fp);
+ }
+
+ defSoundVol = get2bytes(fp);
+ defVol = get2bytes(fp);
+}
+#endif
+bool getSoundCacheStack(stackHandler *sH) {
+//#pragma unused (sH)
+ return true;
+}
diff --git a/engines/sludge/sound_openal.cpp b/engines/sludge/sound_openal.cpp
new file mode 100644
index 0000000000..fdb1e0bc58
--- /dev/null
+++ b/engines/sludge/sound_openal.cpp
@@ -0,0 +1,817 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdint.h>
+#include <stdio.h>
+
+#if 0
+#include "AL/alure.h"
+#endif
+
+#include "allfiles.h"
+#include "debug.h"
+#include "newfatal.h"
+#include "sound.h"
+#include "moreio.h"
+#include "fileset.h"
+
+#define MAX_SAMPLES 8
+#define MAX_MODS 3
+#define NUM_BUFS 3
+
+bool soundOK = false;
+bool cacheLoopySound = false;
+bool SilenceIKillYou = false;
+
+struct soundThing {
+#if 0
+ alureStream *stream;
+ ALuint playingOnSource;
+#endif
+ bool playing;
+ int fileLoaded, vol; //Used for sounds only.
+ bool looping; //Used for sounds only.
+};
+
+soundThing soundCache[MAX_SAMPLES];
+soundThing modCache[MAX_MODS];
+int intpointers[MAX_SAMPLES];
+
+int defVol = 128;
+int defSoundVol = 255;
+const float modLoudness = 0.95f;
+
+/*
+ * Set up, tear down:
+ */
+
+bool initSoundStuff(HWND hwnd) {
+#if 0
+ if (!alureInitDevice(NULL, NULL)) {
+ debugOut("Failed to open OpenAL device: %s\n", alureGetErrorString());
+ return 1;
+ }
+
+ int a;
+ for (a = 0; a < MAX_SAMPLES; a ++) {
+ soundCache[a].stream = NULL;
+ soundCache[a].playing = false;
+ soundCache[a].fileLoaded = -1;
+ soundCache[a].looping = false;
+ intpointers[a] = a;
+ }
+
+ for (a = 0; a < MAX_MODS; a ++) {
+ modCache[a].stream = NULL;
+ modCache[a].playing = false;
+ }
+
+ if (! alureUpdateInterval(0.01)) {
+ debugOut("Failed to set Alure update interval: %s\n", alureGetErrorString());
+ return 1;
+ }
+#endif
+ return soundOK = true;
+}
+
+void killSoundStuff() {
+ if (! soundOK) return;
+#if 0
+ SilenceIKillYou = true;
+ for (int i = 0; i < MAX_SAMPLES; i ++) {
+ if (soundCache[i].playing) {
+
+ if (! alureStopSource(soundCache[i].playingOnSource, AL_TRUE)) {
+ debugOut("Failed to stop source: %s\n",
+ alureGetErrorString());
+ }
+
+ }
+
+ if (soundCache[i].stream != NULL) {
+
+ if (! alureDestroyStream(soundCache[i].stream, 0, NULL)) {
+ debugOut("Failed to destroy stream: %s\n",
+ alureGetErrorString());
+ }
+
+ }
+ }
+
+ for (int i = 0; i < MAX_MODS; i ++) {
+ if (modCache[i].playing) {
+
+ if (! alureStopSource(modCache[i].playingOnSource, AL_TRUE)) {
+ debugOut("Failed to stop source: %s\n",
+ alureGetErrorString());
+ }
+
+ }
+
+ if (modCache[i].stream != NULL) {
+
+ if (! alureDestroyStream(modCache[i].stream, 0, NULL)) {
+ debugOut("Failed to destroy stream: %s\n",
+ alureGetErrorString());
+ }
+
+ }
+ }
+
+ SilenceIKillYou = false;
+
+ alureShutdownDevice();
+#endif
+}
+
+/*
+ * Some setters:
+ */
+
+void setMusicVolume(int a, int v) {
+ if (! soundOK) return;
+
+ if (modCache[a].playing) {
+#if 0
+ alSourcef(modCache[a].playingOnSource, AL_GAIN, (float) modLoudness * v / 256);
+#endif
+ }
+}
+
+void setDefaultMusicVolume(int v) {
+ defVol = v;
+}
+
+void setSoundVolume(int a, int v) {
+ if (! soundOK) return;
+ int ch = findInSoundCache(a);
+ if (ch != -1) {
+ if (soundCache[ch].playing) {
+ soundCache[ch].vol = v;
+#if 0
+ alSourcef(soundCache[ch].playingOnSource, AL_GAIN, (float) v / 256);
+#endif
+ }
+ }
+}
+
+void setDefaultSoundVolume(int v) {
+ defSoundVol = v;
+}
+
+void setSoundLoop(int a, int s, int e) {
+//#pragma unused (a,s,e)
+}
+
+/*
+ * End of stream callbacks:
+ */
+
+#if 0
+static void sound_eos_callback(void *cacheIndex, ALuint source) {
+ int *a = (int *)cacheIndex;
+
+ alDeleteSources(1, &source);
+ if (alGetError() != AL_NO_ERROR) {
+ debugOut("Failed to delete OpenAL source!\n");
+ }
+
+ soundCache[*a].playingOnSource = 0;
+ soundCache[*a].playing = false;
+ soundCache[*a].looping = false;
+
+}
+#endif
+
+#if 0
+static void mod_eos_callback(void *cacheIndex, ALuint source) {
+ int *a = (int *)cacheIndex;
+
+ alDeleteSources(1, &source);
+ if (alGetError() != AL_NO_ERROR) {
+ debugOut("Failed to delete OpenAL source!\n");
+ }
+
+ modCache[*a].playingOnSource = 0;
+
+ if (! alureDestroyStream(modCache[*a].stream, 0, NULL)) {
+ debugOut("Failed to destroy stream: %s\n",
+ alureGetErrorString());
+ }
+
+ modCache[*a].stream = NULL;
+ modCache[*a].playing = false;
+}
+#endif
+
+/*
+ * Stopping things:
+ */
+
+int findInSoundCache(int a) {
+ int i;
+ for (i = 0; i < MAX_SAMPLES; i ++) {
+ if (soundCache[i].fileLoaded == a) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void stopMOD(int i) {
+ if (! soundOK) return;
+#if 0
+ alGetError();
+ if (modCache[i].playing) {
+ if (! alureStopSource(modCache[i].playingOnSource, AL_TRUE)) {
+ debugOut("Failed to stop source: %s\n", alureGetErrorString());
+ }
+ }
+#endif
+}
+
+void huntKillSound(int filenum) {
+ if (! soundOK) return;
+#if 0
+ // Clear OpenAL errors to make sure they don't block anything:
+ alGetError();
+
+ int gotSlot = findInSoundCache(filenum);
+ if (gotSlot == -1) return;
+
+ SilenceIKillYou = true;
+
+ if (soundCache[gotSlot].playing) {
+ if (! alureStopSource(soundCache[gotSlot].playingOnSource, AL_TRUE)) {
+ debugOut("Failed to stop source: %s\n", alureGetErrorString());
+ }
+ }
+#endif
+ SilenceIKillYou = false;
+}
+
+void freeSound(int a) {
+ if (! soundOK) return;
+#if 0
+ // Clear OpenAL errors to make sure they don't block anything:
+ alGetError();
+
+ SilenceIKillYou = true;
+
+ if (soundCache[a].playing) {
+ if (! alureStopSource(soundCache[a].playingOnSource, AL_TRUE)) {
+ debugOut("Failed to stop source: %s\n",
+ alureGetErrorString());
+ }
+ }
+ if (! alureDestroyStream(soundCache[a].stream, 0, NULL)) {
+ debugOut("Failed to destroy stream: %s\n",
+ alureGetErrorString());
+ }
+
+ soundCache[a].stream = NULL;
+ soundCache[a].fileLoaded = -1;
+#endif
+ SilenceIKillYou = false;
+}
+
+
+void huntKillFreeSound(int filenum) {
+ if (! soundOK) return;
+ int gotSlot = findInSoundCache(filenum);
+ if (gotSlot == -1) return;
+ freeSound(gotSlot);
+}
+
+/*
+ * Loading and playing:
+ */
+
+void playStream(int a, bool isMOD, bool loopy) {
+#if 0
+ if (! soundOK) return;
+ ALboolean ok;
+ ALuint src;
+ soundThing *st;
+ void (*eos_callback)(void *userdata, ALuint source);
+
+ if (isMOD) {
+ st = &modCache[a];
+ eos_callback = mod_eos_callback;
+ } else {
+ st = &soundCache[a];
+ eos_callback = sound_eos_callback;
+ }
+
+ alGenSources(1, &src);
+ if (alGetError() != AL_NO_ERROR) {
+ debugOut("Failed to create OpenAL source!\n");
+ return;
+ }
+
+ if (isMOD) {
+ alSourcef(src, AL_GAIN, (float) modLoudness * defVol / 256);
+ } else {
+ alSourcef(src, AL_GAIN, (float) soundCache[a].vol / 256);
+ }
+
+ if (loopy) {
+ ok = alurePlaySourceStream(src, (*st).stream,
+ NUM_BUFS, -1, eos_callback, &intpointers[a]);
+ } else {
+ ok = alurePlaySourceStream(src, (*st).stream,
+ NUM_BUFS, 0, eos_callback, &intpointers[a]);
+ }
+
+ if (!ok) {
+
+ debugOut("Failed to play stream: %s\n", alureGetErrorString());
+ alDeleteSources(1, &src);
+ if (alGetError() != AL_NO_ERROR) {
+ debugOut("Failed to delete OpenAL source!\n");
+ }
+
+ (*st).playingOnSource = 0;
+ } else {
+ (*st).playingOnSource = src;
+ (*st).playing = true;
+ }
+#endif
+}
+
+#if ALLOW_FILE
+char *loadEntireFileToMemory(FILE *inputFile, uint32_t size) {
+ char *allData = new char[size];
+ if (! allData) return NULL;
+
+ size_t bytes_read = fread(allData, size, 1, inputFile);
+ if (bytes_read != size && ferror(inputFile)) {
+ debugOut("Reading error in loadEntireFileToMemory.\n");
+ }
+
+ finishAccess();
+
+ return allData;
+}
+#endif
+
+bool playMOD(int f, int a, int fromTrack) {
+#if ALLOW_FILE
+ if (! soundOK) return true;
+ stopMOD(a);
+
+ setResourceForFatal(f);
+ uint32_t length = openFileFromNum(f);
+ if (length == 0) {
+ finishAccess();
+ setResourceForFatal(-1);
+ return false;
+ }
+#endif
+#if 0
+ unsigned char *memImage;
+ memImage = (unsigned char *) loadEntireFileToMemory(bigDataFile, length);
+ if (! memImage) return fatal(ERROR_MUSIC_MEMORY_LOW);
+
+ modCache[a].stream = alureCreateStreamFromMemory(memImage, length, 19200, 0, NULL);
+
+ delete memImage;
+
+ if (modCache[a].stream != NULL) {
+ setMusicVolume(a, defVol);
+
+ if (! alureSetStreamOrder(modCache[a].stream, fromTrack)) {
+ debugOut("Failed to set stream order: %s\n",
+ alureGetErrorString());
+ }
+
+ playStream(a, true, true);
+
+ } else {
+
+ debugOut("Failed to create stream from MOD: %s\n",
+ alureGetErrorString());
+
+ warning(ERROR_MUSIC_ODDNESS);
+ soundCache[a].stream = NULL;
+ soundCache[a].playing = false;
+ soundCache[a].playingOnSource = 0;
+ }
+ setResourceForFatal(-1);
+#endif
+ return true;
+}
+
+bool stillPlayingSound(int ch) {
+ if (soundOK)
+ if (ch != -1)
+ if (soundCache[ch].fileLoaded != -1)
+ if (soundCache[ch].playing)
+ return true;
+
+ return false;
+}
+
+bool forceRemoveSound() {
+ for (int a = 0; a < MAX_SAMPLES; a ++) {
+ if (soundCache[a].fileLoaded != -1 && ! stillPlayingSound(a)) {
+// soundWarning ("Deleting silent sound", a);
+ freeSound(a);
+ return 1;
+ }
+ }
+
+ for (int a = 0; a < MAX_SAMPLES; a ++) {
+ if (soundCache[a].fileLoaded != -1) {
+// soundWarning ("Deleting playing sound", a);
+ freeSound(a);
+ return 1;
+ }
+ }
+// soundWarning ("Cache is empty!", 0);
+ return 0;
+}
+
+int emptySoundSlot = 0;
+
+int findEmptySoundSlot() {
+ int t;
+ for (t = 0; t < MAX_SAMPLES; t ++) {
+ emptySoundSlot ++;
+ emptySoundSlot %= MAX_SAMPLES;
+#if 0
+ if (soundCache[emptySoundSlot].stream == NULL)
+ return emptySoundSlot;
+#endif
+ }
+
+ for (t = 0; t < MAX_SAMPLES; t ++) {
+ emptySoundSlot ++;
+ emptySoundSlot %= MAX_SAMPLES;
+ if (!soundCache[emptySoundSlot].playing)
+ return emptySoundSlot;
+ }
+
+ // Argh! They're all playing! Let's trash the oldest that's not looping...
+
+ for (t = 0; t < MAX_SAMPLES; t ++) {
+ emptySoundSlot ++;
+ emptySoundSlot %= MAX_SAMPLES;
+ if (! soundCache[emptySoundSlot].looping) return emptySoundSlot;
+ }
+
+ // Holy crap, they're all looping! What's this twat playing at?
+
+ emptySoundSlot ++;
+ emptySoundSlot %= MAX_SAMPLES;
+ return emptySoundSlot;
+}
+
+int cacheSound(int f) {
+
+ if (! soundOK) return -1;
+
+ unsigned int chunkLength;
+ int retval;
+ bool loopy;
+#if 0
+ loopy = cacheLoopySound;
+ cacheLoopySound = false;
+
+ setResourceForFatal(f);
+
+ if (! soundOK) return 0;
+
+ int a = findInSoundCache(f);
+ if (a != -1) {
+
+ if (soundCache[a].playing) {
+ if (! alureStopSource(soundCache[a].playingOnSource, AL_TRUE)) {
+ debugOut("Failed to stop source: %s\n",
+ alureGetErrorString());
+ }
+ }
+ if (! alureRewindStream(soundCache[a].stream)) {
+ debugOut("Failed to rewind stream: %s\n",
+ alureGetErrorString());
+ }
+
+ return a;
+ }
+ if (f == -2) return -1;
+ a = findEmptySoundSlot();
+ freeSound(a);
+
+ uint32_t length = openFileFromNum(f);
+ if (! length) return -1;
+
+ unsigned char *memImage;
+
+ bool tryAgain = true;
+
+ while (tryAgain) {
+ memImage = (unsigned char *)loadEntireFileToMemory(bigDataFile, length);
+ tryAgain = memImage == NULL;
+ if (tryAgain) {
+ if (! forceRemoveSound()) {
+ fatal(ERROR_SOUND_MEMORY_LOW);
+ return -1;
+ }
+ }
+ }
+
+ chunkLength = 19200;
+
+ // Small looping sounds need small chunklengths.
+ if (loopy) {
+ if (length < NUM_BUFS * chunkLength) {
+ chunkLength = length / NUM_BUFS;
+ }
+ } else if (length < chunkLength) {
+ chunkLength = length;
+ }
+
+ soundCache[a].stream = alureCreateStreamFromMemory(memImage, length, chunkLength, 0, NULL);
+
+ delete[] memImage;
+
+ if (soundCache[a].stream != NULL) {
+ soundCache[a].fileLoaded = f;
+ setResourceForFatal(-1);
+ retval = a;
+ } else {
+
+ debugOut("Failed to create stream from sound: %s\n",
+ alureGetErrorString());
+
+ warning(ERROR_SOUND_ODDNESS);
+ soundCache[a].stream = NULL;
+ soundCache[a].playing = false;
+ soundCache[a].playingOnSource = 0;
+ soundCache[a].fileLoaded = -1;
+ soundCache[a].looping = false;
+ retval = -1;
+ }
+#endif
+ return retval;
+}
+
+bool startSound(int f, bool loopy) {
+ if (soundOK) {
+ cacheLoopySound = loopy;
+ int a = cacheSound(f);
+ if (a == -1) {
+ debugOut("Failed to cache sound!\n");
+ return false;
+ }
+ soundCache[a].looping = loopy;
+ soundCache[a].vol = defSoundVol;
+
+ playStream(a, false, loopy);
+ }
+ return true;
+}
+
+#if ALLOW_FILE
+void saveSounds(FILE *fp) {
+ if (soundOK) {
+ for (int i = 0; i < MAX_SAMPLES; i ++) {
+ if (soundCache[i].looping) {
+ fputc(1, fp);
+ put2bytes(soundCache[i].fileLoaded, fp);
+ put2bytes(soundCache[i].vol, fp);
+ }
+ }
+ }
+ fputc(0, fp);
+ put2bytes(defSoundVol, fp);
+ put2bytes(defVol, fp);
+}
+
+void loadSounds(FILE *fp) {
+ for (int i = 0; i < MAX_SAMPLES; i ++) freeSound(i);
+
+ while (fgetc(fp)) {
+ int fileLoaded = get2bytes(fp);
+ defSoundVol = get2bytes(fp);
+ startSound(fileLoaded, 1);
+ }
+
+ defSoundVol = get2bytes(fp);
+ defVol = get2bytes(fp);
+}
+#endif
+
+bool getSoundCacheStack(stackHandler *sH) {
+ variable newFileHandle;
+ newFileHandle.varType = SVT_NULL;
+
+ for (int a = 0; a < MAX_SAMPLES; a ++) {
+ if (soundCache[a].fileLoaded != -1) {
+ setVariable(newFileHandle, SVT_FILE, soundCache[a].fileLoaded);
+ if (! addVarToStackQuick(newFileHandle, sH -> first)) return false;
+ if (sH -> last == NULL) sH -> last = sH -> first;
+ }
+ }
+ return true;
+}
+
+soundList *deleteSoundFromList(soundList *s) {
+ // Don't delete a playing sound.
+ if (s->cacheIndex) return NULL;
+
+ soundList *o = NULL;
+ if (! s->next) {
+ o = s->prev;
+ if (o) o-> next = NULL;
+ delete s;
+ return o;
+ }
+ if (s != s->next) {
+ o = s->next;
+ o->prev = s->prev;
+ if (o->prev) o->prev->next = o;
+ }
+ delete s;
+ return o;
+}
+
+#if 0
+static void list_eos_callback(void *list, ALuint source) {
+ soundList *s = (soundList *) list;
+
+ int a = s->cacheIndex;
+#if 0
+ alDeleteSources(1, &source);
+ if (alGetError() != AL_NO_ERROR) {
+ debugOut("Failed to delete OpenAL source!\n");
+ }
+#endif
+ soundCache[a].playingOnSource = 0;
+ soundCache[a].playing = false;
+ soundCache[a].looping = false;
+ s-> cacheIndex = false;
+ if (SilenceIKillYou) {
+ while (s = deleteSoundFromList(s));
+ } else {
+ if (s->next) {
+ if (s->next == s) {
+ int v = defSoundVol;
+ defSoundVol = soundCache[a].vol;
+ startSound(s->sound, true);
+ defSoundVol = v;
+ while (s = deleteSoundFromList(s));
+ return;
+ }
+ s->next->vol = soundCache[a].vol;
+ playSoundList(s->next);
+ } else {
+ while (s = deleteSoundFromList(s));
+ }
+ }
+}
+#endif
+
+void playSoundList(soundList *s) {
+#if 0
+ if (soundOK) {
+ cacheLoopySound = true;
+ int a = cacheSound(s->sound);
+ if (a == -1) {
+ debugOut("Failed to cache sound!\n");
+ return;
+ }
+ soundCache[a].looping = false;
+ if (s->vol < 0)
+ soundCache[a].vol = defSoundVol;
+ else
+ soundCache[a].vol = s->vol;
+ s-> cacheIndex = a;
+
+ ALboolean ok;
+ ALuint src;
+ soundThing *st;
+
+ st = &soundCache[a];
+
+ alGenSources(1, &src);
+ if (alGetError() != AL_NO_ERROR) {
+ debugOut("Failed to create OpenAL source!\n");
+ return;
+ }
+
+ alSourcef(src, AL_GAIN, (float) soundCache[a].vol / 256);
+
+ ok = alurePlaySourceStream(src, (*st).stream,
+ NUM_BUFS, 0, list_eos_callback, s);
+
+ if (!ok) {
+
+ debugOut("Failed to play stream: %s\n", alureGetErrorString());
+ alDeleteSources(1, &src);
+ if (alGetError() != AL_NO_ERROR) {
+ debugOut("Failed to delete OpenAL source!\n");
+ }
+
+ (*st).playingOnSource = 0;
+ } else {
+ (*st).playingOnSource = src;
+ (*st).playing = true;
+ }
+ }
+#endif
+}
+
+#if ALLOW_FILE
+void playMovieStream(int a) {
+ if (! soundOK) return;
+ ALboolean ok;
+ ALuint src;
+#if 0
+ alGenSources(1, &src);
+ if (alGetError() != AL_NO_ERROR) {
+ debugOut("Failed to create OpenAL source!\n");
+ return;
+ }
+
+ alSourcef(src, AL_GAIN, (float) soundCache[a].vol / 256);
+
+ ok = alurePlaySourceStream(src, soundCache[a].stream,
+ 10, 0, sound_eos_callback, &intpointers[a]);
+#endif
+ if (!ok) {
+#if 0
+ debugOut("Failed to play stream: %s\n", alureGetErrorString());
+ alDeleteSources(1, &src);
+ if (alGetError() != AL_NO_ERROR) {
+ debugOut("Failed to delete OpenAL source!\n");
+ }
+#endif
+ soundCache[a].playingOnSource = 0;
+ } else {
+ soundCache[a].playingOnSource = src;
+ soundCache[a].playing = true;
+ }
+}
+#endif
+
+#if 0
+int initMovieSound(int f, ALenum format, int audioChannels, ALuint samplerate,
+ ALuint(*callback)(void *userdata, ALubyte *data, ALuint bytes)) {
+ if (! soundOK) return 0;
+
+ int retval;
+ int a = findEmptySoundSlot();
+ freeSound(a);
+
+ soundCache[a].looping = false;
+#if 0
+ // audioChannel * sampleRate gives us a buffer of half a second. Not much, but it should be enough.
+ soundCache[a].stream = alureCreateStreamFromCallback(
+ callback,
+ &intpointers[a], format, samplerate,
+ audioChannels * samplerate, 0, NULL);
+#endif
+ if (soundCache[a].stream != NULL) {
+ soundCache[a].fileLoaded = f;
+ soundCache[a].vol = defSoundVol;
+ retval = a;
+ } else {
+#if 0
+ debugOut("Failed to create stream from sound: %s\n",
+ alureGetErrorString());
+#endif
+ warning(ERROR_SOUND_ODDNESS);
+ soundCache[a].stream = NULL;
+ soundCache[a].playing = false;
+ soundCache[a].playingOnSource = 0;
+ soundCache[a].fileLoaded = -1;
+ retval = -1;
+ }
+ //fprintf (stderr, "Stream %d created. Sample rate: %d Channels: %d\n", retval, samplerate, audioChannels);
+
+ return retval;
+}
+#endif
+
+unsigned int getSoundSource(int index) {
+ return 0; /*soundCache[index].playingOnSource;*/ //TODO:false value
+}
diff --git a/engines/sludge/sprbanks.cpp b/engines/sludge/sprbanks.cpp
new file mode 100644
index 0000000000..bb3303b759
--- /dev/null
+++ b/engines/sludge/sprbanks.cpp
@@ -0,0 +1,74 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+#include "sprites.h"
+#include "sprbanks.h"
+#include "newfatal.h"
+#include "debug.h"
+
+loadedSpriteBank *allLoadedBanks = NULL;
+extern spriteBank theFont;
+extern int loadedFontNum, fontTableSize;
+
+
+loadedSpriteBank *loadBankForAnim(int ID) {
+ //debugOut ("loadBankForAnim: Looking for sprite bank with ID %d\n", ID);
+ loadedSpriteBank *returnMe = allLoadedBanks;
+ while (returnMe) {
+ if (returnMe -> ID == ID) {
+ //debugOut ("loadBankForAnim: Found existing sprite bank with ID %d\n", returnMe -> ID);
+ return returnMe;
+ }
+ returnMe = returnMe -> next;
+ }
+ returnMe = new loadedSpriteBank;
+ //debugOut ("loadBankForAnim: No existing sprite bank with ID %d\n", ID);
+ if (checkNew(returnMe)) {
+ returnMe -> ID = ID;
+ if (loadSpriteBank(ID, returnMe -> bank, false)) {
+ returnMe -> timesUsed = 0;
+ returnMe -> next = allLoadedBanks;
+ allLoadedBanks = returnMe;
+ debugOut("loadBankForAnim: New sprite bank created OK\n");
+ return returnMe;
+ } else {
+ debugOut("loadBankForAnim: I guess I couldn't load the sprites...\n");
+ return NULL;
+ }
+ } else return NULL;
+}
+
+void reloadSpriteTextures() {
+ loadedSpriteBank *spriteBank = allLoadedBanks;
+ while (spriteBank) {
+ //fprintf (stderr, "Reloading bank %d: %s.\n", spriteBank->ID, resourceNameFromNum (spriteBank->ID));
+ delete spriteBank-> bank.sprites;
+ spriteBank-> bank.sprites = NULL;
+ loadSpriteBank(spriteBank->ID, spriteBank->bank, false);
+ spriteBank = spriteBank -> next;
+ }
+ if (fontTableSize) {
+ delete theFont.sprites;
+ theFont.sprites = NULL;
+ loadSpriteBank(loadedFontNum, theFont, true);
+ }
+}
diff --git a/engines/sludge/sprbanks.h b/engines/sludge/sprbanks.h
new file mode 100644
index 0000000000..3c0efd3364
--- /dev/null
+++ b/engines/sludge/sprbanks.h
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_SPRBANKS_H
+#define SLUDGE_SPRBANKS_H
+
+#include "sprites.h"
+
+struct loadedSpriteBank {
+ int ID, timesUsed;
+ spriteBank bank;
+ loadedSpriteBank *next;
+};
+
+loadedSpriteBank *loadBankForAnim(int ID);
+void reloadSpriteTextures();
+
+#endif
diff --git a/engines/sludge/sprites.cpp b/engines/sludge/sprites.cpp
new file mode 100644
index 0000000000..43ddcbe8a1
--- /dev/null
+++ b/engines/sludge/sprites.cpp
@@ -0,0 +1,1111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#if 0
+#if defined __unix__ && !(defined __APPLE__)
+#include <png.h>
+#else
+#include <libpng/png.h>
+#endif
+#endif
+
+#include <string.h>
+
+#include "allfiles.h"
+
+#include "fileset.h"
+#include "people.h"
+#include "sprites.h"
+#include "moreio.h"
+#include "newfatal.h"
+#include "colours.h"
+#include "backdrop.h"
+#include "sludger.h"
+#include "zbuffer.h"
+#include "debug.h"
+#include "graphics.h"
+
+#include "shaders.h"
+
+extern zBufferData zBuffer;
+
+#if 0
+extern GLuint backdropTextureName;
+#endif
+
+extern inputType input;
+extern int cameraX, cameraY;
+extern float cameraZoom;
+
+unsigned char currentBurnR = 0, currentBurnG = 0, currentBurnB = 0;
+
+
+void forgetSpriteBank(spriteBank &forgetme) {
+#if 0
+ deleteTextures(forgetme.myPalette.numTextures, forgetme.myPalette.tex_names);
+ if (forgetme.isFont) {
+ deleteTextures(forgetme.myPalette.numTextures, forgetme.myPalette.burnTex_names);
+ delete [] forgetme.myPalette.burnTex_names;
+ forgetme.myPalette.burnTex_names = NULL;
+ }
+
+ delete [] forgetme.myPalette.tex_names;
+ forgetme.myPalette.tex_names = NULL;
+#endif
+ delete [] forgetme.myPalette.tex_w;
+ forgetme.myPalette.tex_w = NULL;
+ delete [] forgetme.myPalette.tex_h;
+ forgetme.myPalette.tex_h = NULL;
+
+ if (forgetme.myPalette.pal) {
+ delete [] forgetme.myPalette.pal;
+ forgetme.myPalette.pal = NULL;
+ delete [] forgetme.myPalette.r;
+ forgetme.myPalette.r = NULL;
+ delete [] forgetme.myPalette.g;
+ forgetme.myPalette.g = NULL;
+ delete [] forgetme.myPalette.b;
+ forgetme.myPalette.b = NULL;
+ }
+
+ delete forgetme.sprites;
+ forgetme.sprites = NULL;
+
+
+ // TODO: also remove sprite bank from allLoadedBanks
+ // And add a function call for this function to the scripting language
+}
+
+bool reserveSpritePal(spritePalette &sP, int n) {
+ if (sP.pal) {
+ delete [] sP.pal;
+ delete [] sP.r;
+ delete [] sP.g;
+ delete [] sP.b;
+ }
+
+ sP.pal = new unsigned short int [n];
+ if (! checkNew(sP.pal)) return false;
+
+ sP.r = new unsigned char [n];
+ if (! checkNew(sP.r)) return false;
+ sP.g = new unsigned char [n];
+ if (! checkNew(sP.g)) return false;
+ sP.b = new unsigned char [n];
+ if (! checkNew(sP.b)) return false;
+ sP.total = n;
+ return (bool)(sP.pal != NULL) && (sP.r != NULL) && (sP.g != NULL) && (sP.b != NULL);
+}
+
+bool loadSpriteBank(int fileNum, spriteBank &loadhere, bool isFont) {
+#if ALLOW_FILE
+ int i, tex_num, total, picwidth, picheight, spriteBankVersion = 0, howmany = 0, startIndex = 0;
+ int *totalwidth, * maxheight;
+ int numTextures = 0;
+ byte *data;
+
+ setResourceForFatal(fileNum);
+ if (! openFileFromNum(fileNum)) return fatal("Can't open sprite bank / font");
+
+ loadhere.isFont = isFont;
+
+ total = get2bytes(bigDataFile);
+ if (! total) {
+ spriteBankVersion = fgetc(bigDataFile);
+ if (spriteBankVersion == 1) {
+ total = 0;
+ } else {
+ total = get2bytes(bigDataFile);
+ }
+ }
+
+ if (total <= 0) return fatal("No sprites in bank or invalid sprite bank file");
+ if (spriteBankVersion > 3) return fatal("Unsupported sprite bank file format");
+
+ loadhere.total = total;
+ loadhere.sprites = new sprite [total];
+ if (! checkNew(loadhere.sprites)) return false;
+ byte **spriteData = new byte * [total];
+ if (! checkNew(spriteData)) return false;
+
+ totalwidth = new int[total];
+ if (! checkNew(totalwidth)) return false;
+
+ maxheight = new int[total];
+ if (! checkNew(maxheight)) return false;
+
+ loadhere.myPalette.tex_names = new GLuint [total];
+ if (! checkNew(loadhere.myPalette.tex_names)) return false;
+
+ if (isFont) {
+ loadhere.myPalette.burnTex_names = new GLuint [total];
+ if (! checkNew(loadhere.myPalette.burnTex_names)) return false;
+ }
+ loadhere.myPalette.tex_w = new int [total];
+ if (! checkNew(loadhere.myPalette.tex_w)) return false;
+ loadhere.myPalette.tex_h = new int [total];
+ if (! checkNew(loadhere.myPalette.tex_h)) return false;
+
+ if (spriteBankVersion && spriteBankVersion < 3) {
+ howmany = fgetc(bigDataFile);
+ startIndex = 1;
+ }
+
+ totalwidth[0] = maxheight[0] = 1;
+
+ for (i = 0; i < total; i ++) {
+ switch (spriteBankVersion) {
+ case 3: {
+ loadhere.sprites[i].xhot = getSigned(bigDataFile);
+ loadhere.sprites[i].yhot = getSigned(bigDataFile);
+
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ return fatal("Can't open sprite bank / font.");
+ }
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
+ return fatal("Can't open sprite bank / font.");
+ }
+
+ png_infop end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ return fatal("Can't open sprite bank / font.");
+ }
+ png_init_io(png_ptr, bigDataFile); // Tell libpng which file to read
+ png_set_sig_bytes(png_ptr, 8); // No sig
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, compression_type, filter_method;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
+
+ int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ unsigned char *row_pointers[height];
+ spriteData[i] = new unsigned char [rowbytes * height];
+ if (! checkNew(spriteData[i])) return false;
+
+ for (unsigned int row = 0; row < height; row++)
+ row_pointers[row] = spriteData[i] + row * rowbytes;
+
+ png_read_image(png_ptr, (png_byte **) row_pointers);
+ png_read_end(png_ptr, NULL);
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+
+ picwidth = loadhere.sprites[i].width = width;
+ picheight = loadhere.sprites[i].height = height;
+ break;
+ }
+ case 2:
+ picwidth = get2bytes(bigDataFile);
+ picheight = get2bytes(bigDataFile);
+ loadhere.sprites[i].xhot = getSigned(bigDataFile);
+ loadhere.sprites[i].yhot = getSigned(bigDataFile);
+ break;
+
+ default:
+ picwidth = (byte) fgetc(bigDataFile);
+ picheight = (byte) fgetc(bigDataFile);
+ loadhere.sprites[i].xhot = fgetc(bigDataFile);
+ loadhere.sprites[i].yhot = fgetc(bigDataFile);
+ break;
+ }
+
+ if (((picwidth > 511) && (totalwidth[numTextures] + picwidth < 2047)) || ((picwidth < 511) && (totalwidth[numTextures] + picwidth < 511))) {
+ loadhere.sprites[i].tex_x = totalwidth[numTextures];
+ totalwidth[numTextures] += (loadhere.sprites[i].width = picwidth) + 1;
+ if ((loadhere.sprites[i].height = picheight) + 2 > maxheight[numTextures]) maxheight[numTextures] = picheight + 2;
+ } else {
+ numTextures++;
+ if (numTextures > 255) return fatal("Can't open sprite bank / font - it's too big.");
+ loadhere.sprites[i].tex_x = 0;
+ totalwidth[numTextures] = (loadhere.sprites[i].width = picwidth);
+ maxheight[numTextures] = loadhere.sprites[i].height = picheight;
+ }
+ loadhere.sprites[i].texNum = numTextures;
+
+ if (spriteBankVersion < 3) {
+ data = (byte *) new byte [picwidth * (picheight + 1)];
+ if (! checkNew(data)) return false;
+ int ooo = picwidth * picheight;
+ for (int tt = 0; tt < picwidth; tt ++) {
+ data[ooo ++] = 0;
+ }
+ spriteData[i] = data;
+ switch (spriteBankVersion) {
+ case 2: { // RUN LENGTH COMPRESSED DATA
+ unsigned size = picwidth * picheight;
+ unsigned pip = 0;
+
+ while (pip < size) {
+ byte col = fgetc(bigDataFile);
+ int looper;
+
+ if (col > howmany) {
+ col -= howmany + 1;
+ looper = fgetc(bigDataFile) + 1;
+ } else looper = 1;
+
+ while (looper --) {
+ data[pip ++] = col;
+ }
+ }
+ }
+ break;
+
+ default: // RAW DATA
+ size_t bytes_read = fread(data, picwidth, picheight, bigDataFile);
+ if (bytes_read != picwidth * picheight && ferror(bigDataFile)) {
+ debugOut("Reading error in loadSpriteBank.\n");
+ }
+ break;
+ }
+ }
+ }
+ numTextures++;
+
+
+ if (! spriteBankVersion) {
+ howmany = fgetc(bigDataFile);
+ startIndex = fgetc(bigDataFile);
+ }
+
+ if (spriteBankVersion < 3) {
+ if (! reserveSpritePal(loadhere.myPalette, howmany + startIndex)) return false;
+
+ for (i = 0; i < howmany; i ++) {
+ loadhere.myPalette.r[i + startIndex] = (byte) fgetc(bigDataFile);
+ loadhere.myPalette.g[i + startIndex] = (byte) fgetc(bigDataFile);
+ loadhere.myPalette.b[i + startIndex] = (byte) fgetc(bigDataFile);
+ loadhere.myPalette.pal[i + startIndex] = makeColour(loadhere.myPalette.r[i + startIndex], loadhere.myPalette.g[i + startIndex], loadhere.myPalette.b[i + startIndex]);
+ }
+ }
+
+ loadhere.myPalette.originalRed = loadhere.myPalette.originalGreen = loadhere.myPalette.originalBlue = 255;
+
+ loadhere.myPalette.numTextures = numTextures;
+ GLubyte *tmp[numTextures];
+ GLubyte *tmp2[numTextures];
+ for (tex_num = 0; tex_num < numTextures; tex_num++) {
+ if (! NPOT_textures) {
+ totalwidth[tex_num] = getNextPOT(totalwidth[tex_num]);
+ maxheight[tex_num] = getNextPOT(maxheight[tex_num]);
+ }
+ tmp[tex_num] = new GLubyte [(maxheight[tex_num] + 1)*totalwidth[tex_num] * 4];
+ if (! checkNew(tmp[tex_num])) return false;
+ memset(tmp[tex_num], 0, maxheight[tex_num]*totalwidth[tex_num] * 4);
+ if (isFont) {
+ tmp2[tex_num] = new GLubyte [(maxheight[tex_num] + 1)*totalwidth[tex_num] * 4];
+ if (! checkNew(tmp2[tex_num])) return false;
+ memset(tmp2[tex_num], 0, maxheight[tex_num]*totalwidth[tex_num] * 4);
+ }
+ loadhere.myPalette.tex_w[tex_num] = totalwidth[tex_num];
+ loadhere.myPalette.tex_h[tex_num] = maxheight[tex_num];
+ }
+
+ int fromhere;
+ unsigned char s;
+
+ for (i = 0; i < total; i ++) {
+ fromhere = 0;
+
+ int transColour = -1;
+ if (spriteBankVersion < 3) {
+ int size = loadhere.sprites[i].height * loadhere.sprites[i].width;
+ while (fromhere < size) {
+ s = spriteData[i][fromhere++];
+ if (s) {
+ transColour = s;
+ break;
+ }
+ }
+ fromhere = 0;
+ }
+
+ for (int y = 1; y < 1 + loadhere.sprites[i].height; y ++) {
+ for (int x = loadhere.sprites[i].tex_x; x < loadhere.sprites[i].tex_x + loadhere.sprites[i].width; x ++) {
+ GLubyte *target = tmp[loadhere.sprites[i].texNum] + 4 * totalwidth[loadhere.sprites[i].texNum] * y + x * 4;
+ if (spriteBankVersion < 3) {
+ s = spriteData[i][fromhere++];
+ if (s) {
+ target[0] = (GLubyte) loadhere.myPalette.r[s];
+ target[1] = (GLubyte) loadhere.myPalette.g[s];
+ target[2] = (GLubyte) loadhere.myPalette.b[s];
+ target[3] = (GLubyte) 255;
+ transColour = s;
+ } else if (transColour >= 0) {
+ target[0] = (GLubyte) loadhere.myPalette.r[transColour];
+ target[1] = (GLubyte) loadhere.myPalette.g[transColour];
+ target[2] = (GLubyte) loadhere.myPalette.b[transColour];
+ target[3] = (GLubyte) 0;
+ }
+ if (isFont) {
+ target = tmp2[loadhere.sprites[i].texNum] + 4 * totalwidth[loadhere.sprites[i].texNum] * y + x * 4;
+ target[0] = (GLubyte) 255;
+ target[1] = (GLubyte) 255;
+ target[2] = (GLubyte) 255;
+ if (s)
+ target[3] = (GLubyte) loadhere.myPalette.r[s];
+ /*else
+ target[3] = (GLubyte) 0;*/
+ }
+ } else {
+ target[0] = (GLubyte) spriteData[i][fromhere++];
+ target[1] = (GLubyte) spriteData[i][fromhere++];
+ target[2] = (GLubyte) spriteData[i][fromhere++];
+ target[3] = (GLubyte) spriteData[i][fromhere++];
+ }
+ }
+ }
+ delete[] spriteData[i];
+ }
+ delete[] spriteData;
+ spriteData = NULL;
+
+#if 0
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ glGenTextures(numTextures, loadhere.myPalette.tex_names);
+ if (isFont)
+ glGenTextures(numTextures, loadhere.myPalette.burnTex_names);
+
+#endif
+
+ for (tex_num = 0; tex_num < numTextures; tex_num++) {
+
+ glBindTexture(GL_TEXTURE_2D, loadhere.myPalette.tex_names[tex_num]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, totalwidth[tex_num], maxheight[tex_num], 0, GL_RGBA, GL_UNSIGNED_BYTE, tmp[tex_num], loadhere.myPalette.tex_names[tex_num]);
+
+ delete[] tmp[tex_num];
+ tmp[tex_num] = NULL;
+
+ if (isFont) {
+
+ glBindTexture(GL_TEXTURE_2D, loadhere.myPalette.burnTex_names[tex_num]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, totalwidth[tex_num], maxheight[tex_num], 0, GL_RGBA, GL_UNSIGNED_BYTE, tmp2[tex_num], loadhere.myPalette.burnTex_names[tex_num]);
+ delete[] tmp2[tex_num];
+ tmp2[tex_num] = NULL;
+ }
+ }
+
+ finishAccess();
+#endif
+ setResourceForFatal(-1);
+
+ return true;
+}
+
+void pasteSpriteToBackDrop(int x1, int y1, sprite &single, const spritePalette &fontPal) {
+#if 0
+ float tx1 = (float)(single.tex_x) / fontPal.tex_w[single.texNum];
+ float ty1 = 0.0;
+ float tx2 = (float)(single.tex_x + single.width) / fontPal.tex_w[single.texNum];
+ float ty2 = (float)(single.height) / fontPal.tex_h[single.texNum];
+
+ float btx1;
+ float btx2;
+ float bty1;
+ float bty2;
+
+ int diffX = single.width;
+ int diffY = single.height;
+
+ x1 -= single.xhot;
+ y1 -= single.yhot;
+
+ if (! NPOT_textures) {
+ btx1 = backdropTexW * x1 / sceneWidth;
+ btx2 = backdropTexW * (x1 + single.width) / sceneWidth;
+ bty1 = backdropTexH * y1 / sceneHeight;
+ bty2 = backdropTexH * (y1 + single.height) / sceneHeight;
+ } else {
+ btx1 = (float) x1 / sceneWidth;
+ btx2 = (float)(x1 + single.width) / sceneWidth;
+ bty1 = (float) y1 / sceneHeight;
+ bty2 = (float)(y1 + single.height) / sceneHeight;
+ }
+
+ const GLfloat btexCoords[] = {
+ btx1, bty1,
+ btx2, bty1,
+ btx1, bty2,
+ btx2, bty2
+ };
+
+ if (x1 < 0) diffX += x1;
+ if (y1 < 0) diffY += y1;
+ if (x1 + diffX > sceneWidth) diffX = sceneWidth - x1;
+ if (y1 + diffY > sceneHeight) diffY = sceneHeight - y1;
+ if (diffX < 0) return;
+ if (diffY < 0) return;
+
+ setPixelCoords(true);
+
+ int xoffset = 0;
+ while (xoffset < diffX) {
+ int w = (diffX - xoffset < viewportWidth) ? diffX - xoffset : viewportWidth;
+
+ int yoffset = 0;
+ while (yoffset < diffY) {
+ int h = (diffY - yoffset < viewportHeight) ? diffY - yoffset : viewportHeight;
+
+
+ // Render the sprite to the backdrop
+ // (using mulitexturing, so the backdrop is seen where alpha < 1.0)
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glActiveTexture(GL_TEXTURE0);
+
+ glUseProgram(shader.paste);
+ GLint uniform = glGetUniformLocation(shader.paste, "useLightTexture");
+ if (uniform >= 0) glUniform1i(uniform, 0); // No lighting
+
+ setPMVMatrix(shader.paste);
+
+ setPrimaryColor(fontPal.originalRed / 255.f, fontPal.originalGreen / 255.f, fontPal.originalBlue / 255.f, 1.0f);
+ glBindTexture(GL_TEXTURE_2D, fontPal.tex_names[single.texNum]);
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ const GLfloat vertices[] = {
+ (GLfloat) - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat)single.width - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat) - xoffset, (GLfloat)single.height - yoffset, 0.,
+ (GLfloat)single.width - xoffset, (GLfloat)single.height - yoffset, 0.
+ };
+
+ const GLfloat texCoords[] = {
+ tx1, ty1,
+ tx2, ty1,
+ tx1, ty2,
+ tx2, ty2
+ };
+
+ drawQuad(shader.paste, vertices, 3, texCoords, NULL, btexCoords);
+
+ // Copy Our ViewPort To The Texture
+ glUseProgram(0);
+
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, (int)((x1 < 0) ? xoffset : x1 + xoffset), (int)((y1 < 0) ? yoffset : y1 + yoffset), (int)((x1 < 0) ? viewportOffsetX - x1 : viewportOffsetX), (int)((y1 < 0) ? viewportOffsetY - y1 : viewportOffsetY), w, h, backdropTextureName);
+
+ yoffset += viewportHeight;
+ }
+ xoffset += viewportWidth;
+ }
+#endif
+ setPixelCoords(false);
+}
+
+void burnSpriteToBackDrop(int x1, int y1, sprite &single, const spritePalette &fontPal) {
+#if 0
+ float tx1 = (float)(single.tex_x - 0.5) / fontPal.tex_w[single.texNum];
+ float ty1 = 0.0;
+ float tx2 = (float)(single.tex_x + single.width + 0.5) / fontPal.tex_w[single.texNum];
+ float ty2 = (float)(single.height + 2) / fontPal.tex_h[single.texNum];
+
+ const GLfloat spriteTexCoords[] = {
+ tx1, ty1,
+ tx2, ty1,
+ tx1, ty2,
+ tx2, ty2
+ };
+
+ x1 -= single.xhot;
+ y1 -= single.yhot - 1;
+
+ float bx1 = (float)x1 * backdropTexW / sceneWidth;
+ float by1 = (float)y1 * backdropTexH / sceneHeight;
+ float bx2 = (float)(x1 + single.width - 1) * backdropTexW / sceneWidth;
+ float by2 = (float)(y1 + single.height - 1) * backdropTexH / sceneHeight;
+
+ const GLfloat backdropTexCoords[] = {
+ bx1, by1,
+ bx2, by1,
+ bx1, by2,
+ bx2, by2
+ };
+
+ const GLfloat backdropTexCoords2[] = {
+ 0.0f, 0.0f,
+ backdropTexW, 0.0f,
+ 0.0f, backdropTexH,
+ backdropTexW, backdropTexH
+ };
+
+ int diffX = single.width + 1;
+ int diffY = single.height + 2;
+
+ if (x1 < 0) diffX += x1;
+ if (y1 < 0) diffY += y1;
+ if (x1 + diffX > sceneWidth) diffX = sceneWidth - x1;
+ if (y1 + diffY > sceneHeight) diffY = sceneHeight - y1;
+ if (diffX < 0) return;
+ if (diffY < 0) return;
+
+ setPixelCoords(true);
+ setPrimaryColor(currentBurnR / 255.f, currentBurnG / 255.f, currentBurnB / 255.f, 1.0f);
+
+ GLfloat xoffset = 0.0f;
+ while (xoffset < diffX) {
+ int w = (diffX - xoffset < viewportWidth) ? diffX - xoffset : viewportWidth;
+
+ GLfloat yoffset = 0.0f;
+ while (yoffset < diffY) {
+ int h = (diffY - yoffset < viewportHeight) ? diffY - yoffset : viewportHeight;
+
+ const GLfloat backdropVertices[] = {
+ -x1 - xoffset, -y1 + yoffset, 0.0f,
+ sceneWidth - 1.0f - x1 - xoffset, -y1 + yoffset, 0.0f,
+ -x1 - xoffset, sceneHeight - 1.0f - y1 + yoffset, 0.0f,
+ sceneWidth - 1.0f - x1 - xoffset, sceneHeight - 1.0f - y1 + yoffset, 0.0f
+ };
+
+ const GLfloat spriteVertices[] = {
+ -xoffset, -yoffset, 0.0f,
+ single.width - 1 - xoffset, -yoffset, 0.0f,
+ -xoffset, single.height - 1 - yoffset, 0.0f,
+ single.width - 1 - xoffset, single.height - 1 - yoffset, 0.0f
+ };
+
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glUseProgram(shader.texture);
+ setPMVMatrix(shader.texture);
+
+ drawQuad(shader.texture, backdropVertices, 1, backdropTexCoords2);
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glActiveTexture(GL_TEXTURE0);
+
+ glUseProgram(shader.paste);
+ GLint uniform = glGetUniformLocation(shader.paste, "useLightTexture");
+ if (uniform >= 0) glUniform1i(uniform, 0); // No lighting
+
+ setPMVMatrix(shader.paste);
+
+ glBindTexture(GL_TEXTURE_2D, fontPal.burnTex_names[single.texNum]);
+
+//FIXME: Test this some more. Also pasting the backdrop again is not strictly necessary but allows using the paste shader.
+ drawQuad(shader.paste, spriteVertices, 3, spriteTexCoords, NULL, backdropTexCoords);
+ glUseProgram(0);
+
+ // Copy Our ViewPort To The Texture
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, (x1 < 0) ? xoffset : x1 + xoffset, (y1 < 0) ? yoffset : y1 + yoffset, viewportOffsetX, viewportOffsetY, w, h, backdropTextureName);
+
+ yoffset += viewportHeight;
+ }
+ xoffset += viewportWidth;
+ }
+ setPixelCoords(false);
+#endif
+}
+
+#if 0
+extern GLuint backdropTextureName;
+#endif
+
+void fontSprite(bool flip, int x, int y, sprite &single, const spritePalette &fontPal) {
+
+ float tx1 = (float)(single.tex_x - 0.5) / fontPal.tex_w[single.texNum];
+ float ty1 = 0.0;
+ float tx2 = (float)(single.tex_x + single.width + (flip ? 1.0 : 0.5)) / fontPal.tex_w[single.texNum];
+ float ty2 = (float)(single.height + 2) / fontPal.tex_h[single.texNum];
+
+ float x1 = (float)x - (float)single.xhot / cameraZoom;
+ float y1 = (float)y - (float)single.yhot / cameraZoom;
+ float x2 = x1 + (float)single.width / cameraZoom;
+ float y2 = y1 + (float)single.height / cameraZoom;
+
+#if 0
+ GLfloat vertices[] = {
+ x1, y1, 0.0f,
+ x2, y1, 0.0f,
+ x1, y2, 0.0f,
+ x2, y2, 0.0f
+ };
+ if (flip) {
+ vertices[0] = x2;
+ vertices[3] = x1;
+ vertices[6] = x2;
+ vertices[9] = x1;
+ }
+
+ const GLfloat texCoords[] = {
+ tx1, ty1,
+ tx2, ty1,
+ tx1, ty2,
+ tx2, ty2
+ };
+
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // GL_MODULATE instead of decal mixes the colours!
+ setPrimaryColor(fontPal.originalRed / 255.f, fontPal.originalGreen / 255.f, fontPal.originalBlue / 255.f, 1.0f);
+
+ glBindTexture(GL_TEXTURE_2D, fontPal.tex_names[single.texNum]);
+
+ glUseProgram(shader.smartScaler);
+ GLuint uniform = glGetUniformLocation(shader.smartScaler, "useLightTexture");
+ if (uniform >= 0) glUniform1i(uniform, 0);
+
+ setPMVMatrix(shader.smartScaler);
+
+ if (gameSettings.antiAlias == 1) {
+ glUniform1i(glGetUniformLocation(shader.smartScaler, "antialias"), 1);
+ } else {
+ glUniform1i(glGetUniformLocation(shader.smartScaler, "antialias"), 0);
+ }
+
+ glEnable(GL_BLEND);
+
+ drawQuad(shader.smartScaler, vertices, 1, texCoords);
+
+ glDisable(GL_BLEND);
+ glUseProgram(0);
+#endif
+}
+
+void fontSprite(int x, int y, sprite &single, const spritePalette &fontPal) {
+ fontSprite(false, x, y, single, fontPal);
+}
+
+void flipFontSprite(int x, int y, sprite &single, const spritePalette &fontPal) {
+ fontSprite(true, x, y, single, fontPal);
+}
+
+
+
+
+unsigned char curLight[3];
+
+void setDrawMode(onScreenPerson *thisPerson) {
+#if 0
+ if (thisPerson->colourmix) {
+ //glEnable(GL_COLOR_SUM); FIXME: replace line?
+ setSecondaryColor(curLight[0]*thisPerson->r * thisPerson->colourmix / 65025 / 255.f, curLight[1]*thisPerson->g * thisPerson->colourmix / 65025 / 255.f, curLight[2]*thisPerson->b * thisPerson->colourmix / 65025 / 255.f, 1.0f);
+ }
+
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ setPrimaryColor(curLight[0] * (255 - thisPerson->colourmix) / 65025.f, curLight[1] * (255 - thisPerson->colourmix) / 65025.f, curLight[2] * (255 - thisPerson->colourmix) / 65025.f, 1.0f - thisPerson->transparency / 255.f);
+#endif
+}
+
+#if 0
+extern GLuint backdropTextureName;
+#endif
+
+bool checkColourChange(bool reset);
+
+bool scaleSprite(sprite &single, const spritePalette &fontPal, onScreenPerson *thisPerson, bool mirror) {
+#if 0
+ float x = thisPerson->x;
+ float y = thisPerson->y;
+
+ float scale = thisPerson-> scale;
+ bool light = !(thisPerson->extra & EXTRA_NOLITE);
+
+ if (scale <= 0.05) return false;
+
+ float tx1 = (float)(single.tex_x) / fontPal.tex_w[single.texNum];
+ float ty1 = (float) 1.0 / fontPal.tex_h[single.texNum];
+ float tx2 = (float)(single.tex_x + single.width) / fontPal.tex_w[single.texNum];
+ float ty2 = (float)(single.height + 1) / fontPal.tex_h[single.texNum];
+
+ int diffX = (int)(((float)single.width) * scale);
+ int diffY = (int)(((float)single.height) * scale);
+
+ GLfloat x1, y1, x2, y2;
+
+ if (thisPerson -> extra & EXTRA_FIXTOSCREEN) {
+ x = x / cameraZoom;
+ y = y / cameraZoom;
+ if (single.xhot < 0)
+ x1 = x - (int)((mirror ? (float)(single.width - single.xhot) : (float)(single.xhot + 1)) * scale / cameraZoom);
+ else
+ x1 = x - (int)((mirror ? (float)(single.width - (single.xhot + 1)) : (float)single.xhot) * scale / cameraZoom);
+ y1 = y - (int)((single.yhot - thisPerson->floaty) * scale / cameraZoom);
+ x2 = x1 + (int)(diffX / cameraZoom);
+ y2 = y1 + (int)(diffY / cameraZoom);
+ } else {
+ x -= cameraX;
+ y -= cameraY;
+ if (single.xhot < 0)
+ x1 = x - (int)((mirror ? (float)(single.width - single.xhot) : (float)(single.xhot + 1)) * scale);
+ else
+ x1 = x - (int)((mirror ? (float)(single.width - (single.xhot + 1)) : (float)single.xhot) * scale);
+ y1 = y - (int)((single.yhot - thisPerson->floaty) * scale);
+ x2 = x1 + diffX;
+ y2 = y1 + diffY;
+ }
+
+ GLfloat z;
+
+ if ((!(thisPerson->extra & EXTRA_NOZB)) && zBuffer.numPanels) {
+ int i;
+ for (i = 1; i < zBuffer.numPanels; i++) {
+ if (zBuffer.panel[i] >= y + cameraY) {
+ i--;
+ break;
+ }
+ }
+ z = 0.999 - (double) i * (1.0 / 128.0);
+ } else {
+ z = -0.5;
+ }
+
+ float ltx1, ltx2, lty1, lty2;
+ if (! NPOT_textures) {
+ ltx1 = lightMap.texW * (x1 + cameraX) / sceneWidth;
+ ltx2 = lightMap.texW * (x2 + cameraX) / sceneWidth;
+ lty1 = lightMap.texH * (y1 + cameraY) / sceneHeight;
+ lty2 = lightMap.texH * (y2 + cameraY) / sceneHeight;
+ } else {
+ ltx1 = (float)(x1 + cameraX) / sceneWidth;
+ ltx2 = (float)(x2 + cameraX) / sceneWidth;
+ lty1 = (float)(y1 + cameraY) / sceneHeight;
+ lty2 = (float)(y2 + cameraY) / sceneHeight;
+ }
+
+ const GLfloat ltexCoords[] = {
+ ltx1, lty1,
+ ltx2, lty1,
+ ltx1, lty2,
+ ltx2, lty2
+ };
+
+ if (light && lightMap.data) {
+ if (lightMapMode == LIGHTMAPMODE_HOTSPOT) {
+ int lx = (int)(x + cameraX);
+ int ly = (int)(y + cameraY);
+
+ if (lx < 0) lx = 0;
+ else if (lx >= sceneWidth) lx = sceneWidth - 1;
+ if (ly < 0) ly = 0;
+ else if (ly >= sceneHeight) ly = sceneHeight - 1;
+
+ GLubyte *target;
+ if (! NPOT_textures) {
+ target = lightMap.data + (ly * getNextPOT(sceneWidth) + lx) * 4;
+ } else {
+ target = lightMap.data + (ly * sceneWidth + lx) * 4;
+ }
+ curLight[0] = target[0];
+ curLight[1] = target[1];
+ curLight[2] = target[2];
+ } else if (lightMapMode == LIGHTMAPMODE_PIXEL) {
+ curLight[0] = curLight[1] = curLight[2] = 255;
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, lightMap.name);
+ glActiveTexture(GL_TEXTURE0);
+
+ }
+ } else {
+ curLight[0] = curLight[1] = curLight[2] = 255;
+ }
+#ifndef HAVE_GLES2
+ if (!(thisPerson->extra & EXTRA_RECTANGULAR))
+ checkColourChange(true);
+#endif
+ setDrawMode(thisPerson);
+
+ glBindTexture(GL_TEXTURE_2D, fontPal.tex_names[single.texNum]);
+
+ glEnable(GL_BLEND);
+
+ glUseProgram(shader.smartScaler);
+ GLuint uniform = glGetUniformLocation(shader.smartScaler, "useLightTexture");
+ if (uniform >= 0) glUniform1i(uniform, light && lightMapMode == LIGHTMAPMODE_PIXEL && lightMap.data);
+
+ setPMVMatrix(shader.smartScaler);
+
+ if (gameSettings.antiAlias == 1) {
+ glUniform1i(glGetUniformLocation(shader.smartScaler, "antialias"), 1);
+ } else {
+ glUniform1i(glGetUniformLocation(shader.smartScaler, "antialias"), 0);
+ }
+
+ const GLfloat vertices[] = {
+ x1, y1, z,
+ x2, y1, z,
+ x1, y2, z,
+ x2, y2, z
+ };
+
+ if (! mirror) {
+ GLfloat tx3 = tx1;
+ tx1 = tx2;
+ tx2 = tx3;
+ }
+ const GLfloat texCoords[] = {
+ tx2, ty1,
+ tx1, ty1,
+ tx2, ty2,
+ tx1, ty2
+ };
+
+ drawQuad(shader.smartScaler, vertices, 2, texCoords, ltexCoords);
+
+ glDisable(GL_BLEND);
+ glUseProgram(0);
+
+ if (light && lightMapMode == LIGHTMAPMODE_PIXEL) {
+ glActiveTexture(GL_TEXTURE1);
+ glActiveTexture(GL_TEXTURE0);
+ }
+
+ setSecondaryColor(0., 0., 0., 1.);
+ //glDisable(GL_COLOR_SUM); FIXME: replace line?
+
+ // Are we pointing at the sprite?
+ if (input.mouseX >= x1 && input.mouseX <= x2 && input.mouseY >= y1 && input.mouseY <= y2) {
+ if (thisPerson->extra & EXTRA_RECTANGULAR) return true;
+#ifdef HAVE_GLES2
+ return true;
+#else
+ return checkColourChange(false);
+#endif
+ }
+#endif
+ return false;
+}
+
+// Paste a scaled sprite onto the backdrop
+void fixScaleSprite(int x, int y, sprite &single, const spritePalette &fontPal, onScreenPerson *thisPerson, int camX, int camY, bool mirror) {
+#if 0
+ float scale = thisPerson-> scale;
+ bool useZB = !(thisPerson->extra & EXTRA_NOZB);
+ bool light = !(thisPerson->extra & EXTRA_NOLITE);
+
+ if (scale <= 0.05) return;
+
+ float tx1 = (float)(single.tex_x) / fontPal.tex_w[single.texNum];
+ float ty1 = (float) 1.0 / fontPal.tex_h[single.texNum]; //0.0;
+ float tx2 = (float)(single.tex_x + single.width) / fontPal.tex_w[single.texNum];
+ float ty2 = (float)(single.height + 1) / fontPal.tex_h[single.texNum];
+
+ int diffX = (int)(((float)single.width) * scale);
+ int diffY = (int)(((float)single.height) * scale);
+ int x1;
+ if (single.xhot < 0)
+ x1 = x - (int)((mirror ? (float)(single.width - single.xhot) : (float)(single.xhot + 1)) * scale);
+ else
+ x1 = x - (int)((mirror ? (float)(single.width - (single.xhot + 1)) : (float)single.xhot) * scale);
+ int y1 = y - (int)((single.yhot - thisPerson->floaty) * scale);
+
+ float spriteWidth = diffX;
+ float spriteHeight = diffY;
+ if (x1 < 0) diffX += x1;
+ if (y1 < 0) diffY += y1;
+ if (x1 + diffX > sceneWidth) diffX = sceneWidth - x1;
+ if (y1 + diffY > sceneHeight) diffY = sceneHeight - y1;
+ if (diffX < 0) return;
+ if (diffY < 0) return;
+
+ GLfloat z;
+
+
+ if (useZB && zBuffer.numPanels) {
+ int i;
+ for (i = 1; i < zBuffer.numPanels; i++) {
+ if (zBuffer.panel[i] >= y + cameraY) {
+ i--;
+ break;
+ }
+ }
+ z = 0.999 - (double) i * (1.0 / 128.0);
+ } else {
+ z = -0.5;
+ }
+
+ float ltx1, btx1;
+ float ltx2, btx2;
+ float lty1, bty1;
+ float lty2, bty2;
+ if (! NPOT_textures) {
+ ltx1 = lightMap.texW * x1 / sceneWidth;
+ ltx2 = lightMap.texW * (x1 + spriteWidth) / sceneWidth;
+ lty1 = lightMap.texH * y1 / sceneHeight;
+ lty2 = lightMap.texH * (y1 + spriteHeight) / sceneHeight;
+ btx1 = backdropTexW * x1 / sceneWidth;
+ btx2 = backdropTexW * (x1 + spriteWidth) / sceneWidth;
+ bty1 = backdropTexH * y1 / sceneHeight;
+ bty2 = backdropTexH * (y1 + spriteHeight) / sceneHeight;
+ } else {
+ btx1 = ltx1 = (float) x1 / sceneWidth;
+ btx2 = ltx2 = (float)(x1 + spriteWidth) / sceneWidth;
+ bty1 = lty1 = (float) y1 / sceneHeight;
+ bty2 = lty2 = (float)(y1 + spriteHeight) / sceneHeight;
+ }
+
+ const GLfloat ltexCoords[] = {
+ ltx1, lty1,
+ ltx2, lty1,
+ ltx1, lty2,
+ ltx2, lty2
+ };
+
+ const GLfloat btexCoords[] = {
+ btx1, bty1,
+ btx2, bty1,
+ btx1, bty2,
+ btx2, bty2
+ };
+
+ if (light && lightMap.data) {
+ if (lightMapMode == LIGHTMAPMODE_HOTSPOT) {
+ int lx = x + cameraX;
+ int ly = y + cameraY;
+ if (lx < 0 || ly < 0 || lx >= sceneWidth || ly >= sceneHeight) {
+ curLight[0] = curLight[1] = curLight[2] = 255;
+ } else {
+ GLubyte *target = lightMap.data + (ly * sceneWidth + lx) * 4;
+ curLight[0] = target[0];
+ curLight[1] = target[1];
+ curLight[2] = target[2];
+ }
+ } else if (lightMapMode == LIGHTMAPMODE_PIXEL) {
+ curLight[0] = curLight[1] = curLight[2] = 255;
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, lightMap.name);
+
+ }
+ } else {
+ curLight[0] = curLight[1] = curLight[2] = 255;
+ }
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glActiveTexture(GL_TEXTURE0);
+
+ setPixelCoords(true);
+ GLfloat xoffset = 0.0f;
+ while (xoffset < diffX) {
+ int w = (diffX - xoffset < viewportWidth) ? (int)(diffX - xoffset) : viewportWidth;
+
+ GLfloat yoffset = 0.0f;
+ while (yoffset < diffY) {
+
+ int h = (diffY - yoffset < viewportHeight) ? (int)(diffY - yoffset) : viewportHeight;
+
+ // Render the scene - first the old backdrop (so that it'll show through when the z-buffer is active
+ //glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ const GLfloat vertices[] = {
+ -x1 - xoffset, -y1 - yoffset, 0.0f,
+ sceneWidth - x1 - xoffset, -y1 - yoffset, 0.0f,
+ -x1 - xoffset, sceneHeight - y1 - yoffset, 0.0f,
+ sceneWidth - x1 - xoffset, sceneHeight - y1 - yoffset, 0.0f
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ backdropTexW, 0.0f,
+ 0.0f, backdropTexH,
+ backdropTexW, backdropTexH
+ };
+
+ glUseProgram(shader.texture);
+ setPMVMatrix(shader.texture);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+
+ // The z-buffer
+ if (useZB) {
+ glDepthMask(GL_TRUE);
+ glClear(GL_DEPTH_BUFFER_BIT);
+ drawZBuffer((int)(x1 + xoffset + camX), (int)(y1 + yoffset + camY), false);
+
+ glDepthMask(GL_FALSE);
+ glEnable(GL_DEPTH_TEST);
+ }
+
+ // Then the sprite
+ glUseProgram(shader.paste);
+ GLint uniform = glGetUniformLocation(shader.paste, "useLightTexture");
+ if (uniform >= 0) glUniform1i(uniform, light && lightMapMode == LIGHTMAPMODE_PIXEL && lightMap.data);
+ setPMVMatrix(shader.paste);
+
+ setDrawMode(thisPerson);
+
+ glBindTexture(GL_TEXTURE_2D, fontPal.tex_names[single.texNum]);
+
+ const GLfloat vertices2[] = {
+ -xoffset, -yoffset, z,
+ spriteWidth - xoffset, -yoffset, z,
+ -xoffset, spriteHeight - yoffset, z,
+ spriteWidth - xoffset, spriteHeight - yoffset, z
+ };
+
+ if (! mirror) {
+ GLfloat tx3 = tx1;
+ tx1 = tx2;
+ tx2 = tx3;
+ }
+ const GLfloat texCoords2[] = {
+ tx2, ty1,
+ tx1, ty1,
+ tx2, ty2,
+ tx1, ty2
+ };
+
+ drawQuad(shader.paste, vertices2, 3, texCoords2, ltexCoords, btexCoords);
+
+ setSecondaryColor(0., 0., 0., 1.);
+ //glDisable(GL_COLOR_SUM); FIXME: replace line?
+ // Copy Our ViewPort To The Texture
+ glUseProgram(0);
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, (int)((x1 < 0) ? xoffset : x1 + xoffset), (int)((y1 < 0) ? yoffset : y1 + yoffset), (int)((x1 < 0) ? viewportOffsetX - x1 : viewportOffsetX), (int)((y1 < 0) ? viewportOffsetY - y1 : viewportOffsetY), w, h, backdropTextureName);
+
+ yoffset += viewportHeight;
+ }
+ xoffset += viewportWidth;
+ }
+
+ setPixelCoords(false);
+ glUseProgram(0);
+#endif
+}
+
diff --git a/engines/sludge/sprites.h b/engines/sludge/sprites.h
new file mode 100644
index 0000000000..e9f053fcd5
--- /dev/null
+++ b/engines/sludge/sprites.h
@@ -0,0 +1,93 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_SPRITE_H
+#define SLUDGE_SPRITE_H
+
+#if 0
+#if !defined(HAVE_GLES2)
+#include "GLee.h"
+#else
+#include <GLES2/gl2.h>
+#endif
+#endif
+
+struct onScreenPerson;
+
+struct sprite {
+ int width, height, xhot, yhot;
+ int tex_x;
+ int texNum;
+ //unsigned char * data;
+};
+
+class spritePalette {
+public:
+ unsigned short int *pal;
+ unsigned char *r;
+ unsigned char *g;
+ unsigned char *b;
+#if 0
+ GLuint *tex_names;
+ GLuint *burnTex_names;
+#endif
+ int *tex_w, * tex_h;
+ int numTextures;
+ unsigned char originalRed, originalGreen, originalBlue, total;
+
+ spritePalette(): pal(0), r(0), g(0), b(0)/*, tex_names(0), burnTex_names(0)*/
+ , tex_w(0), tex_h(0), numTextures(0)
+ , total(0) {}
+
+ ~spritePalette() {
+ delete [] pal;
+ delete [] r;
+ delete [] g;
+ delete [] b;
+// delete [] tex_names;
+// delete [] burnTex_names;
+ delete [] tex_w;
+ delete [] tex_h;
+ }
+};
+
+struct spriteBank {
+ int total;
+ int type;
+ sprite *sprites;
+ spritePalette myPalette;
+ bool isFont;
+};
+
+void forgetSpriteBank(spriteBank &forgetme);
+bool loadSpriteBank(char *filename, spriteBank &loadhere);
+bool loadSpriteBank(int fileNum, spriteBank &loadhere, bool isFont);
+
+void fontSprite(int x1, int y1, sprite &single, const spritePalette &fontPal);
+void flipFontSprite(int x1, int y1, sprite &single, const spritePalette &fontPal);
+
+bool scaleSprite(sprite &single, const spritePalette &fontPal, onScreenPerson *thisPerson, bool mirror);
+void pasteSpriteToBackDrop(int x1, int y1, sprite &single, const spritePalette &fontPal);
+bool reserveSpritePal(spritePalette &sP, int n);
+void fixScaleSprite(int x1, int y1, sprite &single, const spritePalette &fontPal, onScreenPerson *thisPerson, const int camX, const int camY, bool);
+void burnSpriteToBackDrop(int x1, int y1, sprite &single, const spritePalette &fontPal);
+
+#endif
diff --git a/engines/sludge/statusba.cpp b/engines/sludge/statusba.cpp
new file mode 100644
index 0000000000..43674a930b
--- /dev/null
+++ b/engines/sludge/statusba.cpp
@@ -0,0 +1,229 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+
+#include "backdrop.h"
+#include "colours.h"
+#include "sprites.h"
+#include "fonttext.h"
+#include "moreio.h"
+#include "stringy.h"
+#include "newfatal.h"
+#include "statusba.h"
+
+spritePalette verbLinePalette;
+spritePalette litVerbLinePalette;
+
+statusStuff mainStatus;
+statusStuff *nowStatus = & mainStatus;
+extern int fontHeight;
+extern float cameraZoom;
+
+void setLitStatus(int i) {
+ nowStatus -> litStatus = i;
+}
+
+void killLastStatus() {
+ if (nowStatus -> firstStatusBar) {
+ statusBar *kill = nowStatus -> firstStatusBar;
+ nowStatus -> firstStatusBar = kill -> next;
+ delete kill -> text;
+ delete kill;
+ }
+}
+
+void clearStatusBar() {
+ statusBar *stat = nowStatus -> firstStatusBar;
+ statusBar *kill;
+ nowStatus -> litStatus = -1;
+ while (stat) {
+ kill = stat;
+ stat = stat -> next;
+ delete kill -> text;
+ delete kill;
+ }
+ nowStatus -> firstStatusBar = NULL;
+}
+
+void addStatusBar() {
+ statusBar *newStat = new statusBar;
+ if (checkNew(newStat)) {
+ newStat -> next = nowStatus -> firstStatusBar;
+ newStat -> text = copyString("");
+ nowStatus -> firstStatusBar = newStat;
+ }
+}
+
+void setStatusBar(char *txt) {
+ if (nowStatus -> firstStatusBar) {
+ delete nowStatus -> firstStatusBar -> text;
+ nowStatus -> firstStatusBar -> text = copyString(txt);
+ }
+}
+
+void positionStatus(int x, int y) {
+ nowStatus -> statusX = x;
+ nowStatus -> statusY = y;
+}
+
+void drawStatusBar() {
+ int y = nowStatus -> statusY, n = 0;
+ statusBar *stat = nowStatus -> firstStatusBar;
+ fixFont(litVerbLinePalette);
+ fixFont(verbLinePalette);
+ while (stat) {
+ switch (nowStatus -> alignStatus) {
+ case IN_THE_CENTRE:
+ pasteString(stat -> text, ((winWidth - stringWidth(stat -> text)) >> 1) / cameraZoom, y / cameraZoom, (n ++ == nowStatus -> litStatus) ? litVerbLinePalette : verbLinePalette);
+ break;
+
+ case 1001:
+ pasteString(stat -> text, (winWidth - stringWidth(stat -> text)) - nowStatus -> statusX / cameraZoom, y / cameraZoom, (n ++ == nowStatus -> litStatus) ? litVerbLinePalette : verbLinePalette);
+ break;
+
+ default:
+ pasteString(stat -> text, nowStatus -> statusX / cameraZoom, y / cameraZoom, (n ++ == nowStatus -> litStatus) ? litVerbLinePalette : verbLinePalette);
+ }
+ stat = stat -> next;
+ y -= fontHeight;
+ }
+}
+
+void statusBarColour(byte r, byte g, byte b) {
+ setFontColour(verbLinePalette, r, g, b);
+ nowStatus -> statusR = r;
+ nowStatus -> statusG = g;
+ nowStatus -> statusB = b;
+}
+
+void statusBarLitColour(byte r, byte g, byte b) {
+ setFontColour(litVerbLinePalette, r, g, b);
+ nowStatus -> statusLR = r;
+ nowStatus -> statusLG = g;
+ nowStatus -> statusLB = b;
+}
+
+statusStuff *copyStatusBarStuff(statusStuff *here) {
+
+ // Things we want to keep
+ here -> statusLR = nowStatus -> statusLR;
+ here -> statusLG = nowStatus -> statusLG;
+ here -> statusLB = nowStatus -> statusLB;
+ here -> statusR = nowStatus -> statusR;
+ here -> statusG = nowStatus -> statusG;
+ here -> statusB = nowStatus -> statusB;
+ here -> alignStatus = nowStatus -> alignStatus;
+ here -> statusX = nowStatus -> statusX;
+ here -> statusY = nowStatus -> statusY;
+
+ // Things we want to clear
+ here -> litStatus = -1;
+ here -> firstStatusBar = NULL;
+
+ statusStuff *old = nowStatus;
+ nowStatus = here;
+
+ return old;
+}
+
+void restoreBarStuff(statusStuff *here) {
+ delete nowStatus;
+ setFontColour(verbLinePalette, here -> statusR, here -> statusG, here -> statusB);
+ setFontColour(litVerbLinePalette, here -> statusLR, here -> statusLG, here -> statusLB);
+ nowStatus = here;
+}
+
+
+void initStatusBar() {
+ mainStatus.firstStatusBar = NULL;
+ mainStatus.alignStatus = IN_THE_CENTRE;
+ mainStatus.litStatus = -1;
+ mainStatus.statusX = 10;
+ mainStatus.statusY = winHeight - 15;
+ statusBarColour(255, 255, 255);
+ statusBarLitColour(255, 255, 128);
+}
+
+const char *statusBarText() {
+ if (nowStatus -> firstStatusBar) {
+ return nowStatus -> firstStatusBar -> text;
+ } else {
+ return "";
+ }
+}
+
+#if ALLOW_FILE
+void saveStatusBars(FILE *fp) {
+ statusBar *viewLine = nowStatus -> firstStatusBar;
+
+ put2bytes(nowStatus -> alignStatus, fp);
+ putSigned(nowStatus -> litStatus, fp);
+ put2bytes(nowStatus -> statusX, fp);
+ put2bytes(nowStatus -> statusY, fp);
+
+ fputc(nowStatus -> statusR, fp);
+ fputc(nowStatus -> statusG, fp);
+ fputc(nowStatus -> statusB, fp);
+ fputc(nowStatus -> statusLR, fp);
+ fputc(nowStatus -> statusLG, fp);
+ fputc(nowStatus -> statusLB, fp);
+
+ // Write what's being said
+ while (viewLine) {
+ fputc(1, fp);
+ writeString(viewLine -> text, fp);
+ viewLine = viewLine -> next;
+ }
+ fputc(0, fp);
+}
+
+bool loadStatusBars(FILE *fp) {
+ clearStatusBar();
+
+ nowStatus -> alignStatus = get2bytes(fp);
+ nowStatus -> litStatus = getSigned(fp);
+ nowStatus -> statusX = get2bytes(fp);
+ nowStatus -> statusY = get2bytes(fp);
+
+ nowStatus -> statusR = fgetc(fp);
+ nowStatus -> statusG = fgetc(fp);
+ nowStatus -> statusB = fgetc(fp);
+ nowStatus -> statusLR = fgetc(fp);
+ nowStatus -> statusLG = fgetc(fp);
+ nowStatus -> statusLB = fgetc(fp);
+
+ setFontColour(verbLinePalette, nowStatus -> statusR, nowStatus -> statusG, nowStatus -> statusB);
+ setFontColour(litVerbLinePalette, nowStatus -> statusLR, nowStatus -> statusLG, nowStatus -> statusLB);
+ // Read what's being said
+ statusBar * * viewLine = & (nowStatus -> firstStatusBar);
+ statusBar *newOne;
+ while (fgetc(fp)) {
+ newOne = new statusBar;
+ if (! checkNew(newOne)) return false;
+ newOne -> text = readString(fp);
+ newOne -> next = NULL;
+ (* viewLine) = newOne;
+ viewLine = & (newOne -> next);
+ }
+ return true;
+}
+#endif
diff --git a/engines/sludge/statusba.h b/engines/sludge/statusba.h
new file mode 100644
index 0000000000..faa164cab6
--- /dev/null
+++ b/engines/sludge/statusba.h
@@ -0,0 +1,62 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_STATUSBA_H
+#define SLUDGE_STATUSBA_H
+
+struct statusBar {
+ char *text;
+ statusBar *next;
+};
+
+struct statusStuff {
+ statusBar *firstStatusBar;
+ unsigned short alignStatus;
+ int litStatus;
+ int statusX, statusY;
+ int statusR, statusG, statusB;
+ int statusLR, statusLG, statusLB;
+};
+
+void initStatusBar();
+
+void setStatusBar(char *txt);
+void clearStatusBar();
+void addStatusBar();
+void killLastStatus();
+void statusBarColour(unsigned char r, unsigned char g, unsigned char b);
+void statusBarLitColour(unsigned char r, unsigned char g, unsigned char b);
+void setLitStatus(int i);
+const char *statusBarText();
+void positionStatus(int, int);
+void drawStatusBar();
+
+#if ALLOW_FILE
+// Load and save
+bool loadStatusBars(FILE *fp);
+void saveStatusBars(FILE *fp);
+#endif
+
+// For freezing
+void restoreBarStuff(statusStuff *here);
+statusStuff *copyStatusBarStuff(statusStuff *here);
+
+#endif
diff --git a/engines/sludge/stringy.cpp b/engines/sludge/stringy.cpp
new file mode 100644
index 0000000000..fdf4536da0
--- /dev/null
+++ b/engines/sludge/stringy.cpp
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+#include <string.h>
+
+#include "newfatal.h"
+
+char *copyString(const char *copyMe) {
+ char *newString = new char [strlen(copyMe) + 1];
+ if (! checkNew(newString)) return NULL;
+ strcpy(newString, copyMe);
+ return newString;
+}
+
+char *joinStrings(const char *s1, const char *s2) {
+ char *newString = new char [strlen(s1) + strlen(s2) + 1];
+ if (! checkNew(newString)) return NULL;
+ sprintf(newString, "%s%s", s1, s2);
+ return newString;
+}
diff --git a/engines/sludge/stringy.h b/engines/sludge/stringy.h
new file mode 100644
index 0000000000..2306567699
--- /dev/null
+++ b/engines/sludge/stringy.h
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_STRINGY_H
+#define SLUDGE_STRINGY_H
+
+char *copyString(const char *copyMe);
+char *joinStrings(const char *s1, const char *s2);
+
+#endif
diff --git a/engines/sludge/talk.cpp b/engines/sludge/talk.cpp
new file mode 100644
index 0000000000..2803e2b881
--- /dev/null
+++ b/engines/sludge/talk.cpp
@@ -0,0 +1,274 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+
+#include <string.h>
+
+#include "backdrop.h"
+#include "sprites.h"
+#include "sludger.h"
+#include "objtypes.h"
+#include "region.h"
+#include "sprbanks.h"
+#include "people.h"
+#include "talk.h"
+#include "sound.h"
+#include "fonttext.h"
+#include "newfatal.h"
+#include "stringy.h"
+#include "moreio.h"
+
+extern int fontHeight, cameraX, cameraY, speechMode;
+extern float cameraZoom;
+speechStruct *speech;
+float speechSpeed = 1;
+
+void initSpeech() {
+ speech = new speechStruct;
+ if (checkNew(speech)) {
+ speech -> currentTalker = NULL;
+ speech -> allSpeech = NULL;
+ speech -> speechY = 0;
+ speech -> lastFile = -1;
+ }
+}
+
+void killAllSpeech() {
+ if (speech -> lastFile != -1) {
+#if 0
+ huntKillSound(speech -> lastFile);
+#endif
+ speech -> lastFile = -1;
+ }
+
+ if (speech -> currentTalker) {
+ makeSilent(* (speech -> currentTalker));
+ speech -> currentTalker = NULL;
+ }
+
+ speechLine *killMe;
+
+ while (speech -> allSpeech) {
+ killMe = speech -> allSpeech;
+ speech -> allSpeech = speech -> allSpeech -> next;
+ delete killMe -> textLine;
+ delete killMe;
+ }
+}
+
+#define TF_max(a, b) ((a > b) ? a : b)
+#define TF_min(a, b) ((a > b) ? b : a)
+
+inline void setObjFontColour(objectType *t) {
+#if 0
+ setFontColour(speech -> talkCol, t -> r, t -> g, t -> b);
+#endif
+}
+
+void addSpeechLine(char *theLine, int x, int &offset) {
+ int halfWidth = (stringWidth(theLine) >> 1) / cameraZoom;
+ int xx1 = x - (halfWidth);
+ int xx2 = x + (halfWidth);
+ speechLine *newLine = new speechLine;
+ checkNew(newLine);
+
+ newLine -> next = speech -> allSpeech;
+ newLine -> textLine = copyString(theLine);
+ newLine -> x = xx1;
+ speech -> allSpeech = newLine;
+ if ((xx1 < 5) && (offset < (5 - xx1))) {
+ offset = 5 - xx1;
+ } else if ((xx2 >= ((float)winWidth / cameraZoom) - 5) && (offset > (((float)winWidth / cameraZoom) - 5 - xx2))) {
+ offset = ((float)winWidth / cameraZoom) - 5 - xx2;
+ }
+}
+
+int isThereAnySpeechGoingOn() {
+ return speech -> allSpeech ? speech -> lookWhosTalking : -1;
+}
+
+int wrapSpeechXY(char *theText, int x, int y, int wrap, int sampleFile) {
+ int a, offset = 0;
+
+ killAllSpeech();
+
+ int speechTime = (strlen(theText) + 20) * speechSpeed;
+ if (speechTime < 1) speechTime = 1;
+ if (sampleFile != -1) {
+ if (speechMode >= 1) {
+#if 0
+ if (startSound(sampleFile, false)) {
+ speechTime = -10;
+ speech -> lastFile = sampleFile;
+ if (speechMode == 2) return -10;
+ }
+#endif
+ }
+ }
+ speech -> speechY = y;
+
+ while (strlen(theText) > wrap) {
+ a = wrap;
+ while (theText[a] != ' ') {
+ a --;
+ if (a == 0) {
+ a = wrap;
+ break;
+ }
+ }
+ theText[a] = 0;
+ addSpeechLine(theText, x, offset);
+ theText[a] = ' ';
+ theText += a + 1;
+ y -= fontHeight / cameraZoom;
+ }
+ addSpeechLine(theText, x, offset);
+ y -= fontHeight / cameraZoom;
+
+ if (y < 0) speech -> speechY -= y;
+ else if (speech -> speechY > cameraY + (float)(winHeight - fontHeight / 3) / cameraZoom) speech -> speechY = cameraY + (float)(winHeight - fontHeight / 3) / cameraZoom;
+
+ if (offset) {
+ speechLine *viewLine = speech -> allSpeech;
+ while (viewLine) {
+ viewLine -> x += offset;
+ viewLine = viewLine -> next;
+ }
+ }
+ return speechTime;
+}
+
+int wrapSpeechPerson(char *theText, onScreenPerson &thePerson, int sampleFile, bool animPerson) {
+ int i = wrapSpeechXY(theText, thePerson.x - cameraX, thePerson.y - cameraY - (thePerson.scale * (thePerson.height - thePerson.floaty)) - thePerson.thisType -> speechGap, thePerson.thisType -> wrapSpeech, sampleFile);
+ if (animPerson) {
+ makeTalker(thePerson);
+ speech -> currentTalker = & thePerson;
+ }
+ return i;
+}
+
+int wrapSpeech(char *theText, int objT, int sampleFile, bool animPerson) {
+ int i;
+
+ speech -> lookWhosTalking = objT;
+ onScreenPerson *thisPerson = findPerson(objT);
+ if (thisPerson) {
+ setObjFontColour(thisPerson -> thisType);
+ i = wrapSpeechPerson(theText, * thisPerson, sampleFile, animPerson);
+ } else {
+ screenRegion *thisRegion = getRegionForObject(objT);
+ if (thisRegion) {
+ setObjFontColour(thisRegion -> thisType);
+ i = wrapSpeechXY(theText, ((thisRegion -> x1 + thisRegion -> x2) >> 1) - cameraX, thisRegion -> y1 - thisRegion -> thisType -> speechGap - cameraY, thisRegion -> thisType -> wrapSpeech, sampleFile);
+ } else {
+ objectType *temp = findObjectType(objT);
+ setObjFontColour(temp);
+ i = wrapSpeechXY(theText, winWidth >> 1, 10, temp -> wrapSpeech, sampleFile);
+ }
+ }
+ return i;
+}
+
+void viewSpeech() {
+#if 0
+ int viewY = speech -> speechY;
+ speechLine *viewLine = speech -> allSpeech;
+ fixFont(speech -> talkCol);
+ while (viewLine) {
+ pasteString(viewLine -> textLine, viewLine -> x, viewY, speech -> talkCol);
+ viewY -= fontHeight / cameraZoom;
+ viewLine = viewLine -> next;
+ }
+#endif
+}
+
+#if ALLOW_FILE
+void saveSpeech(speechStruct *sS, FILE *fp) {
+ speechLine *viewLine = sS -> allSpeech;
+
+ fputc(sS -> talkCol.originalRed, fp);
+ fputc(sS -> talkCol.originalGreen, fp);
+ fputc(sS -> talkCol.originalBlue, fp);
+
+ putFloat(speechSpeed, fp);
+
+ // Write y co-ordinate
+ put2bytes(sS -> speechY, fp);
+
+ // Write which character's talking
+ put2bytes(sS -> lookWhosTalking, fp);
+ if (sS -> currentTalker) {
+ fputc(1, fp);
+ put2bytes(sS -> currentTalker -> thisType -> objectNum, fp);
+ } else {
+ fputc(0, fp);
+ }
+
+ // Write what's being said
+ while (viewLine) {
+ fputc(1, fp);
+ writeString(viewLine -> textLine, fp);
+ put2bytes(viewLine -> x, fp);
+ viewLine = viewLine -> next;
+ }
+ fputc(0, fp);
+}
+
+bool loadSpeech(speechStruct *sS, FILE *fp) {
+ speech -> currentTalker = NULL;
+ killAllSpeech();
+ byte r = fgetc(fp);
+ byte g = fgetc(fp);
+ byte b = fgetc(fp);
+ setFontColour(sS -> talkCol, r, g, b);
+
+ speechSpeed = getFloat(fp);
+
+ // Read y co-ordinate
+ sS -> speechY = get2bytes(fp);
+
+ // Read which character's talking
+ sS -> lookWhosTalking = get2bytes(fp);
+
+ if (fgetc(fp)) {
+ sS -> currentTalker = findPerson(get2bytes(fp));
+ } else {
+ sS -> currentTalker = NULL;
+ }
+
+ // Read what's being said
+ speechLine * * viewLine = & sS -> allSpeech;
+ speechLine *newOne;
+ speech -> lastFile = -1;
+ while (fgetc(fp)) {
+ newOne = new speechLine;
+ if (! checkNew(newOne)) return false;
+ newOne -> textLine = readString(fp);
+ newOne -> x = get2bytes(fp);
+ newOne -> next = NULL;
+ (* viewLine) = newOne;
+ viewLine = & (newOne -> next);
+ }
+
+ return true;
+}
+#endif
diff --git a/engines/sludge/talk.h b/engines/sludge/talk.h
new file mode 100644
index 0000000000..ba9bd7ffda
--- /dev/null
+++ b/engines/sludge/talk.h
@@ -0,0 +1,52 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_TALK_H
+#define SLUDGE_TALK_H
+
+#include "sprites.h"
+
+struct speechLine {
+ char *textLine;
+ speechLine *next;
+ int x;
+};
+
+
+struct speechStruct {
+ onScreenPerson *currentTalker;
+ speechLine *allSpeech;
+ int speechY, lastFile, lookWhosTalking;
+#if 0
+ spritePalette talkCol;
+#endif
+};
+
+int wrapSpeech(char *theText, int objT, int sampleFile, bool);
+void viewSpeech();
+void killAllSpeech();
+int isThereAnySpeechGoingOn();
+void initSpeech();
+#if ALLOW_FILE
+void saveSpeech(speechStruct *sS, FILE *fp);
+bool loadSpeech(speechStruct *sS, FILE *fp);
+#endif
+#endif
diff --git a/engines/sludge/texture.frag b/engines/sludge/texture.frag
new file mode 100644
index 0000000000..3de427ba8d
--- /dev/null
+++ b/engines/sludge/texture.frag
@@ -0,0 +1,22 @@
+uniform sampler2D sampler2d;
+uniform bool zBuffer;
+uniform float zBufferLayer;
+uniform bool modulateColor;
+
+varying vec2 varCoord;
+varying vec4 color;
+
+void main(void)
+{
+ vec4 col = texture2D(sampler2d, varCoord);
+ if (zBuffer && col.a < 0.0625*zBufferLayer-0.03)
+ {
+ discard;
+ }
+ if (modulateColor)
+ {
+ col = col * color;
+ }
+ gl_FragColor = col;
+}
+
diff --git a/engines/sludge/texture.vert b/engines/sludge/texture.vert
new file mode 100644
index 0000000000..40099af68f
--- /dev/null
+++ b/engines/sludge/texture.vert
@@ -0,0 +1,16 @@
+attribute vec4 myVertex;
+attribute vec2 myUV0;
+
+uniform mat4 myPMVMatrix;
+uniform vec4 myColor;
+
+varying vec2 varCoord;
+varying vec4 color;
+
+void main(void)
+{
+ gl_Position = myPMVMatrix * myVertex;
+ varCoord = myUV0.st;
+ color = myColor;
+}
+
diff --git a/engines/sludge/thumbnail.cpp b/engines/sludge/thumbnail.cpp
new file mode 100644
index 0000000000..53ab7ecfb9
--- /dev/null
+++ b/engines/sludge/thumbnail.cpp
@@ -0,0 +1,266 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdint.h>
+
+#include "allfiles.h"
+#include "errors.h"
+#include "moreio.h"
+#include "CommonCode/version.h"
+#include "sludger.h"
+#include "colours.h"
+#include "backdrop.h"
+#include "graphics.h"
+#include "newfatal.h"
+
+bool freeze();
+void unfreeze(bool); // Because FREEZE.H needs a load of other includes
+
+int thumbWidth = 0, thumbHeight = 0;
+
+#if 0
+extern GLuint backdropTextureName;
+#endif
+
+#if ALLOW_FILE
+bool saveThumbnail(FILE *fp) {
+ GLuint thumbnailTextureName = 0;
+
+ put4bytes(thumbWidth, fp);
+ put4bytes(thumbHeight, fp);
+
+ if (thumbWidth && thumbHeight) {
+ if (! freeze()) return false;
+
+ setPixelCoords(true);
+#if 0
+ glUseProgram(shader.texture);
+
+ setPMVMatrix(shader.texture);
+
+ glGenTextures(1, &thumbnailTextureName);
+ glBindTexture(GL_TEXTURE_2D, thumbnailTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, thumbWidth, thumbHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0, thumbnailTextureName);
+
+ // Render the backdrop (scaled)
+ //glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glBindTexture(GL_TEXTURE_2D, backdropTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+
+ const GLfloat vertices[] = {
+ 0., 0., 0.,
+ thumbWidth - 1.f, 0., 0.,
+ 0., thumbHeight - 1.f, 0.,
+ thumbWidth - 1.f, thumbHeight - 1.f, 0.
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ backdropTexW, 0.0f,
+ 0.0f, backdropTexH,
+ backdropTexW, backdropTexH
+ };
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+
+ if (gameSettings.antiAlias < 0) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+
+ deleteTextures(1, &thumbnailTextureName);
+ thumbnailTextureName = 0;
+
+ // Save Our ViewPort
+#ifdef HAVE_GLES2
+ GLushort *image = new GLushort [thumbWidth * thumbHeight];
+ GLuint *tmp = new GLuint [thumbWidth * thumbHeight];
+ if (! checkNew(image)) return false;
+ glReadPixels(viewportOffsetX, viewportOffsetY, thumbWidth, thumbHeight, GL_RGBA, GL_UNSIGNED_BYTE, tmp);
+ for (int y = 0; y < thumbHeight; y ++) {
+ for (int x = 0; x < thumbWidth; x ++) {
+ const GLuint a = tmp[y * thumbWidth + x];
+ image[y * thumbWidth + x] = ((a & 0x00f80000) >> (16 + 3)) | ((a & 0x0000fc00) >> (8 + 2 - 5)) | ((a & 0x000000f8) << (11 - 3));
+ }
+ }
+ delete[] tmp;
+#else
+ GLushort *image = new GLushort [thumbWidth * thumbHeight];
+ if (! checkNew(image)) return false;
+
+ glReadPixels(viewportOffsetX, viewportOffsetY, thumbWidth, thumbHeight, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, image);
+#endif
+
+
+
+ glUseProgram(0);
+#endif
+ setPixelCoords(false);
+
+ for (int y = 0; y < thumbHeight; y ++) {
+ for (int x = 0; x < thumbWidth; x ++) {
+ put2bytes((*(image + y * thumbWidth + x)), fp);
+ }
+ }
+ delete[] image;
+ image = NULL;
+ unfreeze(true);
+ }
+ fputc('!', fp);
+ return true;
+}
+#endif
+
+void showThumbnail(char *filename, int atX, int atY) {
+#if 0
+ GLubyte *thumbnailTexture = NULL;
+ GLuint thumbnailTextureName = 0;
+
+ GLfloat texCoordW = 1.0;
+ GLfloat texCoordH = 1.0;
+
+ int ssgVersion;
+ FILE *fp = openAndVerify(filename, 'S', 'A', ERROR_GAME_LOAD_NO, ssgVersion);
+ if (ssgVersion >= VERSION(1, 4)) {
+ if (fp == NULL) return;
+ int fileWidth = get4bytes(fp);
+ int fileHeight = get4bytes(fp);
+
+ int picWidth = fileWidth;
+ int picHeight = fileHeight;
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(picWidth);
+ picHeight = getNextPOT(picHeight);
+ texCoordW = ((double)fileWidth) / picWidth;
+ texCoordH = ((double)fileHeight) / picHeight;
+
+ }
+
+ thumbnailTexture = new GLubyte [picHeight * picWidth * 4];
+ if (thumbnailTexture == NULL) return;
+
+ int t1, t2;
+ unsigned short c;
+ GLubyte *target;
+ for (t2 = 0; t2 < fileHeight; t2 ++) {
+ t1 = 0;
+ while (t1 < fileWidth) {
+ c = (unsigned short) get2bytes(fp);
+ target = thumbnailTexture + 4 * picWidth * t2 + t1 * 4;
+ target[0] = (GLubyte) redValue(c);
+ target[1] = (GLubyte) greenValue(c);
+ target[2] = (GLubyte) blueValue(c);
+ target[3] = (GLubyte) 255;
+ t1++;
+ }
+ }
+
+ fclose(fp);
+
+ glGenTextures(1, &thumbnailTextureName);
+ glBindTexture(GL_TEXTURE_2D, thumbnailTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, thumbnailTexture, thumbnailTextureName);
+
+ delete thumbnailTexture;
+ thumbnailTexture = NULL;
+
+ if (atX < 0) {
+ fileWidth += atX;
+ atX = 0;
+ }
+ if (atY < 0) {
+ fileHeight += atY;
+ atY = 0;
+ }
+ if (fileWidth + atX > sceneWidth) fileWidth = sceneWidth - atX;
+ if (fileHeight + atY > sceneHeight) fileHeight = sceneHeight - atY;
+
+ setPixelCoords(true);
+
+ glUseProgram(shader.texture);
+ setPMVMatrix(shader.texture);
+
+ int xoffset = 0;
+ while (xoffset < fileWidth) {
+ int w = (fileWidth - xoffset < viewportWidth) ? fileWidth - xoffset : viewportWidth;
+
+ int yoffset = 0;
+ while (yoffset < fileHeight) {
+ int h = (fileHeight - yoffset < viewportHeight) ? fileHeight - yoffset : viewportHeight;
+ glBindTexture(GL_TEXTURE_2D, thumbnailTextureName);
+ const GLfloat vertices[] = {
+ (GLfloat)fileWidth - 1.f - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat) - xoffset, (GLfloat) - yoffset, 0.,
+ (GLfloat)fileWidth - 1.f - xoffset, (GLfloat)fileHeight - 1.f - yoffset, 0.,
+ (GLfloat) - xoffset, (GLfloat)fileHeight - 1.f - yoffset, 0.
+ };
+
+ const GLfloat texCoords[] = {
+ texCoordW, 0.0f,
+ 0.0f, 0.0f,
+ texCoordW, texCoordH,
+ 0.0f, texCoordH
+ };
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+ glDisable(GL_BLEND);
+ // Copy Our ViewPort To The Texture
+ copyTexSubImage2D(GL_TEXTURE_2D, 0, atX + xoffset, atY + yoffset, viewportOffsetX, viewportOffsetY, w, h, backdropTextureName);
+
+ yoffset += viewportHeight;
+ }
+ xoffset += viewportWidth;
+ }
+ glUseProgram(0);
+
+ setPixelCoords(false);
+ deleteTextures(1, &thumbnailTextureName);
+
+ thumbnailTextureName = 0;
+ }
+#endif
+}
+
+#if ALLOW_FILE
+bool skipThumbnail(FILE *fp) {
+ thumbWidth = get4bytes(fp);
+ thumbHeight = get4bytes(fp);
+ uint32_t skippy = thumbWidth;
+ skippy *= thumbHeight << 1;
+ fseek(fp, skippy, 1);
+ return (fgetc(fp) == '!');
+}
+#endif
diff --git a/engines/sludge/thumbnail.h b/engines/sludge/thumbnail.h
new file mode 100644
index 0000000000..4e505bedd2
--- /dev/null
+++ b/engines/sludge/thumbnail.h
@@ -0,0 +1,31 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_THUMBNAIL_H
+#define SLUDGE_THUMBNAIL_H
+
+#if ALLOW_FILE
+bool saveThumbnail(FILE *fp);
+bool skipThumbnail(FILE *fp);
+#endif
+
+void showThumbnail(char *filename, int x, int y);
+#endif
diff --git a/engines/sludge/timing.cpp b/engines/sludge/timing.cpp
new file mode 100644
index 0000000000..a1dcba0882
--- /dev/null
+++ b/engines/sludge/timing.cpp
@@ -0,0 +1,56 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#if 0
+#include <SDL/SDL.h>
+#endif
+
+int desiredfps = 300; //holds desired frames per second
+Uint32 starttime, endtime;
+Uint32 desired_frame_time;
+
+void Init_Timer(void) {
+ desired_frame_time = 1000 / desiredfps;
+ starttime = SDL_GetTicks();
+}
+
+void Init_Special_Timer(int t) {
+ desired_frame_time = 1000 / t;
+ starttime = SDL_GetTicks();
+}
+
+void Wait_Frame(void) {
+ static Uint32 addNextTime = 0;
+ Uint32 timetaken;
+
+ for (;;) {
+ endtime = SDL_GetTicks();
+ timetaken = addNextTime + endtime - starttime;
+ if (timetaken >= desired_frame_time) break;
+ SDL_Delay(1);
+ }
+
+ addNextTime = timetaken - desired_frame_time;
+ if (addNextTime > desired_frame_time) addNextTime = desired_frame_time;
+
+ starttime = endtime;
+}
+
diff --git a/engines/sludge/timing.h b/engines/sludge/timing.h
new file mode 100644
index 0000000000..504bf62470
--- /dev/null
+++ b/engines/sludge/timing.h
@@ -0,0 +1,31 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_TIMING_H
+#define SLUDGE_TIMING_H
+
+void Init_Timer(void);
+void Init_Special_Timer(int t);
+void Get_Start_Time(void);
+void Get_End_Time(void);
+void Wait_Frame(void);
+
+#endif
diff --git a/engines/sludge/transition.cpp b/engines/sludge/transition.cpp
new file mode 100644
index 0000000000..041e2c97d8
--- /dev/null
+++ b/engines/sludge/transition.cpp
@@ -0,0 +1,408 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <stdint.h>
+#include <string.h>
+
+#include "allfiles.h"
+#include "colours.h"
+#include "backdrop.h"
+#include "graphics.h"
+#include "newfatal.h"
+
+#if 0
+extern GLuint snapshotTextureName;
+#endif
+
+extern unsigned char brightnessLevel;
+
+extern float snapTexW, snapTexH;
+
+unsigned char fadeMode = 2;
+
+
+//----------------------------------------------------
+// PROPER BRIGHTNESS FADING
+//----------------------------------------------------
+
+unsigned lastFrom, lastTo;
+
+void transitionFader() {
+#if 0
+ glEnable(GL_BLEND);
+
+ const GLfloat vertices[] = {
+ 0.f, (GLfloat)winHeight, 0.f,
+ (GLfloat)winWidth, (GLfloat)winHeight, 0.f,
+ 0.f, 0.f, 0.f,
+ (GLfloat)winWidth, 0.f, 0.f
+ };
+
+ glUseProgram(shader.color);
+
+ setPMVMatrix(shader.color);
+ setPrimaryColor(0.0f, 0.0f, 0.0f, 1.0f - brightnessLevel / 255.f);
+ drawQuad(shader.color, vertices, 0);
+
+ glUseProgram(0);
+
+ glDisable(GL_BLEND);
+#endif
+}
+
+void transitionCrossFader() {
+#if 0
+ if (! snapshotTextureName) return;
+
+ glBindTexture(GL_TEXTURE_2D, snapshotTextureName);
+
+ glEnable(GL_BLEND);
+
+ const GLfloat vertices[] = {
+ 0.f, (GLfloat)winHeight, 0.f,
+ (GLfloat)winWidth, (GLfloat)winHeight, 0.f,
+ 0.f, 0.f, 0.f,
+ (GLfloat)winWidth, 0.f, 0.f
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, snapTexH,
+ snapTexW, snapTexH,
+ 0.0f, 0.0f,
+ snapTexW, 0.0f
+ };
+
+ glUseProgram(shader.texture);
+
+ setPMVMatrix(shader.texture);
+
+ glUniform1i(glGetUniformLocation(shader.texture, "modulateColor"), 1);
+
+ setPrimaryColor(1.0f, 1.0f, 1.0f, 1.0f - brightnessLevel / 255.f);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+
+ glUniform1i(glGetUniformLocation(shader.texture, "modulateColor"), 0);
+ glUseProgram(0);
+
+ glDisable(GL_BLEND);
+#endif
+}
+
+void transitionSnapshotBox() {
+#if 0
+ if (! snapshotTextureName) return;
+
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glBindTexture(GL_TEXTURE_2D, snapshotTextureName);
+
+ float xScale = (float) brightnessLevel * winWidth / 510.f; // 510 = 255*2
+ float yScale = (float) brightnessLevel * winHeight / 510.f;
+
+ const GLfloat vertices[] = {
+ xScale, winHeight - yScale, 0,
+ winWidth - xScale, winHeight - yScale, 0,
+ xScale, yScale, 0,
+ winWidth - xScale, yScale, 0
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, snapTexH,
+ snapTexW, snapTexH,
+ 0.0f, 0.0f,
+ snapTexW, 0.0f
+ };
+
+ glUseProgram(shader.texture);
+
+ setPMVMatrix(shader.texture);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+
+ glUseProgram(0);
+#endif
+}
+
+//----------------------------------------------------
+// FAST PSEUDO-RANDOM NUMBER STUFF FOR DISOLVE EFFECT
+//----------------------------------------------------
+
+#define KK 17
+uint32_t randbuffer[KK][2]; // history buffer
+int p1, p2;
+
+void resetRandW() {
+ int32_t seed = 12345;
+
+ for (int i = 0; i < KK; i++) {
+ for (int j = 0; j < 2; j++) {
+ seed = seed * 2891336453u + 1;
+ randbuffer[i][j] = seed;
+ }
+ }
+
+ p1 = 0, p2 = 10;
+}
+
+#if 0
+GLubyte *transitionTexture = NULL;
+GLuint transitionTextureName = 0;
+#endif
+
+bool reserveTransitionTexture() {
+#if 0
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ if (! transitionTexture) {
+ transitionTexture = new GLubyte [256 * 256 * 4];
+ if (! checkNew(transitionTexture)) return false;
+ }
+
+ if (! transitionTextureName) glGenTextures(1, &transitionTextureName);
+ glBindTexture(GL_TEXTURE_2D, transitionTextureName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, transitionTexture, transitionTextureName);
+#endif
+ return true;
+}
+
+
+void transitionDisolve() {
+
+#if 0
+ if (! transitionTextureName) reserveTransitionTexture();
+
+ if (! brightnessLevel) {
+ transitionFader();
+ return;
+ }
+
+ uint32_t n;
+ uint32_t y;
+
+ GLubyte *toScreen = transitionTexture;
+ GLubyte *end = transitionTexture + (256 * 256 * 4);
+
+ do {
+ // generate next number
+ n = randbuffer[p1][1];
+ y = (n << 27) | ((n >> (32 - 27)) + randbuffer[p2][1]);
+
+ n = randbuffer[p1][0];
+ randbuffer[p1][1] = (n << 19) | ((n >> (32 - 19)) + randbuffer[p2][0]);
+ randbuffer[p1][0] = y;
+
+ // rotate list pointers
+ if (! p1 --) p1 = KK - 1;
+ if (! p2 --) p2 = KK - 1;
+
+ if ((y & 255u) > brightnessLevel) {
+ toScreen[0] = toScreen[1] = toScreen[2] = 0;
+ toScreen[3] = 255;
+ } else {
+ toScreen[0] = toScreen[1] = toScreen[2] = toScreen[3] = 0;
+ }
+ toScreen += 4;
+ } while (toScreen < end);
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, transitionTexture, transitionTextureName);
+
+ glEnable(GL_BLEND);
+
+ const GLfloat vertices[] = {
+ 0.f, (GLfloat)winHeight, 0.f,
+ (GLfloat)winWidth, (GLfloat)winHeight, 0.f,
+ 0.f, 0.f, 0.f,
+ (GLfloat)winWidth, 0.f, 0.f
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f
+ };
+
+ glUseProgram(shader.texture);
+
+ setPMVMatrix(shader.texture);
+
+ glUniform1i(glGetUniformLocation(shader.texture, "modulateColor"), 1);
+
+ setPrimaryColor(1.0f, 1.0f, 1.0f, 1.0f);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+
+ glUniform1i(glGetUniformLocation(shader.texture, "modulateColor"), 0);
+ glUseProgram(0);
+
+ glDisable(GL_BLEND);
+#endif
+}
+
+void transitionTV() {
+
+#if 0
+ if (! transitionTextureName) reserveTransitionTexture();
+
+ uint32_t n;
+ uint32_t y;
+
+ GLubyte *toScreen = transitionTexture;
+ GLubyte *end = transitionTexture + (256 * 256 * 4);
+
+ do {
+ // generate next number
+ n = randbuffer[p1][1];
+ y = (n << 27) | ((n >> (32 - 27)) + randbuffer[p2][1]);
+
+ n = randbuffer[p1][0];
+ randbuffer[p1][1] = (n << 19) | ((n >> (32 - 19)) + randbuffer[p2][0]);
+ randbuffer[p1][0] = y;
+
+ // rotate list pointers
+ if (! p1 --) p1 = KK - 1;
+ if (! p2 --) p2 = KK - 1;
+
+ if ((y & 255u) > brightnessLevel) {
+ toScreen[0] = toScreen[1] = toScreen[2] = (n & 255);
+ toScreen[3] = (n & 255);
+ } else {
+ toScreen[0] = toScreen[1] = toScreen[2] = toScreen[3] = 0;
+ }
+ toScreen += 4;
+ } while (toScreen < end);
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, transitionTexture, transitionTextureName);
+
+ glEnable(GL_BLEND);
+
+ const GLfloat vertices[] = {
+ 0.f, (GLfloat)winHeight, 0.f,
+ (GLfloat)winWidth, (GLfloat)winHeight, 0.f,
+ 0.f, 0.f, 0.f,
+ (GLfloat)winWidth, 0.f, 0.f
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f
+ };
+
+ glUseProgram(shader.texture);
+
+ setPMVMatrix(shader.texture);
+
+ glUniform1i(glGetUniformLocation(shader.texture, "modulateColor"), 1);
+
+ setPrimaryColor(1.0f, 1.0f, 1.0f, 1.0f);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+
+ glUniform1i(glGetUniformLocation(shader.texture, "modulateColor"), 0);
+ glUseProgram(0);
+
+ glDisable(GL_BLEND);
+#endif
+}
+
+void transitionBlinds() {
+#if 0
+ if (! transitionTextureName) reserveTransitionTexture();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ GLubyte *toScreen = transitionTexture;
+
+ int level = brightnessLevel / 8;
+
+ if (level) memset(toScreen, 0, 256 * 32 * level);
+ if (level < 32) memset(toScreen + 256 * 32 * level, 255, 256 * 32 * (32 - level));
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, transitionTexture, transitionTextureName);
+
+ glEnable(GL_BLEND);
+
+ const GLfloat vertices[] = {
+ 0.f, (GLfloat)winHeight, 0.f,
+ (GLfloat)winWidth, (GLfloat)winHeight, 0.f,
+ 0.f, 0.f, 0.f,
+ (GLfloat)winWidth, 0.f, 0.f
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 25.0f,
+ 1.0f, 25.0f
+ };
+
+ glUseProgram(shader.texture);
+
+ setPMVMatrix(shader.texture);
+
+ glUniform1i(glGetUniformLocation(shader.texture, "modulateColor"), 1);
+
+ setPrimaryColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+
+ glUniform1i(glGetUniformLocation(shader.texture, "modulateColor"), 0);
+ glUseProgram(0);
+
+ glDisable(GL_BLEND);
+#endif
+}
+
+//----------------------------------------------------
+
+void fixBrightness() {
+ switch (fadeMode) {
+ case 0:
+ transitionFader();
+ break;
+ case 1:
+ resetRandW();
+ // Fall through!
+ case 2:
+ transitionDisolve();
+ break;
+ case 3:
+ transitionTV();
+ break;
+ case 4:
+ transitionBlinds();
+ break;
+ case 5:
+ transitionCrossFader();
+ break;
+ case 6:
+ transitionSnapshotBox();
+ break;
+
+ }
+}
diff --git a/engines/sludge/transition.h b/engines/sludge/transition.h
new file mode 100644
index 0000000000..f05c438ecb
--- /dev/null
+++ b/engines/sludge/transition.h
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_TRANSITION_H
+#define SLUDGE_TRANSITION_H
+
+void fixBrightness();
+void resetRandW();
+
+#endif
diff --git a/engines/sludge/variable.cpp b/engines/sludge/variable.cpp
new file mode 100644
index 0000000000..2c64a7fa25
--- /dev/null
+++ b/engines/sludge/variable.cpp
@@ -0,0 +1,669 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include <string.h>
+
+#include "debug.h"
+
+#include "allfiles.h"
+
+#include "variable.h"
+#include "newfatal.h"
+
+#include "stringy.h"
+#include "objtypes.h"
+#include "people.h"
+#include "fileset.h"
+
+#include <dirent.h>
+#include "moreio.h"
+#ifdef _WIN32
+#include "windows.h"
+
+#endif
+
+const char *typeName[] = {"undefined", "number", "user function", "string",
+ "built-in function", "file", "stack",
+ "object type", "animation", "costume"
+ };
+
+extern char *outputDir;
+
+void unlinkVar(variable &thisVar) {
+ switch (thisVar.varType) {
+ case SVT_STRING:
+ delete[] thisVar.varData.theString;
+ thisVar.varData.theString = NULL;
+ break;
+
+ case SVT_STACK:
+ thisVar.varData.theStack -> timesUsed --;
+ if (thisVar.varData.theStack -> timesUsed <= 0) {
+ while (thisVar.varData.theStack -> first) trimStack(thisVar.varData.theStack -> first);
+ delete thisVar.varData.theStack;
+ thisVar.varData.theStack = NULL;
+ }
+ break;
+
+ case SVT_FASTARRAY:
+ thisVar.varData.fastArray -> timesUsed --;
+ if (thisVar.varData.theStack -> timesUsed <= 0) {
+ delete thisVar.varData.fastArray -> fastVariables;
+ delete[] thisVar.varData.fastArray;
+ thisVar.varData.fastArray = NULL;
+ }
+ break;
+
+ case SVT_ANIM:
+ deleteAnim(thisVar.varData.animHandler);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void setVariable(variable &thisVar, variableType vT, int value) {
+ unlinkVar(thisVar);
+ thisVar.varType = vT;
+ thisVar.varData.intValue = value;
+}
+
+void newAnimationVariable(variable &thisVar, personaAnimation *i) {
+ unlinkVar(thisVar);
+ thisVar.varType = SVT_ANIM;
+ thisVar.varData.animHandler = i;
+}
+
+personaAnimation *getAnimationFromVar(variable &thisVar) {
+ if (thisVar.varType == SVT_ANIM)
+ return copyAnim(thisVar.varData.animHandler);
+
+ if (thisVar.varType == SVT_INT && thisVar.varData.intValue == 0)
+ return makeNullAnim();
+
+ fatal("Expecting an animation variable; found variable of type", typeName[thisVar.varType]);
+ return NULL;
+}
+
+void newCostumeVariable(variable &thisVar, persona *i) {
+ unlinkVar(thisVar);
+ thisVar.varType = SVT_COSTUME;
+ thisVar.varData.costumeHandler = i;
+}
+
+persona *getCostumeFromVar(variable &thisVar) {
+ persona *p = NULL;
+
+ switch (thisVar.varType) {
+ case SVT_ANIM:
+ p = new persona;
+ if (! checkNew(p)) return NULL;
+ p -> numDirections = 1;
+ p -> animation = new personaAnimation * [3];
+ if (! checkNew(p -> animation)) return NULL;
+
+ for (int iii = 0; iii < 3; iii ++)
+ p -> animation[iii] = copyAnim(thisVar.varData.animHandler);
+
+ break;
+
+ case SVT_COSTUME:
+ return thisVar.varData.costumeHandler;
+ break;
+
+ default:
+ fatal("Expecting an animation variable; found variable of type", typeName[thisVar.varType]);
+ }
+
+ return p;
+}
+
+int stackSize(const stackHandler *me) {
+ int r = 0;
+ variableStack *a = me -> first;
+ while (a) {
+ r ++;
+ a = a -> next;
+ }
+ return r;
+}
+
+#ifdef _WIN32
+#include <windows.h>
+WCHAR *ConvertToUTF16(const char *input);
+char *ConvertFromUTF16(const WCHAR *input);
+#endif
+
+bool getSavedGamesStack(stackHandler *sH, char *ext) {
+ char *pattern = joinStrings("*", ext);
+ if (! pattern) return false;
+
+ variable newName;
+ newName.varType = SVT_NULL;
+
+#ifdef _WIN32
+
+ WCHAR *w_pattern = ConvertToUTF16(pattern);
+
+ WIN32_FIND_DATA theData;
+ HANDLE handle = FindFirstFile(w_pattern, & theData);
+
+ delete w_pattern;
+
+ if (handle != INVALID_HANDLE_VALUE) {
+ bool keepGoing;
+ do {
+ theData.cFileName[lstrlen(theData.cFileName) - strlen(ext)] = TEXT('\0');
+ char *fileName = ConvertFromUTF16(theData.cFileName);
+ char *decoded = decodeFilename(fileName);
+ makeTextVar(newName, decoded);
+ delete fileName;
+ delete decoded;
+ if (! addVarToStack(newName, sH -> first)) return false;
+ if (sH -> last == NULL) sH -> last = sH -> first;
+ keepGoing = FindNextFile(handle, & theData);
+ } while (keepGoing);
+ FindClose(handle);
+ }
+
+#else
+
+ DIR *dir = opendir(".");
+ if (! dir) return false;
+
+ struct dirent *d = readdir(dir);
+ while (d != NULL) {
+ if (! strcmp(d->d_name + strlen(d->d_name) - strlen(ext), ext)) {
+ d->d_name[strlen(d->d_name) - strlen(ext)] = 0;
+ char *decoded = decodeFilename(d->d_name);
+ makeTextVar(newName, decoded);
+ delete[] decoded;
+ if (! addVarToStack(newName, sH -> first)) return false;
+ if (sH -> last == NULL) sH -> last = sH -> first;
+ }
+
+ d = readdir(dir);
+ }
+
+
+ closedir(dir);
+
+#endif
+
+ delete[] pattern;
+ pattern = NULL;
+ return true;
+}
+
+bool copyStack(const variable &from, variable &to) {
+ to.varType = SVT_STACK;
+ to.varData.theStack = new stackHandler;
+ if (! checkNew(to.varData.theStack)) return false;
+ to.varData.theStack -> first = NULL;
+ to.varData.theStack -> last = NULL;
+ to.varData.theStack -> timesUsed = 1;
+ variableStack *a = from.varData.theStack -> first;
+
+#if DEBUG_STACKINESS
+ {
+ char *str = getTextFromAnyVar(from);
+ stackDebug((stackfp, "in copyStack, copying %s\n", str));
+ delete[] str;
+ }
+#endif
+
+ while (a) {
+ addVarToStack(a -> thisVar, to.varData.theStack -> first);
+ if (to.varData.theStack -> last == NULL) {
+#if DEBUG_STACKINESS
+ stackDebug((stackfp, "LAST"));
+#endif
+ to.varData.theStack -> last = to.varData.theStack -> first;
+ }
+
+#if DEBUG_STACKINESS
+ {
+ char *str = getTextFromAnyVar(a->thisVar);
+ stackDebug((stackfp, "\ta->thisVar = %s (%p)\n", str, to.varData.theStack->first));
+ delete[] str;
+ }
+#endif
+
+ a = a -> next;
+ }
+
+#if DEBUG_STACKINESS
+ {
+ char *str = getTextFromAnyVar(to);
+ stackDebug((stackfp, "finished copy, got %s\n", str));
+ delete[] str;
+ stackDebug((stackfp, "first = %p\n", to.varData.theStack->first));
+ stackDebug((stackfp, "last = %p\n", to.varData.theStack->last));
+ }
+#endif
+ return true;
+}
+
+/*void newStackVar (variable & thisVar) {
+ unlinkVar (thisVar);
+ thisVar.varType = VT_STACK;
+ thisVar.varData.theStack = NULL;
+} */
+
+void addVariablesInSecond(variable &var1, variable &var2) {
+ if (var1.varType == SVT_INT && var2.varType == SVT_INT) {
+ var2.varData.intValue += var1.varData.intValue;
+ } else {
+ char *string1 = getTextFromAnyVar(var1);
+ char *string2 = getTextFromAnyVar(var2);
+
+ unlinkVar(var2);
+ var2.varData.theString = joinStrings(string1, string2);
+ var2.varType = SVT_STRING;
+ delete[] string1;
+ delete[] string2;
+ }
+}
+
+int compareVars(const variable &var1, const variable &var2) {
+ int re = 0;
+ if (var1.varType == var2.varType) {
+ switch (var1.varType) {
+ case SVT_NULL:
+ re = 1;
+ break;
+
+ case SVT_COSTUME:
+ re = (var1.varData.costumeHandler == var2.varData.costumeHandler);
+ break;
+
+ case SVT_ANIM:
+ re = (var1.varData.animHandler == var2.varData.animHandler);
+ break;
+
+ case SVT_STRING:
+
+ re = (strcmp(var1.varData.theString, var2.varData.theString) == 0);
+ break;
+
+ case SVT_STACK:
+ re = (var1.varData.theStack == var2.varData.theStack);
+ break;
+
+ default:
+ re = (var1.varData.intValue == var2.varData.intValue);
+ }
+ }
+ return re;
+}
+
+void compareVariablesInSecond(const variable &var1, variable &var2) {
+ setVariable(var2, SVT_INT, compareVars(var1, var2));
+}
+
+void makeTextVar(variable &thisVar, const char *txt) {
+ unlinkVar(thisVar);
+ thisVar.varType = SVT_STRING;
+ thisVar.varData.theString = copyString(txt);
+}
+
+bool loadStringToVar(variable &thisVar, int value) {
+#if ALLOW_FILE
+ makeTextVar(thisVar, getNumberedString(value));
+#endif
+ return (bool)(thisVar.varData.theString != NULL);
+}
+
+char *getTextFromAnyVar(const variable &from) {
+ switch (from.varType) {
+ case SVT_STRING:
+ return copyString(from.varData.theString);
+
+ case SVT_FASTARRAY: {
+ char *builder = copyString("FAST:");
+ char *builder2;
+ char *grabText;
+
+ for (int i = 0; i < from.varData.fastArray -> size; i ++) {
+ builder2 = joinStrings(builder, " ");
+ if (! builder2) return NULL;
+ delete builder;
+ grabText = getTextFromAnyVar(from.varData.fastArray -> fastVariables[i]);
+ builder = joinStrings(builder2, grabText);
+ if (! builder) return NULL;
+ delete grabText;
+ grabText = NULL;
+ delete builder2;
+ builder2 = NULL;
+ }
+ return builder;
+ }
+
+ case SVT_STACK: {
+ char *builder = copyString("ARRAY:");
+ char *builder2;
+ char *grabText;
+
+ variableStack *stacky = from.varData.theStack -> first;
+
+ while (stacky) {
+ builder2 = joinStrings(builder, " ");
+ if (! builder2) return NULL;
+ delete builder;
+ grabText = getTextFromAnyVar(stacky -> thisVar);
+ builder = joinStrings(builder2, grabText);
+ if (! builder) return NULL;
+ delete grabText;
+ grabText = NULL;
+ delete builder2;
+ builder2 = NULL;
+ stacky = stacky -> next;
+ }
+ return builder;
+ }
+
+ case SVT_INT: {
+ char *buff = new char[10];
+ if (! checkNew(buff)) return NULL;
+ sprintf(buff, "%i", from.varData.intValue);
+ return buff;
+ }
+
+ case SVT_FILE: {
+// char * buff = new char[15];
+// if (! checkNew (buff)) return NULL;
+// sprintf (buff, "FILE %i", from.varData.intValue);
+ return joinStrings("", resourceNameFromNum(from.varData.intValue));
+ }
+
+ /* case SVT_ANIM:
+ {
+ char * buff = new char[20];
+ if (! checkNew (buff)) return NULL;
+ sprintf (buff, "%p", from.varData.animHandler);
+ return buff;
+ }*/
+
+ case SVT_OBJTYPE: {
+ objectType *thisType = findObjectType(from.varData.intValue);
+ if (thisType) return copyString(thisType -> screenName);
+ }
+
+ default:
+ break;
+ }
+
+ return copyString(typeName[from.varType]);
+}
+
+bool getBoolean(const variable &from) {
+ switch (from.varType) {
+ case SVT_NULL:
+ return false;
+
+ case SVT_INT:
+ return (bool)(from.varData.intValue != 0);
+
+ case SVT_STACK:
+ return (bool)(from.varData.theStack -> first != NULL);
+
+ case SVT_STRING:
+ return (bool)(from.varData.theString[0] != 0);
+
+ case SVT_FASTARRAY:
+ return (bool)(from.varData.fastArray -> size != 0);
+
+ default:
+ break;
+ }
+ return true;
+}
+
+bool copyMain(const variable &from, variable &to) {
+ to.varType = from.varType;
+ switch (to.varType) {
+ case SVT_INT:
+ case SVT_FUNC:
+ case SVT_BUILT:
+ case SVT_FILE:
+ case SVT_OBJTYPE:
+ to.varData.intValue = from.varData.intValue;
+ return true;
+
+ case SVT_FASTARRAY:
+ to.varData.fastArray = from.varData.fastArray;
+ to.varData.fastArray -> timesUsed ++;
+ return true;
+
+ case SVT_STRING:
+ to.varData.theString = copyString(from.varData.theString);
+ return to.varData.theString ? true : false;
+
+ case SVT_STACK:
+ to.varData.theStack = from.varData.theStack;
+ to.varData.theStack -> timesUsed ++;
+ return true;
+
+ case SVT_COSTUME:
+ to.varData.costumeHandler = from.varData.costumeHandler;
+ return true;
+
+ case SVT_ANIM:
+ to.varData.animHandler = copyAnim(from.varData.animHandler);
+ return true;
+
+ case SVT_NULL:
+ return true;
+
+ default:
+ break;
+ }
+ fatal("Unknown value type");
+ return false;
+}
+
+bool copyVariable(const variable &from, variable &to) {
+ unlinkVar(to);
+ return copyMain(from, to);
+}
+
+variable *fastArrayGetByIndex(fastArrayHandler *vS, unsigned int theIndex) {
+ if (theIndex < 0 || theIndex >= vS -> size) return NULL;
+ return & vS -> fastVariables[theIndex];
+}
+
+bool makeFastArraySize(variable &to, int size) {
+ if (size < 0) return fatal("Can't create a fast array with a negative number of elements!");
+ unlinkVar(to);
+ to.varType = SVT_FASTARRAY;
+ to.varData.fastArray = new fastArrayHandler;
+ if (! checkNew(to.varData.fastArray)) return false;
+ to.varData.fastArray -> fastVariables = new variable[size];
+ if (! checkNew(to.varData.fastArray -> fastVariables)) return false;
+ for (int i = 0; i < size; i ++) {
+ initVarNew(to.varData.fastArray -> fastVariables[i]);
+ }
+ to.varData.fastArray -> size = size;
+ to.varData.fastArray -> timesUsed = 1;
+ return true;
+}
+
+bool makeFastArrayFromStack(variable &to, const stackHandler *stacky) {
+ int size = stackSize(stacky);
+ if (! makeFastArraySize(to, size)) return false;
+
+ // Now let's fill up the new array
+
+ variableStack *allV = stacky -> first;
+ size = 0;
+ while (allV) {
+ copyMain(allV -> thisVar, to.varData.fastArray -> fastVariables[size]);
+ size ++;
+ allV = allV -> next;
+ }
+ return true;
+}
+
+/*
+bool moveVariable (variable & from, variable & to) {
+ unlinkVar (to);
+ memcpy (& to, & from, sizeof (variable));
+ from.varType = SVT_NULL;
+}
+*/
+
+bool addVarToStack(const variable &va, variableStack *&thisStack) {
+ variableStack *newStack = new variableStack;
+ if (! checkNew(newStack)) return false;
+
+ if (! copyMain(va, newStack -> thisVar)) return false;
+ newStack -> next = thisStack;
+ thisStack = newStack;
+ //printf("Variable %s was added to stack\n", getTextFromAnyVar(va));
+ return true;
+}
+
+bool addVarToStackQuick(variable &va, variableStack *&thisStack) {
+ variableStack *newStack = new variableStack;
+ if (! checkNew(newStack)) return false;
+
+// if (! copyMain (va, newStack -> thisVar)) return false;
+
+ memcpy(& (newStack -> thisVar), & va, sizeof(variable));
+ va.varType = SVT_NULL;
+
+ newStack -> next = thisStack;
+ thisStack = newStack;
+ //printf("Variable %s was added to stack quick\n", getTextFromAnyVar(va));
+ return true;
+}
+
+bool stackSetByIndex(variableStack *vS, unsigned int theIndex, const variable &va) {
+ while (theIndex --) {
+ vS = vS -> next;
+ if (! vS) return fatal("Index past end of stack.");
+ }
+ return copyVariable(va, vS -> thisVar);
+}
+
+variable *stackGetByIndex(variableStack *vS, unsigned int theIndex) {
+ while (theIndex --) {
+ vS = vS -> next;
+ if (! vS) {
+ return NULL;
+ }
+ }
+ return & (vS -> thisVar);
+}
+
+int deleteVarFromStack(const variable &va, variableStack *&thisStack, bool allOfEm) {
+ variableStack * * huntVar = & thisStack;
+ variableStack *killMe;
+ int reply = 0;
+
+ while (* huntVar) {
+ if (compareVars((* huntVar) -> thisVar, va)) {
+ killMe = * huntVar;
+ * huntVar = killMe -> next;
+ unlinkVar(killMe -> thisVar);
+ delete killMe;
+ if (! allOfEm) return 1;
+ reply ++;
+ } else {
+ huntVar = & ((* huntVar) -> next);
+ }
+ }
+
+ return reply;
+}
+
+// Would be a LOT better just to keep this up to date in the above function... ah well
+variableStack *stackFindLast(variableStack *hunt) {
+ if (hunt == NULL)
+ return NULL;
+
+ while (hunt->next)
+ hunt = hunt->next;
+
+ return hunt;
+}
+
+bool getValueType(int &toHere, variableType vT, const variable &v) {
+ //if (! v) return false;
+ if (v.varType != vT) {
+ char *e1 = joinStrings("Can only perform specified operation on a value which is of type ", typeName[vT]);
+ char *e2 = joinStrings("... value supplied was of type ", typeName[v.varType]);
+ fatal(e1, e2);
+
+ return false;
+ }
+ toHere = v.varData.intValue;
+ return true;
+}
+
+void trimStack(variableStack *&stack) {
+ variableStack *killMe = stack;
+ stack = stack -> next;
+
+ //printf("Variable %s was removed from stack\n", getTextFromAnyVar(killMe -> thisVar));
+
+ // When calling this, we've ALWAYS checked that stack != NULL
+ unlinkVar(killMe -> thisVar);
+ delete killMe;
+}
+/*
+void debugVar (FILE * fp, const variable & thisVar) {
+ switch (thisVar.varType) {
+ case SVT_INT:
+ fprintf (fp, "integer value %i", thisVar.varData.intValue);
+ break;
+
+ case SVT_FUNC:
+ fprintf (fp, "pointer to function %i", thisVar.varData.intValue);
+ break;
+
+ case SVT_BUILT:
+ fprintf (fp, "pointer to bif %i", thisVar.varData.intValue);
+ break;
+
+ case SVT_OBJTYPE:
+ fprintf (fp, "object type %i", thisVar.varData.intValue);
+ break;
+
+ case SVT_STRING:
+ fprintf (fp, "\"%s\"", thisVar.varData.theString);
+ break;
+
+ case SVT_FILE:
+ fprintf (fp, "file handle %i", thisVar.varData.intValue);
+ break;
+
+ case SVT_NULL:
+ fprintf (fp, "null");
+ break;
+
+ default:
+ fprintf (fp, "unknown variable type");
+ break;
+ }
+}
+*/
diff --git a/engines/sludge/variable.h b/engines/sludge/variable.h
new file mode 100644
index 0000000000..8d633c8160
--- /dev/null
+++ b/engines/sludge/variable.h
@@ -0,0 +1,119 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_VARIABLE_H
+#define SLUDGE_VARIABLE_H
+
+enum variableType {SVT_NULL, SVT_INT, SVT_FUNC, SVT_STRING,
+ SVT_BUILT, SVT_FILE, SVT_STACK,
+ SVT_OBJTYPE, SVT_ANIM, SVT_COSTUME,
+ SVT_FASTARRAY, SVT_NUM_TYPES
+ };
+
+struct fastArrayHandler {
+ struct variable *fastVariables;
+ int size;
+ int timesUsed;
+};
+
+struct stackHandler {
+ struct variableStack *first;
+ struct variableStack *last;
+ int timesUsed;
+};
+
+union variableData {
+ signed int intValue;
+ char *theString;
+ stackHandler *theStack;
+ struct personaAnimation *animHandler;
+ struct persona *costumeHandler;
+ fastArrayHandler *fastArray;
+};
+
+struct variable {
+ variableType varType;
+ variableData varData;
+};
+
+struct variableStack {
+ variable thisVar;
+ variableStack *next;
+};
+
+// Initialisation
+
+#define initVarNew(thisVar) thisVar.varType = SVT_NULL
+
+// Setting variables
+
+void setVariable(variable &thisVar, variableType vT, int value);
+bool copyVariable(const variable &from, variable &to);
+bool loadStringToVar(variable &thisVar, int value);
+void newAnimationVariable(variable &thisVar, struct personaAnimation *i);
+void newCostumeVariable(variable &thisVar, struct persona *i);
+void makeTextVar(variable &thisVar, const char *txt);
+void addVariablesInSecond(variable &var1, variable &var2);
+void compareVariablesInSecond(const variable &var1, variable &var2);
+
+// Misc.
+
+void unlinkVar(variable &thisVar);
+char *getNumberedString(int value);
+char *getTextFromAnyVar(const variable &from);
+struct persona *getCostumeFromVar(variable &thisVar);
+struct personaAnimation *getAnimationFromVar(variable &thisVar);
+bool getBoolean(const variable &from);
+bool getValueType(int &toHere, variableType vT, const variable &v);
+
+// Stacky stuff
+
+bool addVarToStack(const variable &va, variableStack *&thisStack);
+bool addVarToStackQuick(variable &va, variableStack *&thisStack);
+void trimStack(variableStack *&stack);
+int deleteVarFromStack(const variable &va, variableStack *&thisStack, bool allOfEm = false);
+variableStack *stackFindLast(variableStack *hunt);
+bool copyStack(const variable &from, variable &to);
+int stackSize(const stackHandler *me);
+bool stackSetByIndex(variableStack *, unsigned int, const variable &);
+variable *stackGetByIndex(variableStack *, unsigned int);
+bool getSavedGamesStack(stackHandler *sH, char *ext);
+
+bool makeFastArrayFromStack(variable &to, const stackHandler *stacky);
+bool makeFastArraySize(variable &to, int size);
+variable *fastArrayGetByIndex(fastArrayHandler *vS, unsigned int theIndex);
+
+
+#define DEBUG_STACKINESS 0
+
+#if DEBUG_STACKINESS
+#define stackDebug(params) { \
+ FILE * stackfp = fopen ("stackout.txt", "at"); \
+ fprintf params; \
+ fclose (stackfp); \
+ }
+#else
+#define stackDebug(a) {}
+#endif
+
+
+#endif
+
diff --git a/engines/sludge/vid.cpp b/engines/sludge/vid.cpp
new file mode 100644
index 0000000000..a95a452e89
--- /dev/null
+++ b/engines/sludge/vid.cpp
@@ -0,0 +1,259 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "allfiles.h"
+
+#ifndef _MSC_VER // Microsoft compiler?
+#include <unistd.h> // For unlink
+#else
+#include <io.h>
+#include <stdio.h>
+#endif
+
+#include "newfatal.h"
+#include "stringy.h"
+#if 0
+#include "vfw.h"
+#endif
+#include "sound.h"
+#include "colours.h"
+#include "fileset.h"
+
+struct timStream {
+ PAVISTREAM got;
+ AVISTREAMINFO info;
+ LONG chunkSize;
+ LPBYTE chunk;
+};
+
+static PAVIFILE pAviFile = NULL;
+static PGETFRAME pgf;
+static timStream audio, video;
+static int videoFrameNum = 0;
+static int vidBytesPerPixel = 0;
+static int vidWidth, vidHeight;
+static DWORD biSize;
+
+static char *videoFile = NULL;
+
+bool videoPlaying = false;
+
+extern int winWidth, winHeight;
+extern unsigned short int *screen;
+extern HWND hMainWindow;
+
+void initialiseMovieStuff() {
+ char buffer[500];
+ if (ExpandEnvironmentStrings("%temp%", buffer, 499) == 0) buffer[0] = NULL;
+ videoFile = joinStrings(buffer, "\\test.avi");
+// warning (videoFile);
+}
+
+bool getStream(DWORD type, timStream &intoHere) {
+ if (AVIFileGetStream(pAviFile, & intoHere.got, type, 0)) {
+ intoHere.got = NULL;
+ return true;
+ } else if (AVIStreamInfo(intoHere.got, & intoHere.info, sizeof(AVISTREAMINFO))) {
+ return fatal("Can't get stream info");
+ } else if (AVIStreamReadFormat(intoHere.got, AVIStreamStart(intoHere.got), NULL, & intoHere.chunkSize)) {
+ return fatal("Can't get stream chunk size");
+ } else {
+ // So far so good! Let's read a chunk of data (huh?)
+
+ intoHere.chunk = new BYTE[intoHere.chunkSize];
+
+ if (! intoHere.chunk) {
+ return fatal("Out of memory");
+ } else if (AVIStreamReadFormat(intoHere.got, AVIStreamStart(intoHere.got), intoHere.chunk, & intoHere.chunkSize)) {
+ return fatal("Couldn't read stream format");
+ }
+ }
+ return true;
+}
+
+void killStream(timStream &intoHere) {
+ delete intoHere.chunk;
+ intoHere.chunk = NULL;
+}
+
+/*
+#define WAVHEADERSIZE 20
+char wavHeader[WAVHEADERSIZE] = {'R', 'I', 'F', 'F', 0, 0, 0, 0,
+ 'W', 'A', 'V', 'E', 'f', 'm', 't',
+ 0x20, 0x10, 0, 0, 0};
+
+void handleAudio () {
+ LONG aSize;
+
+ if(AVIStreamRead(audio.got, 0, AVISTREAMREAD_CONVENIENT, NULL, 0, & aSize, NULL)) return;
+
+ int totalSize = aSize + audio.chunkSize + WAVHEADERSIZE + 4;
+
+ LPBYTE pBuffer = new BYTE[totalSize];
+ if (!pBuffer) return;
+
+ memcpy (pBuffer, wavHeader, WAVHEADERSIZE);
+ pBuffer[4] = (char) (aSize);
+ pBuffer[5] = (char) (aSize >> 8);
+ memcpy (pBuffer + WAVHEADERSIZE, audio.chunk, audio.chunkSize);
+ memcpy (pBuffer + WAVHEADERSIZE + audio.chunkSize, "data", 4);
+
+ if(! AVIStreamRead (audio.got, 0, AVISTREAMREAD_CONVENIENT, pBuffer + audio.chunkSize + WAVHEADERSIZE + 4, aSize, NULL, NULL)) {
+ FILE * fp = fopen ("test.wav", "wb");
+ if (fp) {
+ fwrite (pBuffer, 1, totalSize, fp);
+ fclose (fp);
+ }
+ int i = fakeCacheSoundForVideo ((char *) pBuffer, totalSize);
+ if (i != -1) startSound (i, false);
+ }
+
+ delete pBuffer;
+}
+*/
+
+void finishVideo() {
+ videoPlaying = false;
+ AVIStreamGetFrameClose(pgf);
+ if (audio.got) AVIStreamRelease(audio.got);
+ if (video.got) AVIStreamRelease(video.got);
+ killStream(audio);
+ killStream(video);
+ AVIFileRelease(pAviFile);
+ AVIFileExit();
+#ifdef _MSC_VER
+ _unlink(videoFile);
+#else
+ unlink(videoFile);
+#endif
+}
+
+#define COPYSIZE 256
+
+bool extractSlice(int fileNum, char *toName) {
+ unsigned char buff[COPYSIZE];
+
+ unsigned long fileLength = openFileFromNum(fileNum);
+ if (! fileLength) return false; // Error already displayed
+
+ FILE *copyVid = fopen(toName, "wb");
+ if (! copyVid) return fatal("Can't extract resource");
+
+ while (fileLength >= COPYSIZE) {
+ fread(buff, COPYSIZE, 1, bigDataFile);
+ if (fwrite(buff, 1, COPYSIZE, copyVid) != COPYSIZE) return fatal("Out of disk space extracting resource");
+ fileLength -= COPYSIZE;
+ }
+ if (fileLength) {
+ fread(buff, fileLength, 1, bigDataFile);
+ if (fwrite(buff, 1, fileLength, copyVid) != fileLength) return fatal("Out of disk space extracting resource");
+ }
+
+ fclose(copyVid);
+ finishAccess();
+
+ return true;
+}
+
+bool startVideo(int fileNum) {
+
+ setResourceForFatal(fileNum);
+
+ AVIFILEINFO info;
+
+ if (videoPlaying) finishVideo();
+ AVIFileInit();
+
+ if (! extractSlice(fileNum, videoFile)) return false;
+ if (AVIFileOpen(& pAviFile, videoFile, OF_READ, NULL))
+ return fatal(ERROR_AVI_FILE_ERROR);
+
+ AVIFileInfo(pAviFile, &info, sizeof(info));
+
+ if (! getStream(streamtypeAUDIO, audio)) return false;
+ if (! getStream(streamtypeVIDEO, video)) return false;
+
+ if (! video.got) return fatal(ERROR_AVI_NO_STREAM);
+
+// if (audio.got) handleAudio ();
+
+ pgf = AVIStreamGetFrameOpen(video.got, NULL);
+ if (!pgf) return fatal(ERROR_AVI_ARGH);
+
+ LPBITMAPINFO pInfo = (LPBITMAPINFO)(video.chunk);
+ vidBytesPerPixel = pInfo -> bmiHeader.biBitCount / 8;
+ biSize = pInfo -> bmiHeader.biSize;
+ vidWidth = pInfo -> bmiHeader.biWidth;
+ vidHeight = pInfo -> bmiHeader.biHeight;
+
+ videoFrameNum = 0;
+ videoPlaying = true;
+
+ setResourceForFatal(-1);
+
+ return true;
+}
+
+bool nextVideoFrame() {
+ LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pgf, videoFrameNum);
+ if (! lpbi) {
+ finishVideo();
+ return false;
+ }
+
+ BYTE *pData = (((BYTE *) lpbi) + lpbi->biSize);
+
+ int xOff = (winWidth - vidWidth) >> 1;
+ int yOff = (winHeight + vidHeight) >> 1;
+ unsigned short int *startingPoint = screen + xOff + (yOff * winWidth);
+
+ for (int y = 0; y < vidHeight; y ++) {
+ startingPoint -= winWidth;
+ unsigned short int *toHere = startingPoint;
+ for (int x = 0; x < vidWidth; x ++) {
+ switch (vidBytesPerPixel) {
+ case 1:
+ (* toHere) = makeGrey(*pData);
+ break;
+
+ case 3:
+ case 4:
+ (* toHere) = makeColour(*(pData + 2), *(pData + 1), *pData);
+ break;
+
+ default: {
+ WORD Pixel16 = * ((WORD *) pData);
+ (* toHere) = makeColour(
+ (((UINT)(Pixel16) >> 10) & 0x1F) << 3,
+ (((UINT)(Pixel16) >> 5) & 0x1F) << 3,
+ (((UINT)(Pixel16) >> 0) & 0x1F) << 3);
+ }
+ break;
+ }
+ pData += vidBytesPerPixel;
+ toHere ++;
+ }
+ }
+
+ videoFrameNum ++;
+
+ return true;
+}
diff --git a/engines/sludge/vid.h b/engines/sludge/vid.h
new file mode 100644
index 0000000000..5b28e371a1
--- /dev/null
+++ b/engines/sludge/vid.h
@@ -0,0 +1,30 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_VID_H
+#define SLUDGE_VID_H
+
+bool startVideo(int fileNum);
+bool nextVideoFrame();
+void finishVideo();
+void initialiseMovieStuff();
+
+#endif
diff --git a/engines/sludge/winstuff.cpp b/engines/sludge/winstuff.cpp
new file mode 100644
index 0000000000..2e51b4bb3d
--- /dev/null
+++ b/engines/sludge/winstuff.cpp
@@ -0,0 +1,227 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifdef _WIN32
+#include <windows.h>
+#include <shellapi.h>
+
+#include "allfiles.h"
+#include "debug.h"
+
+#include "winstuff.h"
+#include "platform-dependent.h"
+#include "language.h"
+#include "newfatal.h"
+#include "sprites.h"
+#include "sprbanks.h"
+#include "fonttext.h"
+#include "backdrop.h"
+#include "sludger.h"
+#include "cursors.h"
+#include "objtypes.h"
+#include "region.h"
+#include "people.h"
+#include "talk.h"
+#include "direct.h"
+#include "sound.h"
+#include "colours.h"
+#include "moreio.h"
+#include "stringy.h"
+
+#include <shellapi.h>
+#include <shlobj.h> // For SHGetFolderPath
+
+#include "..\..\images\resource.h"
+
+HINSTANCE hInst; // Handle of the main instance
+extern HWND hMainWindow;
+
+extern variableStack *noStack;
+
+// The platform-specific functions - Windows edition.
+
+WCHAR *ConvertToUTF16(const char *input) {
+ int s = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, input, -1, NULL, 0);
+ WCHAR *ret = new WCHAR [s];
+ checkNew(ret);
+ /*int a = */MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, input, -1, ret, s);
+ return ret;
+}
+
+char *ConvertFromUTF16(const WCHAR *input) {
+ int s = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL);
+ char *ret = new char [s];
+ checkNew(ret);
+ /*int a = */WideCharToMultiByte(CP_UTF8, 0, input, -1, ret, s, NULL, NULL);
+ return ret;
+}
+
+
+char *grabFileName() {
+ OPENFILENAME ofn;
+ WCHAR path[MAX_PATH];
+ WCHAR file[MAX_PATH] = TEXT("");
+
+ hInst = GetModuleHandle(NULL);
+
+ memset(& ofn, 0, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = NULL;
+ ofn.hInstance = hInst;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.lpstrInitialDir = path;
+ ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER;
+ ofn.lpstrFilter = TEXT("SLUDGE games (*.SLG)\0*.slg\0\0");
+ ofn.lpstrFile = file;
+
+ if (GetOpenFileName(& ofn)) {
+ return ConvertFromUTF16(file);
+ } else {
+ return NULL;
+ }
+}
+
+extern char **languageName;
+extern int *languageTable;
+
+HBITMAP hLogo = NULL;
+extern unsigned char *gameLogo;
+
+BOOL CALLBACK setupDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ switch (message) {
+ case WM_INITDIALOG:
+ if (gameLogo) {
+ hLogo = CreateBitmap(310, 88, 1, 32, gameLogo);
+ SendDlgItemMessage(hDlg, 1003, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hLogo);
+ }
+
+ if (gameSettings.userFullScreen)
+ CheckDlgButton(hDlg, 1000, BST_CHECKED);
+ else
+ CheckDlgButton(hDlg, 1000, BST_UNCHECKED);
+
+ SendDlgItemMessage(hDlg, 1002, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)TEXT("Default (best looking)")));
+ SendDlgItemMessage(hDlg, 1002, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)TEXT("Linear (faster but blurry)")));
+ SendDlgItemMessage(hDlg, 1002, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)TEXT("Off (blocky graphics)")));
+
+ if (gameSettings.antiAlias < 0)
+ SendDlgItemMessage(hDlg, 1002, CB_SETCURSEL, 1, 0);
+ else if (gameSettings.antiAlias)
+ SendDlgItemMessage(hDlg, 1002, CB_SETCURSEL, 0, 0);
+ else
+ SendDlgItemMessage(hDlg, 1002, CB_SETCURSEL, 2, 0);
+
+ if (gameSettings.numLanguages) {
+ WCHAR text[20];
+ for (unsigned int i = 0; i <= gameSettings.numLanguages; i++) {
+ if (languageName[i]) {
+ WCHAR *w_lang = ConvertToUTF16(languageName[i]);
+ SendDlgItemMessage(hDlg, 1001, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)w_lang));
+ delete w_lang;
+ } else {
+ swprintf(text, TEXT("Language %d"), i);
+ SendDlgItemMessage(hDlg, 1001, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)text));
+ }
+ }
+ SendDlgItemMessage(hDlg, 1001, CB_SETCURSEL, getLanguageForFileB(), 0);
+ } else {
+ const WCHAR *text = TEXT("No translations available");
+ SendDlgItemMessage(hDlg, 1001, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)text));
+ SendDlgItemMessage(hDlg, 1001, CB_SETCURSEL, 0, 0);
+ EnableWindow(GetDlgItem(hDlg, 1001), false);
+ }
+ return true;
+
+ case WM_COMMAND:
+ if (hLogo) DeleteObject(hLogo);
+ switch (LOWORD(wParam)) {
+ case IDOK:
+
+ gameSettings.userFullScreen = (IsDlgButtonChecked(hDlg, 1000) == BST_CHECKED);
+ gameSettings.antiAlias = SendDlgItemMessage(hDlg, 1002, CB_GETCURSEL, 0, 0);
+ if (gameSettings.antiAlias == 0) gameSettings.antiAlias = 1;
+ else if (gameSettings.antiAlias == 1) gameSettings.antiAlias = -1;
+ else if (gameSettings.antiAlias == 2) gameSettings.antiAlias = 0;
+
+ if (gameSettings.numLanguages) {
+ gameSettings.languageID = SendDlgItemMessage(hDlg, 1001, CB_GETCURSEL, 0, 0);
+ if (gameSettings.languageID < 0) gameSettings.languageID = 0;
+ gameSettings.languageID = languageTable[gameSettings.languageID];
+ }
+ EndDialog(hDlg, true);
+ return TRUE;
+
+ case IDCANCEL:
+ EndDialog(hDlg, false);
+ return TRUE;
+ }
+ break;
+ }
+ return false;
+}
+
+int showSetupWindow() {
+
+ hInst = GetModuleHandle(NULL);
+
+ if (! hInst) debugOut("ERROR: No hInst!\n");
+
+ if (DialogBox(hInst, TEXT("SETUPWINDOW"), NULL, setupDlgProc)) return true;
+ return false;
+
+}
+
+void msgBox(const char *head, const char *msg) {
+ WCHAR *w_head = ConvertToUTF16(head);
+ WCHAR *w_msg = ConvertToUTF16(msg);
+ MessageBox(NULL, w_msg, w_head, MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL | MB_SETFOREGROUND);
+ delete w_head;
+ delete w_msg;
+}
+
+int msgBoxQuestion(const char *head, const char *msg) {
+ WCHAR *w_head = ConvertToUTF16(head);
+ WCHAR *w_msg = ConvertToUTF16(msg);
+ int val = MessageBox(NULL, w_msg, w_head, MB_YESNO | MB_SETFOREGROUND | MB_APPLMODAL | MB_ICONQUESTION) == IDNO;
+ delete w_head;
+ delete w_msg;
+ if (val)
+ return false;
+ return true;
+}
+
+void changeToUserDir() {
+ TCHAR szAppData[MAX_PATH];
+ /*hr = */SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, szAppData);
+ _wchdir(szAppData);
+}
+
+uint32_t launch(char *f) {
+ WCHAR *w_f = ConvertToUTF16(f);
+ uint32_t r = (uint32_t) ShellExecute(hMainWindow, TEXT("open"), w_f, NULL, TEXT("C:\\"), SW_SHOWNORMAL);
+ delete w_f;
+ return r;
+}
+
+bool defaultUserFullScreen() {
+ return true;
+}
+#endif
diff --git a/engines/sludge/winstuff.h b/engines/sludge/winstuff.h
new file mode 100644
index 0000000000..a934f4479a
--- /dev/null
+++ b/engines/sludge/winstuff.h
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_WINSTUFF_H
+#define SLUDGE_WINSTUFF_H
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#ifndef _WIN32
+#ifndef HINSTANCE
+#define HINSTANCE int
+#endif
+#endif
+
+void setWindowName(const char *tx);
+bool InitApplication(HINSTANCE hInstance);
+bool InitInstance(HINSTANCE hInstance, const char *);
+
+#endif
diff --git a/engines/sludge/yuv.frag b/engines/sludge/yuv.frag
new file mode 100644
index 0000000000..42db4fe098
--- /dev/null
+++ b/engines/sludge/yuv.frag
@@ -0,0 +1,25 @@
+uniform sampler2D Ytex;
+uniform sampler2D Utex;
+uniform sampler2D Vtex;
+
+varying vec2 varCoord;
+
+void main()
+{
+ float y, u, v, r, g, b;
+
+ y=texture2D(Ytex, varCoord).a;
+ u=texture2D(Utex, varCoord).a;
+ v=texture2D(Vtex, varCoord).a;
+
+ y=1.1643*(y-0.0625);
+ u=u-0.5;
+ v=v-0.5;
+
+ r=y+1.5958*v;
+ g=y-0.39173*u-0.81290*v;
+ b=y+2.017*u;
+
+ gl_FragColor=vec4(r,g,b,1.0);
+}
+
diff --git a/engines/sludge/yuv.vert b/engines/sludge/yuv.vert
new file mode 100644
index 0000000000..28613b6c90
--- /dev/null
+++ b/engines/sludge/yuv.vert
@@ -0,0 +1,12 @@
+attribute vec4 myVertex;
+attribute vec2 myUV0;
+
+uniform mat4 myPMVMatrix;
+
+varying vec2 varCoord;
+
+void main()
+{
+ gl_Position = myPMVMatrix * myVertex;
+ varCoord = myUV0.st;
+}
diff --git a/engines/sludge/zbuffer.cpp b/engines/sludge/zbuffer.cpp
new file mode 100644
index 0000000000..6597f3749d
--- /dev/null
+++ b/engines/sludge/zbuffer.cpp
@@ -0,0 +1,205 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#include "common/system.h"
+
+#include "allfiles.h"
+#include "zbuffer.h"
+#include "fileset.h"
+#include "moreio.h"
+#include "newfatal.h"
+#include "graphics.h"
+
+zBufferData zBuffer;
+extern int sceneWidth, sceneHeight;
+
+void killZBuffer() {
+#if 0
+ if (zBuffer.tex) {
+ deleteTextures(1, &zBuffer.texName);
+ zBuffer.texName = 0;
+ delete zBuffer.tex;
+ zBuffer.tex = NULL;
+ }
+ zBuffer.numPanels = 0;
+ zBuffer.originalNum = 0;
+#endif
+}
+
+void sortZPal(int *oldpal, int *newpal, int size) {
+ int i, tmp;
+
+ for (i = 0; i < size; i ++) {
+ newpal[i] = i;
+ }
+
+ if (size < 2) return;
+
+ for (i = 1; i < size; i ++) {
+ if (oldpal[newpal[i]] < oldpal[newpal[i - 1]]) {
+ tmp = newpal[i];
+ newpal[i] = newpal[i - 1];
+ newpal[i - 1] = tmp;
+ i = 0;
+ }
+ }
+}
+
+bool setZBuffer(int y) {
+#if ALLOW_FILE
+ int x, n;
+ uint32_t stillToGo = 0;
+ int yPalette[16], sorted[16], sortback[16];
+
+ killZBuffer();
+
+ setResourceForFatal(y);
+
+ zBuffer.originalNum = y;
+ if (! openFileFromNum(y)) return false;
+ if (fgetc(bigDataFile) != 'S') return fatal("Not a Z-buffer file");
+ if (fgetc(bigDataFile) != 'z') return fatal("Not a Z-buffer file");
+ if (fgetc(bigDataFile) != 'b') return fatal("Not a Z-buffer file");
+
+ switch (fgetc(bigDataFile)) {
+ case 0:
+ zBuffer.width = 640;
+ zBuffer.height = 480;
+ break;
+
+ case 1:
+ zBuffer.width = get2bytes(bigDataFile);
+ zBuffer.height = get2bytes(bigDataFile);
+ break;
+
+ default:
+ return fatal("Extended Z-buffer format not supported in this version of the SLUDGE engine");
+ }
+ if (zBuffer.width != sceneWidth || zBuffer.height != sceneHeight) {
+ char tmp[256];
+ sprintf(tmp, "Z-w: %d Z-h:%d w: %d, h:%d", zBuffer.width, zBuffer.height, sceneWidth, sceneHeight);
+ return fatal("Z-buffer width and height don't match scene width and height", tmp);
+ }
+
+ zBuffer.numPanels = fgetc(bigDataFile);
+ for (y = 0; y < zBuffer.numPanels; y ++) {
+ yPalette[y] = get2bytes(bigDataFile);
+ }
+ sortZPal(yPalette, sorted, zBuffer.numPanels);
+ for (y = 0; y < zBuffer.numPanels; y ++) {
+ zBuffer.panel[y] = yPalette[sorted[y]];
+ sortback[sorted[y]] = y;
+ }
+
+ int picWidth = sceneWidth;
+ int picHeight = sceneHeight;
+ if (! NPOT_textures) {
+ picWidth = getNextPOT(picWidth);
+ picHeight = getNextPOT(picHeight);
+ }
+ zBuffer.tex = new GLubyte [picHeight * picWidth];
+ if (! checkNew(zBuffer.tex)) return false;
+
+ for (y = 0; y < sceneHeight; y ++) {
+ for (x = 0; x < sceneWidth; x ++) {
+ if (stillToGo == 0) {
+ n = fgetc(bigDataFile);
+ stillToGo = n >> 4;
+ if (stillToGo == 15) stillToGo = get2bytes(bigDataFile) + 16l;
+ else stillToGo ++;
+ n &= 15;
+ }
+ zBuffer.tex[y * picWidth + x] = sortback[n] * 16;
+ stillToGo --;
+ }
+ }
+ finishAccess();
+#endif
+ setResourceForFatal(-1);
+#if 0
+ if (! zBuffer.texName) glGenTextures(1, &zBuffer.texName);
+ glBindTexture(GL_TEXTURE_2D, zBuffer.texName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ texImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, picWidth, picHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, zBuffer.tex, zBuffer.texName);
+#endif
+ return true;
+}
+
+void drawZBuffer(int x, int y, bool upsidedown) {
+ int i;
+#if 0
+ if (! zBuffer.tex) return;
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ glDepthMask(GL_TRUE);
+
+ glUseProgram(shader.texture);
+
+ setPMVMatrix(shader.texture);
+
+ //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glBindTexture(GL_TEXTURE_2D, zBuffer.texName);
+
+ setPrimaryColor(1.0, 1.0, 1.0, 1.0);
+
+ for (i = 1; i < zBuffer.numPanels; i++) {
+ GLfloat z = 1.0 - (double) i * (1.0 / 128.0);
+
+ GLfloat vy1 = -y, vy2 = zBuffer.height - y;
+ if (upsidedown) {
+ vy1 += zBuffer.height;
+ vy2 -= zBuffer.height;
+ }
+
+ const GLfloat vertices[] = {
+ (GLfloat) - x, vy1, z,
+ (GLfloat)zBuffer.width - x, vy1, z,
+ (GLfloat) - x, vy2, z,
+ (GLfloat)zBuffer.width - x, vy2, z
+ };
+
+ const GLfloat texCoords[] = {
+ 0.0f, 0.0f,
+ backdropTexW, 0.0f,
+ 0.0f, backdropTexH,
+ backdropTexW, backdropTexH
+ };
+
+ glUniform1i(glGetUniformLocation(shader.texture, "zBuffer"), 1);
+ glUniform1f(glGetUniformLocation(shader.texture, "zBufferLayer"), i);
+
+ drawQuad(shader.texture, vertices, 1, texCoords);
+ glUniform1i(glGetUniformLocation(shader.texture, "zBuffer"), 0);
+ }
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glDepthMask(GL_FALSE);
+ glDisable(GL_BLEND);
+ glUseProgram(0);
+#endif
+}
+
diff --git a/engines/sludge/zbuffer.h b/engines/sludge/zbuffer.h
new file mode 100644
index 0000000000..9040f5764b
--- /dev/null
+++ b/engines/sludge/zbuffer.h
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef SLUDGE_ZBUFFER_H
+#define SLUDGE_ZBUFFER_H
+
+#if 0
+#if !defined(HAVE_GLES2)
+#include "GLee.h"
+#else
+#include <GLES2/gl2.h>
+#endif
+#endif
+
+
+struct zBufferData {
+ int width, height;
+// bool loaded;
+ int numPanels;
+ int panel[16];
+ int originalNum;
+#if 0
+ GLubyte *tex;
+ GLuint texName;
+#endif
+};
+
+
+bool setZBuffer(int y);
+void killZBuffer();
+void drawZBuffer(int x, int y, bool upsidedown);
+
+#endif