--- /dev/null
+/* Reconstructed Commander Keen 4-6 Source Code\r
+ * Copyright (C) 2021 K1n9_Duk3\r
+ *\r
+ * This file is loosely based on:\r
+ * Keen Dreams Source Code\r
+ * Copyright (C) 2014 Javier M. Chavez\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include "CK_DEF.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+ GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean scorescreenkludge;\r
+\r
+/*\r
+=============================================================================\r
+\r
+ LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == EGAGR\r
+\r
+Uint8 starcolors[17] = STARPALETTE;\r
+Uint16 plaquenum[4] = {IDSOFTPIC, PROGTEAMPIC, ARTISTPIC, DIRECTORPIC};\r
+Uint8 termcolors[17] = INTROPALETTE;\r
+Uint8 termcolors2[17] = SHRINKPALETTE;\r
+\r
+Uint8 ortoend[8] = {0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01};\r
+Uint8 andtoend[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE};\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+// uninitialized variables:\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+typedef struct {\r
+ Uint16 height;\r
+ Uint16 width;\r
+ Uint16 rowofs[200];\r
+} shapehead;\r
+\r
+typedef shapehead _seg * shapeseg;\r
+\r
+// text crawl variables:\r
+memptr linecode;\r
+void far *linestarts[200];\r
+Uint16 sourceline[200];\r
+Uint16 masterlines;\r
+void far *routine;\r
+memptr sourcepic;\r
+memptr bittables;\r
+\r
+// terminator intro variables:\r
+shapeseg commander;\r
+shapeseg keen;\r
+shapeseg both;\r
+memptr scaletable;\r
+memptr cmdrshifts[8];\r
+Sint16 commanderbwide;\r
+Uint16 lastsource;\r
+Uint16 keenstart;\r
+memptr basepl[5];\r
+Uint16 baseplwidth[5];\r
+Uint16 baseplheight[5];\r
+memptr plaqueseg;\r
+Uint16 plaquewidth;\r
+Uint16 plaquewidthwords;\r
+Uint16 plaqueheight;\r
+Uint16 plaqueplane;\r
+Uint16 plaquedelta;\r
+Uint16 *shiftptr;\r
+Uint16 planeon;\r
+Sint16 drawheight;\r
+Uint16 source2;\r
+static Uint16 t_dest;\r
+static Sint16 plaque;\r
+static Sint16 plaquephase;\r
+static Sint16 plaquey;\r
+static Sint16 lastframe;\r
+static Sint16 pageon;\r
+static Sint16 prevbottom[2];\r
+Uint16 pageofs;\r
+Uint16 byteadjust;\r
+\r
+#endif // if GRMODE == EGAGR\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= CheckLastScan\r
+=\r
+============================\r
+*/\r
+\r
+void CheckLastScan(void)\r
+{\r
+ if (LastScan)\r
+ {\r
+ if (storedemo)\r
+ {\r
+ playstate = ex_resetgame;\r
+ restartgame = gd_Normal;\r
+ IN_ClearKeysDown();\r
+ NewGame();\r
+ }\r
+#ifndef KEEN6\r
+ else if (LastScan == sc_F1)\r
+ {\r
+ HelpScreens();\r
+ }\r
+#endif\r
+ else\r
+ {\r
+ US_ControlPanel();\r
+ if (restartgame)\r
+ {\r
+ playstate = ex_resetgame;\r
+ }\r
+ else if (loadedgame)\r
+ {\r
+ playstate = ex_loadedgame;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+#if GRMODE == EGAGR\r
+/*\r
+=============================================================================\r
+\r
+ TERMINATOR INTRO\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+============================\r
+=\r
+= LoadPlaque\r
+=\r
+============================\r
+*/\r
+\r
+void LoadPlaque(Sint16 index)\r
+{\r
+ Sint16 LocatePlaque(Sint16 elapsed);\r
+\r
+ Uint16 chunk, picnum, width, height, planesize, i;\r
+ Uint8 far *source;\r
+ Uint16 far *dest;\r
+\r
+ //\r
+ // cache the pic and get pic size\r
+ //\r
+ chunk = plaquenum[index];\r
+ CA_CacheGrChunk(chunk);\r
+ picnum = chunk - STARTPICS;\r
+ baseplwidth[index] = width = pictable[picnum].width;\r
+ baseplheight[index] = height = pictable[picnum].height;\r
+ planesize = width * height * 2;\r
+\r
+ //\r
+ // allocate buffer and convert pic into to our format\r
+ // (convert bytes to word indices for faster shift-drawing)\r
+ //\r
+ MM_GetPtr(&basepl[index], planesize*2); // 2 planes\r
+ source = grsegs[chunk];\r
+ dest = basepl[index];\r
+ for (i=0; i<planesize; i++)\r
+ {\r
+ *dest++ = *source++ << 1;\r
+ }\r
+\r
+ //\r
+ // pic in original format is no longer needed\r
+ //\r
+ MM_FreePtr(&grsegs[chunk]);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= DrawPlaque\r
+=\r
+============================\r
+*/\r
+\r
+void DrawPlaque(Sint16 elapsed, Uint16 x)\r
+{\r
+ Uint16 shift, xb;\r
+ Sint16 y, bottom, oldbottom;\r
+ Uint16 eraseheight, skip, screenoff;\r
+\r
+ shift = x & 7;\r
+ xb = (pageofs + (x / 8)) + (20 - (plaquewidth >> 1));\r
+\r
+ EGAMAPMASK(12); // write to "red" and "intensity" plane (for erasing old pic)\r
+\r
+ //\r
+ // update position (and pic number)\r
+ //\r
+ y = LocatePlaque(elapsed);\r
+\r
+ //\r
+ // erase leftovers of the previous frame\r
+ //\r
+ bottom = y + plaqueheight;\r
+ if (bottom < 0)\r
+ bottom = 0;\r
+ oldbottom = prevbottom[pageon];\r
+ if (bottom < 200 && oldbottom > bottom)\r
+ {\r
+ eraseheight = oldbottom - bottom;\r
+ screenoff = xb + ylookup[bottom];\r
+ asm {\r
+ mov es, screenseg;\r
+ mov bx, linewidth;\r
+ sub bx, plaquewidthwords;\r
+ sub bx, plaquewidthwords;\r
+ mov di, screenoff;\r
+ mov dx, eraseheight;\r
+ mov si, plaquewidthwords;\r
+ xor ax, ax;\r
+ }\r
+eraseloop:\r
+ asm {\r
+ mov cx, si;\r
+ rep stosw;\r
+ add di, bx;\r
+ dec dx;\r
+ jnz eraseloop;\r
+ }\r
+ }\r
+ if (bottom > 200)\r
+ bottom = 200;\r
+ prevbottom[pageon] = bottom;\r
+\r
+ //\r
+ // draw the (new) pic at the new position\r
+ //\r
+ drawheight = plaqueheight;\r
+ skip = 0;\r
+ if (y < 0)\r
+ {\r
+ skip = -y * (plaquewidth << 1);\r
+ drawheight += y;\r
+ y = 0;\r
+ }\r
+ else if (y + plaqueheight > 200)\r
+ {\r
+ drawheight = 200 - y;\r
+ }\r
+ source2 = skip + plaqueplane;\r
+ if (drawheight > 0)\r
+ {\r
+ shiftptr = shifttabletable[shift];\r
+ t_dest = xb + ylookup[y];\r
+ asm {\r
+ mov bx, skip;\r
+ push bp;\r
+ mov bp, shiftptr;\r
+ mov es, screenseg;\r
+ mov ds, plaqueseg;\r
+ mov ah, 4;\r
+ mov BYTE PTR ss:planeon, ah;\r
+ }\r
+planeloop:\r
+ asm {\r
+ mov dx, SC_INDEX;\r
+ mov al, SC_MAPMASK;\r
+ out dx, ax;\r
+ mov dx, ss:drawheight;\r
+ mov di, ss:t_dest;\r
+ }\r
+yloop:\r
+ asm {\r
+ mov cx, ss:plaquewidth;\r
+ xor al, al;\r
+ }\r
+xloop:\r
+ asm {\r
+ mov si, [bx];\r
+ add bx, 2;\r
+ xor ah, ah;\r
+ or ax, [bp+si];\r
+ stosb;\r
+ mov al, ah;\r
+ loop xloop;\r
+ stosb;\r
+ mov WORD PTR es:[di], 0;\r
+ add di, ss:plaquedelta;\r
+ dec dx;\r
+ jnz yloop;\r
+ mov bx, ss:source2;\r
+ mov ah, BYTE PTR ss:planeon;\r
+ shl ah, 1;\r
+ mov BYTE PTR ss:planeon, ah;\r
+ cmp ah, 16;\r
+ jnz planeloop;\r
+ pop bp;\r
+ mov ax, ss;\r
+ mov ds, ax;\r
+ }\r
+ }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= LocatePlaque\r
+=\r
+============================\r
+*/\r
+\r
+Sint16 LocatePlaque(Sint16 elapsed)\r
+{\r
+ switch (plaquephase)\r
+ {\r
+ case -1:\r
+ //\r
+ // pic starts to appear\r
+ //\r
+ plaqueseg = basepl[plaque];\r
+ plaquewidth = baseplwidth[plaque];\r
+ plaquewidthwords = (plaquewidth + 3) >> 1;\r
+ plaqueheight = baseplheight[plaque];\r
+ plaquedelta = linewidth - (plaquewidth + 1);\r
+ plaqueplane = (plaquewidth * plaqueheight) << 1;\r
+ plaquephase++;\r
+ lastframe = elapsed;\r
+ plaquey = 240;\r
+ // no break or return here!\r
+ case 0:\r
+ //\r
+ // pic is moving from the bottom to the center of the screen\r
+ //\r
+ plaquey -= (elapsed - lastframe) << 1;\r
+ if (plaquey < 100)\r
+ {\r
+ plaquey = 100;\r
+ plaquephase++;\r
+ }\r
+ lastframe = elapsed;\r
+ return plaquey - (plaqueheight >> 1);\r
+\r
+ case 1:\r
+ //\r
+ // pic is staying at the center position\r
+ //\r
+ if (elapsed - lastframe > 200)\r
+ {\r
+ plaquephase++;\r
+ lastframe = elapsed;\r
+ }\r
+ return 100 - (plaqueheight >> 1);\r
+\r
+ case 2:\r
+ //\r
+ // pic is moving up from the center to the top of the screen\r
+ //\r
+ plaquey -= (elapsed - lastframe) << 1;\r
+ if (plaquey < -40)\r
+ {\r
+ plaquey = -40;\r
+ if (++plaque < 4)\r
+ {\r
+ plaquephase = -1;\r
+ }\r
+ else\r
+ {\r
+ plaquephase = 3;\r
+ }\r
+ }\r
+ lastframe = elapsed;\r
+ return plaquey - (plaqueheight >> 1);\r
+ }\r
+\r
+ return -40;\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= SlideLetters\r
+=\r
+============================\r
+*/\r
+\r
+void SlideLetters(void)\r
+{\r
+ Sint16 x, cPosX, screenxb;\r
+ Uint16 elapsed, totaltics, dstofs;\r
+ Sint16 cStart, cEnd, cTotalMove;\r
+ Uint16 shift, srcseg, srcofs;\r
+ Sint16 clearleft, copywidth, clearright;\r
+ Uint16 srcdif, dstdif;\r
+ Sint32 now;\r
+\r
+ //\r
+ // set up characteristics of the animation\r
+ //\r
+ EGAWRITEMODE(0);\r
+ EGAREADMAP(0); // useless...\r
+\r
+ keenstart = keen->width + 200;\r
+ EGAREADMAP(1); // also useless ... I think...\r
+\r
+ cEnd = 120 - commander->width;\r
+ cStart = 320;\r
+ cTotalMove = cEnd - cStart;\r
+ totaltics = abs(cTotalMove);\r
+\r
+ pageofs = pageon = 0;\r
+ lasttimecount = TimeCount;\r
+ while (TimeCount == lasttimecount);\r
+ lasttimecount = TimeCount;\r
+\r
+ for (elapsed=0; elapsed <= totaltics; elapsed += tics)\r
+ {\r
+ //\r
+ // draw the credits pic\r
+ //\r
+ x = ((Sint32)keenstart * (Sint32)(totaltics-elapsed)) / (Sint32)totaltics;\r
+ DrawPlaque(elapsed, x);\r
+\r
+ //\r
+ // get ready to draw draw the "COMMANDER" pic\r
+ //\r
+ cPosX = cStart + ((Sint32)cTotalMove * (Sint32)elapsed) / (Sint32)totaltics;\r
+ cPosX += x & 7;\r
+ screenxb = (cPosX + 0x800) / 8 + -0x100;\r
+ shift = (cPosX + 0x800) & 7;\r
+ srcseg = FP_SEG(cmdrshifts[shift]);\r
+ srcofs = 0;\r
+ dstofs = pageofs + x / 8;\r
+ if (screenxb > 0)\r
+ {\r
+ clearleft = (screenxb + 1) / 2;\r
+ if (screenxb & 1)\r
+ dstofs--;\r
+ copywidth = 21 - clearleft;\r
+ clearright = 0;\r
+ }\r
+ else if (-commanderbwide + 40 < screenxb)\r
+ {\r
+ clearleft = 0;\r
+ copywidth = 21;\r
+ clearright = 0;\r
+ srcofs -= screenxb;\r
+ }\r
+ else\r
+ {\r
+ clearleft = 0;\r
+ copywidth = (commanderbwide + screenxb) / 2;\r
+ clearright = 21 - copywidth;\r
+ srcofs -= screenxb;\r
+ }\r
+ srcdif = commanderbwide - copywidth*2;\r
+ dstdif = 248 - (clearleft + copywidth + clearright)*2;\r
+\r
+ //\r
+ // draw "COMMANDER" pic\r
+ //\r
+ EGAMAPMASK(2);\r
+\r
+ asm {\r
+ mov di, dstofs;\r
+ mov es, screenseg;\r
+ mov si, srcofs;\r
+ mov lastsource, si;\r
+ mov ds, srcseg;\r
+ mov dx, 200;\r
+ }\r
+yloop:\r
+ asm {\r
+ xor ax, ax;\r
+ mov cx, clearleft;\r
+ rep stosw;\r
+ mov cx, copywidth;\r
+ rep movsw;\r
+ xor ax, ax;\r
+ mov cx, clearright;\r
+ rep stosw;\r
+ test dx, 1;\r
+ jnz oddline;\r
+ mov si, ss:lastsource;\r
+ jmp nextline;\r
+ }\r
+oddline:\r
+ asm {\r
+ add si, srcdif;\r
+ mov ss:lastsource, si;\r
+ }\r
+nextline:\r
+ asm {\r
+ add di, dstdif;\r
+ dec dx;\r
+ jnz yloop;\r
+ mov ax, ss;\r
+ mov ds, ax;\r
+ }\r
+\r
+ //\r
+ // page flip\r
+ //\r
+ VW_SetScreen(pageofs + x / 8, x & 7);\r
+ pageon ^= 1;\r
+ if (pageon)\r
+ {\r
+ pageofs = 124;\r
+ }\r
+ else\r
+ {\r
+ pageofs = 0;\r
+ }\r
+\r
+ //\r
+ // handle timing\r
+ //\r
+ do\r
+ {\r
+ now = TimeCount;\r
+ tics = now - lasttimecount;\r
+ } while (tics < 2);\r
+ lasttimecount = now;\r
+\r
+ //\r
+ // handle input\r
+ //\r
+ if (IN_IsUserInput() && LastScan != sc_F1)\r
+ {\r
+ LastScan = sc_Space;\r
+ }\r
+ if (LastScan)\r
+ return;\r
+ }\r
+\r
+ byteadjust = x / 8;\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= DrawScan\r
+=\r
+============================\r
+*/\r
+\r
+void DrawScan(Sint16 far *source, Uint8 far *dest)\r
+{\r
+ register Uint16 x;\r
+ register Sint16 w;\r
+ register Uint16 val;\r
+ register Uint16 i;\r
+\r
+ val = x = 0;\r
+ for (;;)\r
+ {\r
+ //\r
+ // first part: puts black pixels (<width> pixels wide)\r
+ //\r
+ w = *source++;\r
+ if (w == -1)\r
+ {\r
+ *dest++ = val;\r
+ *dest = 0;\r
+ return;\r
+ }\r
+\r
+ x += w;\r
+ if (x > 7)\r
+ {\r
+ *dest++ = val;\r
+ val = 0;\r
+ i = (x / 8) - 1;\r
+ while (i--)\r
+ {\r
+ *dest++ = 0;\r
+ }\r
+ x &= 7;\r
+ }\r
+\r
+ //\r
+ // second part: puts white pixels (<width> pixels wide)\r
+ //\r
+ w = *source++;\r
+ if (w == -1)\r
+ {\r
+ *dest++ = val;\r
+ *dest = 0;\r
+ return;\r
+ }\r
+\r
+ val |= ortoend[x];\r
+ x += w;\r
+ if (x > 7)\r
+ {\r
+ *dest++ = val;\r
+ val = 0xFF;\r
+ i = (x / 8) - 1;\r
+ while (i--)\r
+ {\r
+ *dest++ = 0xFF;\r
+ }\r
+ x &= 7;\r
+ }\r
+ val &= andtoend[x];\r
+ }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= BuildScaleShape\r
+=\r
+============================\r
+*/\r
+\r
+void BuildScaleShape(void)\r
+{\r
+ Sint16 px, w;\r
+ Sint16 far *source;\r
+ Sint16 far *dest;\r
+ Sint16 y;\r
+\r
+ MM_GetPtr((memptr*)&both, 30000);\r
+ dest = MK_FP(FP_SEG(both), sizeof(shapehead));\r
+\r
+ for (y=0; y<200; y++)\r
+ {\r
+ both->rowofs[y] = FP_OFF(dest);\r
+ px = 0;\r
+\r
+ EGAREADMAP(1); // this is pretty useless, we're not reading from EGA memory here\r
+\r
+ source = (Sint16 far *)((byte _seg *)commander + commander->rowofs[y]);\r
+ w = *source++;\r
+ do\r
+ {\r
+ *dest++ = px;\r
+ px = px + w;\r
+ w = *source++;\r
+ } while (w != -1);\r
+\r
+ //\r
+ // insert an 80 pixel gap between "COMMANDER" and "KEEN"\r
+ //\r
+ // This assumes that the rightmost column(s) of the "COMMANDER"\r
+ // shape are black. Otherwise the gap would be filled with\r
+ // white pixels and the "KEEN" image would use inverted colors\r
+ // as a result.\r
+ //\r
+ px += 80;\r
+\r
+ EGAREADMAP(0); // this is pretty useless, we're not reading from EGA memory here\r
+\r
+ source = (Sint16 far *)((byte _seg *)keen + keen->rowofs[y]);\r
+ source++; // kludgy bit, causes errors when left egde of "KEEN" is no rectangle\r
+ w = *source++;\r
+ do\r
+ {\r
+ *dest++ = px;\r
+ px = px + w;\r
+ w = *source++;\r
+ } while (w != -1);\r
+\r
+ *dest++ = px; // put last value\r
+ *dest++ = -1; // put end-of-line\r
+ }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= ScalePointScan\r
+=\r
+============================\r
+*/\r
+\r
+void ScalePointScan(Sint16 far *rowptr, Sint16 y, Sint16 toleft, Sint16 far *scaletable)\r
+{\r
+ Uint8 far *dest;\r
+ Sint16 left, endx;\r
+ Uint16 w, val, x, right;\r
+ register Sint16 px, sx;\r
+\r
+ val = x = 0;\r
+ endx = 320 - toleft;\r
+ dest = MK_FP(0xA000, pageofs + byteadjust + ylookup[y]);\r
+\r
+ if (toleft < 0)\r
+ {\r
+ left = -toleft;\r
+ val = 0;\r
+ x = 0;\r
+\r
+ for (;;)\r
+ {\r
+ px = *rowptr++;\r
+ sx = scaletable[px];\r
+ if (sx > left)\r
+ goto drawwhite;\r
+\r
+ px = *rowptr++;\r
+ sx = scaletable[px];\r
+ if (sx > left)\r
+ goto drawblack;\r
+ }\r
+ }\r
+\r
+ //\r
+ // regular\r
+ //\r
+ val = 0;\r
+ x = toleft & 7;\r
+ dest += (toleft >> 3);\r
+ left = 0;\r
+ rowptr++; // the first value is always 0, we need the next value\r
+drawloop:\r
+ px = *rowptr++;\r
+ sx = scaletable[px];\r
+\r
+ //\r
+ // draw/add black pixels\r
+ //\r
+drawblack:\r
+ w = sx - left;\r
+ left = sx;\r
+ x += w;\r
+ if (x > 7)\r
+ {\r
+ asm {\r
+ les di, dest;\r
+ mov al, BYTE PTR val;\r
+ stosb;\r
+ mov cx, x;\r
+ shr cx, 1;\r
+ shr cx, 1;\r
+ shr cx, 1;\r
+ dec cx;\r
+ xor al, al;\r
+ mov BYTE PTR val, al;\r
+ rep stosb;\r
+ and x, 7;\r
+ mov WORD PTR dest, di;\r
+ }\r
+ }\r
+\r
+ //\r
+ // stop if the right side of the screen is reached\r
+ //\r
+ if (sx > endx)\r
+ return;\r
+\r
+ //\r
+ // stop if the end of the image row is reached\r
+ // \r
+ // This is only checked after drawing the black part, so the\r
+ // combined shape must not end with white pixels on the right.\r
+ // That means the rightmost column(s) of the "KEEN" shape must\r
+ // always be black.\r
+ //\r
+ px = *rowptr++;\r
+ if (px == -1)\r
+ goto clearright;\r
+\r
+ sx = scaletable[px];\r
+\r
+ //\r
+ // draw/add white pixels\r
+ //\r
+drawwhite:\r
+ w = sx - left;\r
+ left = sx;\r
+ val |= ortoend[x];\r
+ x += w;\r
+ if (x > 7)\r
+ {\r
+ asm {\r
+ les di, dest;\r
+ mov al, BYTE PTR val;\r
+ stosb;\r
+ mov cx, x;\r
+ shr cx, 1;\r
+ shr cx, 1;\r
+ shr cx, 1;\r
+ dec cx;\r
+ mov al, 255;\r
+ mov BYTE PTR val, al;\r
+ rep stosb;\r
+ and x, 7;\r
+ mov WORD PTR dest, di;\r
+ }\r
+ }\r
+\r
+ //\r
+ // stop if the right side of the screen is reached\r
+ //\r
+ if (sx > endx)\r
+ return;\r
+\r
+ val &= andtoend[x];\r
+ goto drawloop;\r
+\r
+ //\r
+ // clear the right side of the screen\r
+ //\r
+clearright:\r
+ w = 320 - left;\r
+ x += w;\r
+ if (x > 7)\r
+ {\r
+ *dest++ = val;\r
+ val = 0;\r
+ right = x / 8 - 1;\r
+ while (right--)\r
+ {\r
+ *dest++ = 0;\r
+ }\r
+ x &= 7;\r
+ return;\r
+ }\r
+ return;\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= ScaleDown\r
+=\r
+============================\r
+*/\r
+\r
+void ScaleDown(void)\r
+{\r
+ Uint16 i;\r
+ Uint16 toleft, ticselapsed, ticstotal, scale, endscale, rownum, rowinc;\r
+ Sint32 now;\r
+ Sint16 far *rowptr;\r
+ Uint16 scaleheight, top, bottom, lastbottom[2];\r
+ Sint32 leftorigin;\r
+\r
+ //\r
+ // set our new palette\r
+ //\r
+ SetPalette(termcolors2);\r
+\r
+ EGAREADMAP(1); // this is pretty useless, we're not reading from EGA memory here\r
+\r
+ leftorigin = 120l - commander->width;\r
+ BuildScaleShape();\r
+ MM_GetPtr(&scaletable, 2500*sizeof(Uint16));\r
+\r
+ scale = 0x100; // 100%\r
+ endscale = 0x21; // 13% (scale from 200px to 26px)\r
+ endscale = 0x21; // redundant\r
+ lastbottom[0] = lastbottom[1] = 200;\r
+ ticselapsed = 1;\r
+ ticstotal = 30; // time for the whole shrinking animation\r
+\r
+ while (ticselapsed <= ticstotal)\r
+ {\r
+ //\r
+ // get current scaling\r
+ //\r
+ if (ticselapsed == ticstotal)\r
+ {\r
+ scale = endscale;\r
+ toleft = 0;\r
+ top = 4;\r
+ }\r
+ else\r
+ {\r
+ scale = 0x100 - ((0x100-endscale) * ticselapsed) / ticstotal;\r
+ toleft = (leftorigin * (ticstotal - ticselapsed)) / ticstotal;\r
+ top = (ticselapsed * 4) / ticstotal;\r
+ }\r
+\r
+ //\r
+ // build scale table: scaletable[i] = (i*scale) / 0x100;\r
+ //\r
+ asm {\r
+ xor ax, ax;\r
+ xor dx, dx;\r
+ mov cx, 2500;\r
+ mov bx, scale;\r
+ mov es, scaletable;\r
+ xor di, di;\r
+ }\r
+l1:\r
+ asm {\r
+ mov es:[di], ah;\r
+ inc di;\r
+ mov es:[di], dl;\r
+ inc di;\r
+ add ax, bx;\r
+ adc dx, 0;\r
+ loop l1;\r
+ }\r
+\r
+ //\r
+ // wait... didn't we already do this?\r
+ //\r
+ if (ticselapsed == ticstotal)\r
+ {\r
+ toleft = 0;\r
+ }\r
+ else\r
+ {\r
+ toleft = (leftorigin * (ticstotal - ticselapsed)) / ticstotal;\r
+ }\r
+\r
+ //\r
+ // prepare scaled drawing process\r
+ //\r
+ scaleheight = ((Sint16 _seg *)scaletable)[200];\r
+ rownum = 0;\r
+ rowinc = 0x10000l / scale;\r
+ bufferofs = pageofs + byteadjust;\r
+\r
+ //\r
+ // erase stuff at the top\r
+ //\r
+ if (top > 0)\r
+ {\r
+ VW_Bar(0, 0, 320, top, BLACK);\r
+ }\r
+\r
+ //\r
+ // draw the scaled shape\r
+ //\r
+ EGAWRITEMODE(0);\r
+ EGAMAPMASK(15);\r
+\r
+ for (i=0; i<scaleheight; i++)\r
+ {\r
+ rowptr = (Sint16 far *)((byte _seg *)both + both->rowofs[rownum >> 8]);\r
+ ScalePointScan(rowptr, i+top, toleft, scaletable);\r
+\r
+ rownum += rowinc;\r
+ }\r
+\r
+ //\r
+ // erase leftovers at the bottom of the screen\r
+ //\r
+ bufferofs = pageofs + byteadjust;\r
+ bottom = scaleheight + top;\r
+ if (lastbottom[pageon] > bottom)\r
+ {\r
+ VW_Bar(0, bottom, 320, lastbottom[pageon] - bottom, BLACK);\r
+ lastbottom[pageon] = bottom;\r
+ }\r
+\r
+ //\r
+ // page flip\r
+ //\r
+ VW_SetScreen(pageofs+byteadjust, 0);\r
+ pageon ^= 1;\r
+ if (pageon)\r
+ {\r
+ pageofs = 124;\r
+ }\r
+ else\r
+ {\r
+ pageofs = 0;\r
+ }\r
+\r
+ //\r
+ // handle timing\r
+ //\r
+ now = TimeCount;\r
+ tics = now - lasttimecount;\r
+ lasttimecount = now;\r
+ if (tics > 8)\r
+ tics = 8; // don't skip too many frames on slow systems\r
+\r
+ if (ticselapsed == ticstotal)\r
+ break;\r
+\r
+ ticselapsed += tics;\r
+ if (ticselapsed > ticstotal)\r
+ ticselapsed = ticstotal;\r
+\r
+ //\r
+ // handle input\r
+ //\r
+ if (IN_IsUserInput() && LastScan != sc_F1)\r
+ {\r
+ LastScan = sc_Space;\r
+ }\r
+ if (LastScan)\r
+ return; // BUG: buffers aren't freed!\r
+ }\r
+\r
+ //\r
+ // free the buffers\r
+ //\r
+ MM_FreePtr(&scaletable);\r
+ MM_FreePtr((memptr*)&both);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= FinishPage\r
+=\r
+============================\r
+*/\r
+\r
+void FinishPage(void)\r
+{\r
+ Sint16 swap, temp, i, n, x, y;\r
+ Uint16 ofs;\r
+ Sint16 top, bottom, delta;\r
+ Uint8 bitmask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};\r
+ Sint16 xtable[320], ytable[200];\r
+\r
+ //\r
+ // build lookup tables\r
+ //\r
+ for (i=0; i<320; i++)\r
+ {\r
+ xtable[i] = i;\r
+ }\r
+ for (i=0; i<320; i++)\r
+ {\r
+ swap = random(320);\r
+ temp = xtable[swap];\r
+ xtable[swap] = xtable[i];\r
+ xtable[i] = temp;\r
+ }\r
+ for (i=0; i<200; i++)\r
+ {\r
+ ytable[i] = xtable[i];\r
+ }\r
+\r
+ //\r
+ // set up display\r
+ //\r
+ VW_SetDefaultColors();\r
+ if (pageon)\r
+ {\r
+ bufferofs = byteadjust + 124;\r
+ displayofs = byteadjust;\r
+ }\r
+ else\r
+ {\r
+ bufferofs = byteadjust;\r
+ displayofs = byteadjust + 124;\r
+ }\r
+ VW_SetScreen(displayofs, 0);\r
+\r
+ //\r
+ // draw title pic to the non-displayed buffer\r
+ //\r
+ VW_DrawPic(0, 0, TITLEPICPIC);\r
+\r
+ //\r
+ // copy "random" pixels from the non-displayed area\r
+ // into the displayed area to create the "fizzle" effect\r
+ //\r
+ delta = displayofs - bufferofs;\r
+\r
+ //\r
+ // set ES register for the pixel copying code in the loops\r
+ //\r
+ // This is faster than setting the ES register in the loops,\r
+ // but you need to make sure nothing in the loops overwrites\r
+ // the ES register, otherwise the code won't work correctly.\r
+ //\r
+ asm mov es, screenseg;\r
+\r
+ for (i = 0; i< 360; i++)\r
+ {\r
+ top = i - 160;\r
+ if (top < 0)\r
+ top = 0;\r
+\r
+ bottom = i;\r
+ if (bottom >= 200)\r
+ bottom = 199;\r
+\r
+ for (y = top; y <= bottom; y++)\r
+ {\r
+ ofs = bufferofs + ylookup[y];\r
+ for (n=0; n<2; n++)\r
+ {\r
+ x = xtable[ytable[y]];\r
+ if (++ytable[y] == 320)\r
+ {\r
+ ytable[y] = 0;\r
+ }\r
+\r
+ //\r
+ // set bitmask for our x value\r
+ //\r
+ asm mov cx, x;\r
+ asm mov si, cx;\r
+ asm and si, 7;\r
+ asm cli;\r
+ asm mov dx, GC_INDEX;\r
+ asm mov al, GC_BITMASK;\r
+ asm mov ah, BYTE PTR bitmask[si];\r
+ asm out dx, ax;\r
+ asm sti;\r
+\r
+ //\r
+ // set up source and dest index registers\r
+ //\r
+ asm mov si, ofs;\r
+ asm shr cx, 1;\r
+ asm shr cx, 1;\r
+ asm shr cx, 1;\r
+ asm add si, cx;\r
+ asm mov di, si;\r
+ asm add di, delta;\r
+\r
+ //\r
+ // copy the pixel data (all 4 planes)\r
+ //\r
+ // "blue" plane:\r
+ asm mov dx, SC_INDEX;\r
+ asm mov ax, SC_MAPMASK + 1*256;\r
+ asm out dx, ax;\r
+ asm mov dx, GC_INDEX;\r
+ asm mov ax, GC_READMAP + 0*256;\r
+ asm out dx, ax;\r
+ asm mov bl, es:[si];\r
+ asm xchg bl, es:[di];\r
+ // "green" plane:\r
+ asm mov dx, SC_INDEX;\r
+ asm mov ax, SC_MAPMASK + 2*256;\r
+ asm out dx, ax;\r
+ asm mov dx, GC_INDEX;\r
+ asm mov ax, GC_READMAP + 1*256;\r
+ asm out dx, ax;\r
+ asm mov bl, es:[si];\r
+ asm xchg bl, es:[di];\r
+ // "red" plane:\r
+ asm mov dx, SC_INDEX;\r
+ asm mov ax, SC_MAPMASK + 4*256;\r
+ asm out dx, ax;\r
+ asm mov dx, GC_INDEX;\r
+ asm mov ax, GC_READMAP + 2*256;\r
+ asm out dx, ax;\r
+ asm mov bl, es:[si];\r
+ asm xchg bl, es:[di];\r
+ // "intensity" plane:\r
+ asm mov dx, SC_INDEX;\r
+ asm mov ax, SC_MAPMASK + 8*256;\r
+ asm out dx, ax;\r
+ asm mov dx, GC_INDEX;\r
+ asm mov ax, GC_READMAP + 3*256;\r
+ asm out dx, ax;\r
+ asm mov bl, es:[si];\r
+ asm xchg bl, es:[di];\r
+ }\r
+ }\r
+\r
+ VW_WaitVBL(1); // so the fizzle animation won't go super fast\r
+\r
+ if (IN_IsUserInput() && LastScan != sc_F1)\r
+ {\r
+ LastScan = sc_Space;\r
+ }\r
+ if (LastScan)\r
+ {\r
+ EGABITMASK(0xFF);\r
+ EGAMAPMASK(15);\r
+ return;\r
+ }\r
+ }\r
+\r
+ //\r
+ // clean up EGA registers\r
+ //\r
+ EGABITMASK(0xFF);\r
+ EGAMAPMASK(15);\r
+\r
+ //\r
+ // pause for 6 seconds\r
+ //\r
+ IN_UserInput(6 * TickBase, false);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= Terminator\r
+=\r
+============================\r
+*/\r
+\r
+void Terminator(void)\r
+{\r
+ Uint16 i, shift, bufsize;\r
+ Sint16 far *source;\r
+ Uint8 far *dest;\r
+ Uint16 srcseg, destseg;\r
+ boolean pagefinished;\r
+ Uint16 rowofs[200];\r
+\r
+ pagefinished = false;\r
+ CA_SetAllPurge();\r
+ SetPaletteEx(colors[0]); // all black\r
+ VW_ClearVideo(BLACK);\r
+ VW_SetLineWidth(248); // 1984 pixels total, we're using 992 per "page"\r
+\r
+ CA_CacheGrChunk(TITLEPICPIC);\r
+ CA_CacheGrChunk(BIGCOMMANDER);\r
+ CA_CacheGrChunk(BIGKEEN);\r
+ keen = grsegs[BIGKEEN];\r
+ commander = grsegs[BIGCOMMANDER];\r
+\r
+ EGAMAPMASK(1);\r
+\r
+ keenstart = keen->width + 200;\r
+ VW_SetScreen((keenstart/8)+1, 0);\r
+\r
+ //\r
+ // draw the "KEEN" pic (to first "page")\r
+ //\r
+ for (i=0; i<200; i++)\r
+ {\r
+ source = (Sint16 far *)((byte _seg *)keen + keen->rowofs[i]);\r
+ dest = MK_FP(0xA000, ylookup[i]);\r
+ dest += 25; // 25 bytes -> 200 pixels\r
+ DrawScan(source, dest);\r
+ }\r
+ //\r
+ // copy pic from first "page" to second "page"\r
+ //\r
+ VW_ScreenToScreen(0, 124, 109, 200);\r
+\r
+ //\r
+ // create pre-shifted image buffers for the "COMMANDER" pic\r
+ // (only 100 pixels high instead of 200 pixels to save memory)\r
+ //\r
+ commanderbwide = (commander->width + 7) / 8;\r
+ commanderbwide = (commanderbwide + 3) & ~1;\r
+ bufsize = commanderbwide * 100; // half height\r
+ for (shift = 0; shift < 8; shift++)\r
+ {\r
+ MM_GetPtr(&cmdrshifts[shift], bufsize);\r
+ }\r
+\r
+ //\r
+ // re-assign shape pointers (memory manager might have moved the buffers)\r
+ //\r
+ keen = grsegs[BIGKEEN];\r
+ commander = grsegs[BIGCOMMANDER];\r
+\r
+ //\r
+ // draw the first (unshifted) version of the "COMMANDER" pic to the buffer\r
+ //\r
+ for (i=0; i<100; i++)\r
+ {\r
+ rowofs[i*2] = rowofs[i*2+1] = i * commanderbwide;\r
+ source = (Sint16 far *)((byte _seg *)commander + commander->rowofs[i*2]);\r
+ dest = (Uint8 _seg *)cmdrshifts[0] + rowofs[i*2];\r
+ DrawScan(source, dest);\r
+ }\r
+\r
+ //\r
+ // create the shifted versions of the "COMMANDER" pic\r
+ //\r
+ for (shift = 1; shift < 8; shift++)\r
+ {\r
+ srcseg = FP_SEG(cmdrshifts[shift-1]);\r
+ destseg = FP_SEG(cmdrshifts[shift]);\r
+ asm {\r
+ mov ds, srcseg;\r
+ mov es, destseg;\r
+ mov cx, bufsize;\r
+ clc;\r
+ xor si, si;\r
+ xor di, di;\r
+ }\r
+l1:\r
+ asm {\r
+ lodsb;\r
+ rcr al, 1;\r
+ stosb;\r
+ loop l1;\r
+ mov ax, ss;\r
+ mov ds, ax;\r
+ }\r
+ }\r
+\r
+ //\r
+ // prepare (and set) the palettes\r
+ //\r
+ termcolors[16] = termcolors2[16] = termcolors[16] = bordercolor;\r
+ SetPalette(termcolors);\r
+\r
+ //\r
+ // cache the credits pics (they are converted into a special\r
+ // format to make shifted drawing easier during the animation)\r
+ //\r
+ for (i=0; i<4; i++)\r
+ {\r
+ LoadPlaque(i);\r
+ }\r
+\r
+ //\r
+ // play the animation\r
+ //\r
+ plaque = lastframe = 0;\r
+ plaquephase = -1;\r
+ SlideLetters();\r
+\r
+ //\r
+ // free some of the buffers\r
+ // (shrink animation needs additional memory)\r
+ //\r
+ for (i=0; i<4; i++)\r
+ {\r
+ MM_FreePtr(&basepl[i]);\r
+ }\r
+ for (shift=0; shift<8; shift++)\r
+ {\r
+ MM_FreePtr(&cmdrshifts[shift]);\r
+ }\r
+\r
+ //\r
+ // do the shrinking and fizzle fade animations\r
+ // (if intro wasn't aborted)\r
+ //\r
+ if (!LastScan)\r
+ {\r
+ ScaleDown();\r
+ }\r
+\r
+ if (!LastScan)\r
+ {\r
+ FinishPage();\r
+ pagefinished = true;\r
+ }\r
+\r
+ //\r
+ // free the remaining buffers\r
+ //\r
+ MM_SetPurge(&grsegs[BIGCOMMANDER], 3);\r
+ MM_SetPurge(&grsegs[BIGKEEN], 3);\r
+\r
+ //\r
+ // switch back to default graphics settings\r
+ //\r
+ VW_ClearVideo(BLACK);\r
+ VW_SetLineWidth(SCREENWIDTH);\r
+ VW_SetDefaultColors();\r
+ RF_FixOfs();\r
+ CA_ClearMarks();\r
+\r
+ //\r
+ // handle input and main menu stuff\r
+ //\r
+ if (LastScan == sc_None)\r
+ {\r
+ return;\r
+ }\r
+#ifndef KEEN6\r
+ if (LastScan == sc_F1)\r
+ {\r
+ HelpScreens();\r
+ return;\r
+ }\r
+#endif\r
+ if (!pagefinished)\r
+ {\r
+ RF_FixOfs(); //redundant\r
+ CA_CacheGrChunk(TITLEPICPIC);\r
+ VW_DrawPic(0, 0, TITLEPICPIC);\r
+ VW_SetScreen(bufferofs, 0);\r
+ IN_Ack();\r
+ CA_ClearMarks();\r
+ if (storedemo)\r
+ {\r
+ playstate = ex_resetgame;\r
+ restartgame = gd_Normal;\r
+ IN_ClearKeysDown();\r
+ NewGame();\r
+ return;\r
+ }\r
+ }\r
+\r
+ US_ControlPanel();\r
+ if (restartgame)\r
+ {\r
+ playstate = ex_resetgame;\r
+ }\r
+ else if (loadedgame)\r
+ {\r
+ playstate = ex_loadedgame;\r
+ }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+ STAR WARS TEXT CRAWL\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+============================\r
+=\r
+= BuildBitTables\r
+=\r
+============================\r
+*/\r
+\r
+void BuildBitTables(void)\r
+{\r
+ Uint16 bit1, bit2, i;\r
+ Uint8 far *buffer;\r
+\r
+ MM_GetPtr(&bittables, 0x4000);\r
+ buffer = bittables;\r
+\r
+ //\r
+ // generate a lookup table that maps the bits of the "texture" (bit1)\r
+ // to the appropriate bit for the screen position (bit2) to make the\r
+ // scaler code faster and smaller\r
+ //\r
+ // table[((7-b1)*8+(7-b2))*256+i] = (i & (1 << (7-b1))) ? (1 << (7-b2)) : 0;\r
+ //\r
+ for (bit1 = 1; bit1 < 0x100; bit1 <<= 1)\r
+ {\r
+ for (bit2 = 1; bit2 < 0x100; bit2 <<= 1)\r
+ {\r
+ for (i = 0; i < 0x100; i++, buffer++)\r
+ {\r
+ if (i & bit1)\r
+ {\r
+ *buffer = bit2;\r
+ }\r
+ else\r
+ {\r
+ *buffer = 0;\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= CompileSWUpdate\r
+=\r
+============================\r
+*/\r
+\r
+void CompileSWUpdate(void)\r
+{\r
+ Sint16 y;\r
+ Uint16 i, width, scalestep, step;\r
+ Sint32 scale, rowof, xpos, size;\r
+ void far *buffer;\r
+ Uint8 srcoff, srcbit, bitpos;\r
+ Uint16 destoff, srcx, left, orindex, lastoff;\r
+\r
+ BuildBitTables();\r
+ size = 190000;\r
+ MM_GetPtr(&linecode, size);\r
+ buffer = linecode;\r
+ //\r
+ // Note: You should really lock the pointer to prevent the memmory manager\r
+ // from moving the buffer around. This code stores a bunch of pointers to\r
+ // this memory block in the linestarts array. Those pointers will not be\r
+ // updated when the memory manager moves the buffer around and the game\r
+ // might end up crashing (or worse) when trying to run the "code" at the\r
+ // memory location after the data was moved. The game starts playing music\r
+ // after this function is done, which may or may not cause the memory\r
+ // manager to move memory blocks around.\r
+ //\r
+\r
+ //\r
+ // move the buffer address into ES:DI (and keep it there)\r
+ //\r
+ asm mov es, WORD PTR buffer+2;\r
+ asm mov di, WORD PTR buffer;\r
+ //\r
+ // Since the address is kept in ES:DI, we must save and restore\r
+ // the ES register when calling other functions (push es / pop es).\r
+ // The Borland C compiler always saves and restores the DI register\r
+ // when a function modifies it, so we don't need to worry about\r
+ // that register. This is a bit of an ugly hack, but it makes this\r
+ // code a little faster and smaller.\r
+ //\r
+\r
+ scale = 320l << 11;\r
+ scalestep = (((Uint32)(320-40) << 11) / 200); // roughly 1.4 pixels per step, going from 320 pixels to 40 pixels in 200 steps\r
+ rowof = 0;\r
+\r
+ for (y=199; y >= 0; y--)\r
+ {\r
+ //\r
+ // draw a blue line for the current row\r
+ //\r
+ asm push es;\r
+ VW_Hlin(0, 320, y, BLUE);\r
+ asm pop es;\r
+\r
+ //\r
+ // update the buffer variable with the current (normalized) ES:DI address\r
+ //\r
+ asm mov WORD PTR buffer, di;\r
+ asm mov WORD PTR buffer+2, es;\r
+\r
+ //\r
+ // store the address in the scaler lookup table\r
+ //\r
+ linestarts[y] = buffer;\r
+\r
+ //\r
+ // get current scaling factors\r
+ //\r
+ width = ((Uint16)((scale/2) >> 11)) << 1; // some trickery to make sure width is even\r
+ sourceline[y] = (rowof >> 11);\r
+ step = (336l << 11) / width;\r
+ xpos = 0;\r
+ rowof += step;\r
+ left = 160 - (width >> 1);\r
+ destoff = ylookup[y] + left / 8;\r
+ bitpos = left & 7;\r
+\r
+ //\r
+ // generate the machine code\r
+ //\r
+ // MOV CX, SS\r
+ // MOV SS, AX\r
+ // ADD DI, <destoff>\r
+ // XOR AL, AL\r
+ //\r
+ asm mov ax, 0D18Ch;\r
+ asm stosw;\r
+ asm mov ax, 0D08Eh;\r
+ asm stosw;\r
+ asm mov ax, 0C781h;\r
+ asm stosw;\r
+ asm mov ax, destoff;\r
+ asm stosw;\r
+ asm mov ax, 0C030h;\r
+ asm stosw;\r
+\r
+ lastoff = -1;\r
+ for (i=0; i<width; i++)\r
+ {\r
+ srcx = (xpos >> 11);\r
+ srcoff = (srcx / 8);\r
+ srcbit = srcx & 7;\r
+\r
+ orindex = ((7-srcbit)*8 + 7-bitpos) << 8;\r
+ if (srcoff != lastoff)\r
+ {\r
+ //\r
+ // MOV BL, [BP + <srcoff>]\r
+ //\r
+ asm mov ax, 5E8Ah;\r
+ asm stosw;\r
+ asm mov al, srcoff;\r
+ asm stosb;\r
+\r
+ lastoff = srcoff;\r
+ }\r
+\r
+ //\r
+ // OR AL, [BX + <orindex>]\r
+ //\r
+ asm mov ax, 870Ah;\r
+ asm stosw;\r
+ asm mov ax, orindex;\r
+ asm stosw;\r
+\r
+ bitpos++;\r
+ if (bitpos == 8)\r
+ {\r
+ bitpos = 0;\r
+\r
+ //\r
+ // STOSB\r
+ // XOR AL, AL\r
+ //\r
+ asm mov ax, 30AAh;\r
+ asm stosw;\r
+ asm mov al, 0C0h;\r
+ asm stosb;\r
+ }\r
+\r
+ xpos += step;\r
+ }\r
+\r
+ if (bitpos)\r
+ {\r
+ //\r
+ // STOSB\r
+ //\r
+ asm mov al, 0AAh;\r
+ asm stosb;\r
+ }\r
+ //\r
+ // generate end of subroutine\r
+ //\r
+ // MOV SS, CX\r
+ // RETF\r
+ //\r
+ asm mov ax, 0D18Eh;\r
+ asm stosw;\r
+ asm mov al, 0CBh;\r
+ asm stosb;\r
+\r
+ //\r
+ // normalize ES:DI\r
+ //\r
+ asm mov ax, di;\r
+ asm shr ax, 1;\r
+ asm shr ax, 1;\r
+ asm shr ax, 1;\r
+ asm shr ax, 1;\r
+ asm mov bx, es;\r
+ asm add ax, bx;\r
+ asm mov es, ax;\r
+ asm and di, 0Fh;\r
+\r
+ //\r
+ // update scale value for next row\r
+ //\r
+ scale -= scalestep;\r
+\r
+ //\r
+ // replace the blue line with the row from the background image\r
+ //\r
+ asm push es;\r
+ VW_ScreenToScreen(ylookup[y] + 0x8000, ylookup[y], 40, 1);\r
+ asm pop es;\r
+\r
+ if (LastScan)\r
+ return;\r
+ }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= TranslateString\r
+=\r
+============================\r
+*/\r
+\r
+void TranslateString(char *text)\r
+{\r
+ char c;\r
+\r
+ while (*text)\r
+ {\r
+ c = *text;\r
+\r
+ if (c >= 'A' && c <= 'Z')\r
+ {\r
+ c = c + -33;\r
+ }\r
+ else if (c >= 'a' && c <= 'z')\r
+ {\r
+ c = c + -39;\r
+ }\r
+ else if (c == '.')\r
+ {\r
+ c = 84;\r
+ }\r
+ else if (c == ',')\r
+ {\r
+ c = 85;\r
+ }\r
+ else if (c == '-')\r
+ {\r
+ c = 86;\r
+ }\r
+ else if (c == '"')\r
+ {\r
+ c = 87;\r
+ }\r
+ else if (c == ' ')\r
+ {\r
+ c = 88;\r
+ }\r
+ else if (c == '!')\r
+ {\r
+ c = 89;\r
+ }\r
+ else if (c == '\'')\r
+ {\r
+ c = 90;\r
+ }\r
+ else if (c != '\n')\r
+ {\r
+ c = 84; // any unhandled character is drawn as '.'\r
+ }\r
+\r
+ *text++ = c;\r
+ }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= DrawSWText\r
+=\r
+============================\r
+*/\r
+\r
+void DrawSWText(void)\r
+{\r
+ char far *text;\r
+ char *ptr;\r
+ char c;\r
+ char strbuf[80];\r
+\r
+ WindowX = 0;\r
+ WindowW = 336;\r
+ PrintY = 1; // always leave the first line blank\r
+ bufferofs = 0;\r
+ panadjust = 0;\r
+ text = swtext;\r
+ masterlines = 0;\r
+\r
+ //\r
+ // draw the entire text to video memory\r
+ //\r
+ while (*text)\r
+ {\r
+ ptr = strbuf;\r
+ do\r
+ {\r
+ c = *text++;\r
+ *ptr++ = c;\r
+ } while (c != '\n' && c != '\0');\r
+ *ptr = '\0';\r
+\r
+ TranslateString(strbuf);\r
+\r
+ US_CPrint(strbuf);\r
+\r
+ bufferofs += ylookup[PrintY];\r
+ masterlines += PrintY;\r
+ PrintY = 0;\r
+ }\r
+\r
+ //\r
+ // allocate a buffer large enough to hold the entire text image\r
+ // and move the image data from video memory into that buffer\r
+ //\r
+ MM_GetPtr(&sourcepic, bufferofs);\r
+ EGAREADMAP(1); // read from "green" plane (doesn't really matter from which plane we read)\r
+ movedata(screenseg, 0, FP_SEG(sourcepic), 0, bufferofs);\r
+\r
+ //\r
+ // erase the (first screen of the) text from video memory.\r
+ // we're going to display this area and copy the backgound pic\r
+ // here line-by-line as the scalers are generated and we don't\r
+ // want to have parts of the text still visible at that point.\r
+ //\r
+ bufferofs = 0;\r
+ VW_Bar(0, 0, 320, 200, BLACK);\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= ScrollSWText\r
+=\r
+============================\r
+*/\r
+\r
+void ScrollSWText(void)\r
+{\r
+ Sint32 now;\r
+ Uint16 pos;\r
+ Sint16 i, rowof;\r
+\r
+ tics = TimeCount = lasttimecount = 0;\r
+\r
+ EGAWRITEMODE(0);\r
+ EGAMAPMASK(8); // only draw to the "intensity" plane (so we don't erase the backgound pic)\r
+\r
+ pos = 0;\r
+ while (masterlines + 400 >= pos)\r
+ {\r
+ for (i = 199; i >= 0; i--)\r
+ {\r
+ rowof = pos - sourceline[i];\r
+ if (rowof < 0 || rowof >= masterlines)\r
+ {\r
+ masterofs = 0; // draw the first (blank) line of the buffer\r
+ }\r
+ else\r
+ {\r
+ masterofs = rowof * 42;\r
+ }\r
+ routine = linestarts[i];\r
+ asm {\r
+ mov es, screenseg;\r
+ mov di, pageofs;\r
+ mov ds, bittables;\r
+ push bp;\r
+ mov bp, ss:masterofs;\r
+ mov ax, ss:sourcepic;\r
+ xor bh, bh;\r
+ cli; // disable interrupts (scaler changes register SS, so interrupts would be fatal!)\r
+ call ss:routine;\r
+ sti; // enable interrupts again\r
+ pop bp;\r
+ mov ax, ss;\r
+ mov ds, ax;\r
+ }\r
+ }\r
+\r
+ VW_SetScreen(pageofs, 0);\r
+ pageon ^= 1;\r
+ pageofs = pageon << 15;\r
+\r
+ now = TimeCount;\r
+ tics = tics + (now - lasttimecount);\r
+ lasttimecount = now;\r
+ if (tics > 20)\r
+ tics = 20;\r
+\r
+ pos = pos + tics / 4;\r
+ tics &= 3;\r
+\r
+ if (IN_IsUserInput() && LastScan != sc_F1)\r
+ LastScan = sc_Space;\r
+\r
+ if (LastScan)\r
+ break;\r
+ }\r
+}\r
+\r
+/*\r
+============================\r
+=\r
+= StarWars\r
+=\r
+============================\r
+*/\r
+\r
+void StarWars(void)\r
+{\r
+ SetPaletteEx(colors[0]); // all black\r
+ VW_ClearVideo(BLACK);\r
+ VW_SetLineWidth(42); // 336 pixels\r
+ VW_SetScreen(0, 0);\r
+ pageon = pageofs = 0;\r
+ CA_SetAllPurge();\r
+ CA_CacheGrChunk(STARTFONT+2);\r
+ fontnumber = 2;\r
+ DrawSWText();\r
+ fontnumber = 0;\r
+\r
+ CA_CacheGrChunk(SW_BACKGROUNDPIC);\r
+ bufferofs = 0x8000;\r
+ VW_DrawPic(0, 0, SW_BACKGROUNDPIC);\r
+ CA_SetAllPurge();\r
+ SetPaletteEx(starcolors);\r
+ bufferofs = 0;\r
+ CompileSWUpdate();\r
+\r
+ if (!LastScan)\r
+ {\r
+ StartMusic(STARWARSMUSIC);\r
+ ScrollSWText();\r
+ StopMusic();\r
+ }\r
+\r
+ MM_FreePtr(&linecode);\r
+ MM_FreePtr(&bittables);\r
+ MM_FreePtr(&sourcepic);\r
+\r
+ VW_ClearVideo(BLACK);\r
+ VW_SetLineWidth(SCREENWIDTH);\r
+ VW_SetDefaultColors();\r
+ RF_FixOfs();\r
+ CA_ClearMarks();\r
+\r
+ CheckLastScan();\r
+}\r
+\r
+#endif // if GRMODE == EGAGR\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= ShowTitle\r
+=\r
+============================\r
+*/\r
+\r
+void ShowTitle(void)\r
+{\r
+ panadjust = 0;\r
+ CA_CacheGrChunk(TITLEPICPIC);\r
+ VW_DrawPic(0, 0, TITLEPICPIC);\r
+#if GRMODE == CGAGR\r
+ VW_UpdateScreen();\r
+#else\r
+ VW_SetScreen(displayofs, 0);\r
+ VW_ScreenToScreen(bufferofs, displayofs, 42, 224);\r
+#endif\r
+ IN_UserInput(6*TickBase, false);\r
+ CA_ClearMarks();\r
+ CheckLastScan();\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if GRMODE == CGAGR\r
+/*\r
+============================\r
+=\r
+= ShowCredits\r
+=\r
+============================\r
+*/\r
+\r
+void ShowCredits(void)\r
+{\r
+ panadjust = 0;\r
+ CA_CacheGrChunk(SW_BACKGROUNDPIC);\r
+ VW_DrawPic(0, 0, SW_BACKGROUNDPIC);\r
+ VW_UpdateScreen();\r
+ IN_UserInput(6*TickBase, false);\r
+ CA_ClearMarks();\r
+ CheckLastScan();\r
+}\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= RunDemo\r
+=\r
+============================\r
+*/\r
+\r
+void RunDemo(Sint16 num)\r
+{\r
+ Uint16 far *demodata;\r
+ \r
+ NewGame();\r
+ num += DEMO0;\r
+ CA_CacheGrChunk(num);\r
+ demodata = grsegs[num];\r
+ gamestate.mapon = demodata[0];\r
+ DemoSize = demodata[1];\r
+ MM_GetPtr(&(memptr)DemoBuffer, DemoSize);\r
+ MM_SetLock(&(memptr)DemoBuffer, true);\r
+ _fmemcpy(DemoBuffer, ((char _seg *)grsegs[num])+4, DemoSize);\r
+ MM_FreePtr(&grsegs[num]);\r
+ IN_StartDemoPlayback(DemoBuffer, DemoSize);\r
+ SetupGameLevel(true);\r
+ if (scorescreenkludge)\r
+ {\r
+ DrawHighScores();\r
+ }\r
+ PlayLoop();\r
+ IN_StopDemo();\r
+ MM_FreePtr(&(memptr)DemoBuffer);\r
+ VW_FixRefreshBuffer();\r
+ CA_ClearMarks();\r
+ CheckLastScan();\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= DrawHighScores\r
+=\r
+============================\r
+*/\r
+\r
+void DrawHighScores(void)\r
+{\r
+ Uint16 i, n;\r
+ Uint16 width, height;\r
+ HighScore *entry;\r
+ Uint16 oldbufferofs;\r
+ char buf[16], *bufptr;\r
+ \r
+ RF_NewPosition(0, 0);\r
+ oldbufferofs = bufferofs;\r
+ bufferofs = masterofs;\r
+#ifdef KEEN5\r
+#if GRMODE == CGAGR\r
+ fontcolor = 2;\r
+#else\r
+ fontcolor = BLUE ^ LIGHTMAGENTA; // blue text on light magenta background (XOR draw mode!)\r
+#endif\r
+#endif\r
+ for (i=0, entry=&Scores[0]; i<MaxScores; i++, entry++)\r
+ {\r
+ PrintY = i*16 + HIGHSCORE_TOP;\r
+ PrintX = HIGHSCORE_LEFT;\r
+ US_Print(entry->name);\r
+#ifdef KEEN4\r
+ PrintX = 152;\r
+ for (n=0; n<entry->completed; n++)\r
+ {\r
+ VWB_DrawTile8(PrintX, PrintY+1, 71);\r
+ PrintX += 8;\r
+ }\r
+#endif\r
+ ultoa(entry->score, buf, 10);\r
+ for (bufptr=buf; *bufptr; bufptr++)\r
+ {\r
+ *bufptr = *bufptr + 81;\r
+ }\r
+ USL_MeasureString(buf, &width, &height);\r
+ PrintX = HIGHSCORE_RIGHT - width;\r
+ US_Print(buf);\r
+ }\r
+ fontcolor = WHITE; // back to default color\r
+ bufferofs = oldbufferofs;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= CheckHighScore\r
+=\r
+============================\r
+*/\r
+\r
+void CheckHighScore(Sint32 score, Sint16 completed)\r
+{\r
+ Uint16 i, n;\r
+ Sint16 index;\r
+ HighScore entry;\r
+ \r
+ strcpy(entry.name, ""); //Note: 'entry.name[0] = 0;' would be more efficient\r
+ entry.score = score;\r
+ entry.completed = completed;\r
+ for (i=0, index=-1; i<MaxScores; i++)\r
+ {\r
+ if (Scores[i].score < entry.score ||\r
+ (Scores[i].score == entry.score && Scores[i].completed < entry.completed))\r
+ {\r
+ n=MaxScores;\r
+ while (--n > i)\r
+ {\r
+ Scores[n] = Scores[n-1];\r
+ }\r
+ Scores[i] = entry;\r
+ index = i;\r
+ HighScoresDirty = true;\r
+ break;\r
+ }\r
+ }\r
+ if (index != -1)\r
+ {\r
+ scorescreenkludge = true;\r
+ gamestate.mapon = HIGHSCORE_MAP;\r
+ SetupGameLevel(true);\r
+ DrawHighScores();\r
+#ifdef KEEN5\r
+#if GRMODE == CGAGR\r
+ fontcolor = 2;\r
+#else\r
+ fontcolor = BLUE ^ LIGHTMAGENTA; // blue text on light magenta background (XOR draw mode!)\r
+#endif\r
+#endif\r
+ RF_Refresh();\r
+ RF_Refresh();\r
+ PrintY = i*16 + HIGHSCORE_TOP;\r
+ PrintX = HIGHSCORE_LEFT;\r
+ US_LineInput(PrintX, PrintY, Scores[index].name, NULL, true, MaxHighName, 112);\r
+ scorescreenkludge = false;\r
+ }\r
+#ifdef KEEN5\r
+ fontcolor = 15; // back to default color (white)\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+============================\r
+=\r
+= ShowHighScores\r
+=\r
+============================\r
+*/\r
+\r
+void ShowHighScores(void)\r
+{\r
+ scorescreenkludge = true;\r
+ IN_ClearKeysDown();\r
+ RunDemo(4);\r
+ scorescreenkludge = false;\r
+}\r