From fd4e2883b1cd0ca657ed2f703950b4efa5fbd9dd Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sun, 9 Jul 2023 17:01:55 -0700 Subject: [PATCH] M4: Implementing lots of wscript and dependencies Oh, soooo many dependencies. --- engines/m4/adv_r/adv_control.cpp | 10 + engines/m4/adv_r/adv_control.h | 1 + engines/m4/core/errors.cpp | 35 +- engines/m4/core/errors.h | 2 + engines/m4/core/globals.h | 1 - engines/m4/core/imath.cpp | 15 + engines/m4/core/imath.h | 2 +- engines/m4/dbg/dbg_defs.h | 51 + engines/m4/dbg/dbg_wscript.cpp | 67 ++ engines/m4/dbg/dbg_wscript.h | 48 + engines/m4/dbg/debug.h | 27 + engines/m4/fileio/fstream.cpp | 543 +++++++++- engines/m4/fileio/fstream.h | 12 +- engines/m4/graphics/gr_series.cpp | 6 + engines/m4/graphics/gr_series.h | 6 + engines/m4/graphics/krn_pal.cpp | 841 ++++++++++++++- engines/m4/graphics/krn_pal.h | 76 ++ engines/m4/graphics/rend.cpp | 35 + engines/m4/graphics/rend.h | 68 ++ engines/m4/gui/gui.h | 8 +- engines/m4/m4_types.h | 2 + engines/m4/mem/reloc.cpp | 4 + engines/m4/mem/reloc.h | 2 + engines/m4/module.mk | 3 + engines/m4/platform/sound/digi.cpp | 31 + engines/m4/platform/sound/digi.h | 18 + engines/m4/platform/sound/midi.cpp | 13 + engines/m4/platform/sound/midi.h | 5 + engines/m4/vars.cpp | 11 + engines/m4/vars.h | 6 +- engines/m4/wscript/ws_cruncher.cpp | 1556 +++++++++++++++++++++++++++- engines/m4/wscript/ws_cruncher.h | 53 +- engines/m4/wscript/ws_hal.cpp | 508 ++++++++- engines/m4/wscript/ws_hal.h | 32 +- engines/m4/wscript/ws_load.cpp | 1110 ++++++++++++++++++-- engines/m4/wscript/ws_load.h | 17 +- engines/m4/wscript/ws_machine.cpp | 1168 ++++++++++++++++++++- engines/m4/wscript/ws_machine.h | 28 +- engines/m4/wscript/ws_timer.cpp | 21 +- engines/m4/wscript/wst_regs.cpp | 122 +++ engines/m4/wscript/wst_regs.h | 8 +- 41 files changed, 6321 insertions(+), 251 deletions(-) create mode 100644 engines/m4/dbg/dbg_defs.h create mode 100644 engines/m4/dbg/dbg_wscript.cpp create mode 100644 engines/m4/dbg/dbg_wscript.h create mode 100644 engines/m4/dbg/debug.h create mode 100644 engines/m4/graphics/rend.cpp create mode 100644 engines/m4/graphics/rend.h create mode 100644 engines/m4/wscript/wst_regs.cpp diff --git a/engines/m4/adv_r/adv_control.cpp b/engines/m4/adv_r/adv_control.cpp index 691f35f95c4..0234cfd15ad 100644 --- a/engines/m4/adv_r/adv_control.cpp +++ b/engines/m4/adv_r/adv_control.cpp @@ -50,4 +50,14 @@ void player_set_commands_allowed(bool t_or_f) { } } +void game_pause(bool flag) { + if (flag) { + _G(kernel).pause = true; + PauseEngines(); + } else { + _G(kernel).pause = false; + UnpauseEngines(); + } +} + } // End of namespace M4 diff --git a/engines/m4/adv_r/adv_control.h b/engines/m4/adv_r/adv_control.h index 1f7400a5893..45e0ebb8014 100644 --- a/engines/m4/adv_r/adv_control.h +++ b/engines/m4/adv_r/adv_control.h @@ -30,6 +30,7 @@ namespace M4 { extern bool kernel_section_startup(); extern void player_set_commands_allowed(bool t_or_f); +extern void game_pause(bool flag); } // End of namespace M4 diff --git a/engines/m4/core/errors.cpp b/engines/m4/core/errors.cpp index 119af547b59..5eaca4fedf5 100644 --- a/engines/m4/core/errors.cpp +++ b/engines/m4/core/errors.cpp @@ -19,12 +19,16 @@ * */ -#include "common/str.h" +#include "common/file.h" #include "common/textconsole.h" #include "m4/core/errors.h" namespace M4 { +inline static bool quadchar_equals_string(uint32 code, const Common::String &str) { + return READ_BE_UINT32(str.c_str()) == code; +} + void error_show(const char *filename, uint32 line, quadchar errorcode, const char *fmt, ...) { va_list va; va_start(va, fmt); @@ -38,4 +42,33 @@ void error_show(const char *filename, uint32 line, quadchar errorcode) { error_show(filename, line, errorcode, "No extra description"); } +void error_look_up(quadchar errorcode, char *result_string) { + Common::File f; + *result_string = '\0'; + + if (!f.open(ERROR_FILE)) + return; + + Common::String buffer; + + while (!f.eos()) { + buffer = f.readString(); + const char *mark = buffer.c_str() + 1; + + if (quadchar_equals_string(errorcode, buffer) || quadchar_equals_string(errorcode, mark)) { + const char *src = (const char *)buffer.c_str() + 5; + int16 count = 0; + + do { + *result_string++ = *src; + ++count; + } while (*src++ && (count < MAX_STRING_LEN)); + + break; + } + } +} + + + } // namespace M4 diff --git a/engines/m4/core/errors.h b/engines/m4/core/errors.h index a755cf63314..9d07e094b9f 100644 --- a/engines/m4/core/errors.h +++ b/engines/m4/core/errors.h @@ -27,9 +27,11 @@ namespace M4 { #define FL __FILE__,__LINE__ +#define ERROR_FILE "error.m4" extern void error_show(const char *filename, uint32 line, quadchar errorcode, const char *fmt, ...); extern void error_show(const char *filename, uint32 line, quadchar errorcode); +extern void error_look_up(quadchar errorcode, char *result_string); } // namespace M4 diff --git a/engines/m4/core/globals.h b/engines/m4/core/globals.h index 9a586cf443c..9443a7db63d 100644 --- a/engines/m4/core/globals.h +++ b/engines/m4/core/globals.h @@ -76,7 +76,6 @@ enum { }; constexpr int GLB_SHARED_VARS = 256; -constexpr int GLOB_COUNT = 39; struct GlobalVars : public Common::Array { void syncGame(Common::Serializer &s); diff --git a/engines/m4/core/imath.cpp b/engines/m4/core/imath.cpp index bfdeb7bd947..8bb2ffff789 100644 --- a/engines/m4/core/imath.cpp +++ b/engines/m4/core/imath.cpp @@ -117,6 +117,21 @@ frac16 SqrtF16(frac16 n) { return (frac16)r; } +#define DIV_128_PI 0x28be61 + +frac16 ArcTan(frac16 x, frac16 y) { + double floatX, floatY, result; + frac16 fracResult; + + floatX = (float)(x >> 16) + (float)((float)(x & 0xffff) / (float)65536); + floatY = (float)(y >> 16) + (float)((float)(y & 0xffff) / (float)65536); + result = atan2(floatY, floatX); + fracResult = (((int32)(floor(result))) << 16) + (int32)(floor((result - floor(result)) * 65536)); + fracResult = MulSF16(fracResult, DIV_128_PI); + if (fracResult < 0) fracResult += 0x1000000; + return fracResult; +} + uint16 HighWord(uint32 n) { return (uint16)(n >> 16); } diff --git a/engines/m4/core/imath.h b/engines/m4/core/imath.h index de8e5573e4f..7afdf03d48c 100644 --- a/engines/m4/core/imath.h +++ b/engines/m4/core/imath.h @@ -56,7 +56,7 @@ frac16 imath_ranged_rand16(frac16 a, frac16 b); frac16 dist2d(int32 x1, int32 y1, int32 x2, int32 y2); frac16 SqrtF16(frac16 n); -//frac16 ArcTan(frac16 x, frac16 y); +frac16 ArcTan(frac16 x, frac16 y); uint16 HighWord(uint32 n); uint16 LowWord(uint32 n); diff --git a/engines/m4/dbg/dbg_defs.h b/engines/m4/dbg/dbg_defs.h new file mode 100644 index 00000000000..b426cbcb799 --- /dev/null +++ b/engines/m4/dbg/dbg_defs.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 3 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, see . + * + */ + +#ifndef M4_DBG_DBG_DEFS_H +#define M4_DBG_DBG_DEFS_H + +#include "m4/m4_types.h" +#include "m4/dbg/dbg_defs.h" +#include "m4/gui/gui_dialog.h" +#include "m4/wscript/ws_machine.h" + +namespace M4 { + +struct DBGSequSR { + DBGSequSR *next; + int32 prevSequHash; + int32 returnOffset; +}; + +struct DBGWatch { + DBGWatch *next; + DBGWatch *prev; + machine *m; + Dialog *d; + bool moreInfo; + bool machStep; + int32 sequHash; + bool sequStep; +}; + +} // namespace M4 + +#endif diff --git a/engines/m4/dbg/dbg_wscript.cpp b/engines/m4/dbg/dbg_wscript.cpp new file mode 100644 index 00000000000..c0b7e0b061d --- /dev/null +++ b/engines/m4/dbg/dbg_wscript.cpp @@ -0,0 +1,67 @@ +/* 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 3 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, see . + * + */ + +#include "m4/dbg/dbg_wscript.h" + +namespace M4 { + +bool dbg_ws_init(bool showTheScreen, Font *useThisFont, frac16 *theGlobals) { + return true; +} + +void dbg_ws_shutdown() { + // No implementation +} + +void dbg_ws_update() { + // No implementation +} + +void dbg_LaunchSequence(Anim8 *myAnim8) { + // No implementation +} + +void dbg_DebugWSMach(machine *m, bool debug) { + // No implementation +} + +void dbg_DebugNextCycle() { + // No implementation +} + +void dbg_RemoveWSMach(machine *m) { + // No implementation +} + +void dbg_SetCurrMachInstr(machine *m, int32 pcOffset) { + // No implementation +} + +void dbg_SetCurrSequInstr(Anim8 *myAnim8, int32 compareCCR) { + // No implementation +} + +void dbg_WSError(Common::WriteStream *logFile, machine *m, int32 errorType, + const char *errDesc, const char *errMsg, int32 pcOffset) { + // No implementation +} + +} // namespace M4 diff --git a/engines/m4/dbg/dbg_wscript.h b/engines/m4/dbg/dbg_wscript.h new file mode 100644 index 00000000000..7fa4a3c0158 --- /dev/null +++ b/engines/m4/dbg/dbg_wscript.h @@ -0,0 +1,48 @@ +/* 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 3 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, see . + * + */ + +#ifndef M4_DBG_DBG_WSCRIPT_H +#define M4_DBG_DBG_WSCRIPT_H + +#include "common/stream.h" +#include "m4/m4_types.h" +#include "m4/dbg/dbg_defs.h" +#include "m4/graphics/gr_font.h" +#include "m4/wscript/ws_machine.h" + +namespace M4 { + +extern bool dbg_ws_init(bool showTheScreen, Font *useThisFont, frac16 *theGlobals); +extern void dbg_ws_shutdown(); +extern void dbg_ws_update(); + +extern void dbg_LaunchSequence(Anim8 *myAnim8); +extern void dbg_DebugWSMach(machine *m, bool debug); +extern void dbg_DebugNextCycle(); +extern void dbg_RemoveWSMach(machine *m); +extern void dbg_SetCurrMachInstr(machine *m, int32 pcOffset); +extern void dbg_SetCurrSequInstr(Anim8 *myAnim8, int32 compareCCR); +extern void dbg_WSError(Common::WriteStream *logFile, machine *m, int32 errorType, + const char *errDesc, const char *errMsg, int32 pcOffset); + +} // namespace M4 + +#endif diff --git a/engines/m4/dbg/debug.h b/engines/m4/dbg/debug.h new file mode 100644 index 00000000000..f26de6b4ae4 --- /dev/null +++ b/engines/m4/dbg/debug.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 3 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, see . + * + */ + +#ifndef M4_DBG_DEBUG_H +#define M4_DBG_DEBUG_H + +#include "m4/dbg/dbg_wscript.h" + +#endif diff --git a/engines/m4/fileio/fstream.cpp b/engines/m4/fileio/fstream.cpp index 107d466e045..92a52828f64 100644 --- a/engines/m4/fileio/fstream.cpp +++ b/engines/m4/fileio/fstream.cpp @@ -20,15 +20,13 @@ */ #include "m4/fileio/fstream.h" +#include "m4/core/errors.h" +#include "m4/core/imath.h" #include "m4/vars.h" namespace M4 { -bool f_stream_Init() { - _G(firstStream) = nullptr; - _G(lastStream) = nullptr; - return true; -} +#define STR_STRMREQ "stream request" StreamFile::StreamFile(const Common::String &filename) { if (!_file.open(filename)) @@ -43,7 +41,7 @@ bool StreamFile::seek(uint32 n) { return _file.seek(n); } -bool StreamFile::seek_ahead(uint32 n) { +bool StreamFile::seek_ahead(int32 n) { return _file.skip(n); } @@ -51,4 +49,537 @@ uint32 StreamFile::get_pos() { return _file.pos(); } +bool f_stream_Init() { + _G(firstStream) = nullptr; + _G(lastStream) = nullptr; + return true; +} + +void f_stream_Shutdown(void) { + strmRequest *myStream; + + // Loop through the list, closing all stream requests, which also deallocs the request + myStream = _G(firstStream); + while (myStream) { + _G(firstStream) = _G(firstStream)->next; + f_stream_Close(myStream); + myStream = _G(firstStream); + } +} + +strmRequest *f_stream_Open(StreamFile *srcFile, int32 fileOffset, int32 strmMinBuffSize, int32 strmBuffSize, + int32 numBlocksToRead, int32 *blockSizeArray, int32 initialRead, bool wrapStream) { + + strmRequest *newStream; + int32 bytesRead, i, bytesToRead; + bool finished; + void *bufferHandle; + int32 memAvail; + + const int32 PADDING = 256; + + // Parameter verification + if (!srcFile) { + error_show(FL, 'FSF!'); + } + + if (strmMinBuffSize < 0) { + error_show(FL, 'FSF1', "neg min buffsize: %ld", strmMinBuffSize); + } + + // Allocate a new stream request struct + if ((newStream = (strmRequest *)mem_alloc(sizeof(strmRequest), STR_STRMREQ)) == nullptr) { + error_show(FL, 'OOM!', "%ld", sizeof(strmRequest)); + return nullptr; + } + + // Try to get memory + newStream->strmHandle = NewHandle(strmBuffSize, "stream buff"); + if (newStream->strmHandle) { + goto got_mem; + } + + // Maximize available memory + MaxMem((Size *)&memAvail); // param on PC is max mem avail in one block + + // try to get requested size + if (memAvail >= strmBuffSize) { + /* + if ((newStream->strmBuff = (uint8*)mem_alloc(strmBuffSize, STR_STRMBUFF)) != nullptr) { + goto got_mem; + } + */ + // try to get memory + newStream->strmHandle = NewHandle(strmBuffSize, "stream buff"); + if (newStream->strmHandle) { + goto got_mem; + } + } + + // try to get what's left if it's enough + // get a compromise between free and requested. + // if we get it all, system gets unstable... + if (memAvail > strmMinBuffSize) { + int32 alloc_me = ((memAvail - strmMinBuffSize) / 2) + strmMinBuffSize; + /* + if ((newStream->strmBuff = (uint8*)mem_alloc(alloc_me, STR_STRMBUFF)) != nullptr) { + strmBuffSize = alloc_me; + goto got_mem; + } + */ + // try to get memory + newStream->strmHandle = NewHandle(alloc_me, "stream buff"); + if (newStream->strmHandle) { + strmBuffSize = alloc_me; + goto got_mem; + } + + } + + // sorry, bud. + error_show(FL, 'FSOM', "want: %ld, have: %ld", strmMinBuffSize, memAvail); + +got_mem: + //lock the buffer - to be locked until the stream is closed + HLock(newStream->strmHandle); + newStream->strmBuff = (uint8 *) * (newStream->strmHandle); + + // Initialize the stream request + newStream->strmSize = strmBuffSize; + newStream->strmHead = newStream->strmBuff; + newStream->strmTail = newStream->strmBuff; + newStream->endStrmBuff = newStream->strmBuff + strmBuffSize; + newStream->strmWrap = newStream->endStrmBuff; + newStream->strmLastRead = newStream->endStrmBuff; + newStream->numBlocksToRead = numBlocksToRead; + newStream->blockSizeArray = blockSizeArray; + newStream->wrapStream = wrapStream; + newStream->srcFile = srcFile; + + // If the streaming should begin part way into the file, seek to the beginning of where to start streaming + if (fileOffset > 0) { + // If (fseek(newStream->srcFile, fileOffset, SEEK_SET) != 0) { + if (!newStream->srcFile->seek(fileOffset)) { + delete newStream->srcFile; + mem_free(newStream); + return nullptr; + } + } + + // Check if we are to initially read the stream + if (initialRead > 0) { + + // If the blockSizeArray exists, then initialRead is the number of blocks to read + if (newStream->blockSizeArray) { + // Calculate the total number of bytes to read in initially + initialRead = (int32)imath_min(initialRead, numBlocksToRead); + finished = false; + bytesToRead = 0; + i = 0; + while ((i < initialRead) && (!finished)) { + if ((bytesToRead + blockSizeArray[i]) <= strmBuffSize) { + bytesToRead += blockSizeArray[i]; + i++; + } else { + finished = true; + } + } + + // Update the blockSizeArray, and numBlocksToRead entries. We plan to read in "i" blocks so far. + newStream->numBlocksToRead -= i; + newStream->blockSizeArray += i; + } + + // Else the initialRead refers to the number of bytes to initially read + else { + + //bounds check the initialRead and set the nextReadSize field + bytesToRead = (int32)imath_min(initialRead, strmBuffSize); + newStream->nextReadSize = bytesToRead; + } + + // Make sure we still have something to read + if (bytesToRead > 0) { + + // Read in the initial bytes to read + //bytesRead = fread((void*)newStream->strmHead, 1, bytesToRead, newStream->srcFile); + bufferHandle = newStream->strmHead; + bytesRead = newStream->srcFile->read((Handle)&bufferHandle, bytesToRead); + + //did we actually read that many? If not, close the file + if (bytesRead < bytesToRead) { + delete newStream->srcFile; + newStream->srcFile = nullptr; + } + + // Update the strmHead pointer + newStream->strmHead += bytesRead; + } + } + + //link the stream request into the list of requests + newStream->prev = nullptr; + newStream->next = _G(firstStream); + if (_G(firstStream)) { + _G(firstStream)->prev = newStream; + } else { + _G(lastStream) = newStream; + } + _G(firstStream) = newStream; + + // Return the stream request + return newStream; +} + + +static bool UnwrapStream(strmRequest *myStream) { + int32 bytesToMove = 0, bytesAvail; + uint8 *tempBuff; + + // Using tempBuff as a flag to determine whether data needs to be temporarily stored + tempBuff = nullptr; + + // Since strmTail is never allowed to be equal to strmWrap if it is > strmHead, there must be a + // Non-zero amount of data at the end which we must move. + //Therefore, we may have to temporarily store anything at the beginning of the buffer + if (myStream->strmHead > myStream->strmBuff) { + + // Calculate how many bytes to store and copy to a temporary buffer + bytesToMove = (int32)myStream->strmHead - (int32)myStream->strmBuff; + + if ((tempBuff = (uint8 *)mem_alloc(bytesToMove, "stream temp buff")) == nullptr) + error_show(FL, 'OOM!', "UnwrapStream() failed - temp buff avail: %ld", bytesToMove); + + memcpy(tempBuff, myStream->strmBuff, bytesToMove); + } + + // Move the data at the end of the buffer to the beginning and reset the strmWrap pointer + bytesAvail = (int32)myStream->strmWrap - (int32)myStream->strmTail; + memmove(myStream->strmBuff, myStream->strmTail, bytesAvail); + myStream->strmTail = myStream->strmBuff; + myStream->strmHead = (uint8 *)((int32)(myStream->strmTail) + bytesAvail); + myStream->strmWrap = myStream->endStrmBuff; + + // Now check if we temporarily store data. if so, copy it back to the stream and turf the temp buffer + if (tempBuff) { + memcpy(myStream->strmHead, tempBuff, bytesToMove); + myStream->strmHead += bytesToMove; + mem_free(tempBuff); + } + return true; +} + + +void f_stream_DumpPreviouslyRead(strmRequest *myStream) { + // This is used to allow the f_stream_Process() function to overwrite the stream buffer space where + // the previously read data was stored. ie. If you call f_stream_Read(), and then make a copy, + // you wouldn't care if the data in the stream buffer was overwritten, so call this procedure. + if (myStream) { + myStream->strmLastRead = myStream->strmTail; + if (myStream->strmTail == myStream->strmHead) { + myStream->strmTail = myStream->strmBuff; + myStream->strmHead = myStream->strmBuff; + } + } +} + + +int32 f_stream_Read(strmRequest *myStream, uint8 **dest, int32 numBytes) { + int32 bytesAvail, bytesNeeded, bytesRead; + void *bufferHandle; + + // Parameter verification + if (!myStream) + error_show(FL, 'FSIS', "f_stream_Read() failed - invalid stream request"); + + if ((numBytes <= 0) || (numBytes >= myStream->strmSize)) + error_show(FL, 'FSR!', "%ld stream size %ld", numBytes, myStream->strmSize); + + // If the stream tail is > the stream head, and the number of bytes at the end of the buffer is < numBytes + // we must unwrap the stream, moving the data at the end of the buffer to the beginning, and slide the beginning down + if ((myStream->strmTail > myStream->strmHead) && (((int32)myStream->strmWrap - (int32)myStream->strmTail) < numBytes)) { + UnwrapStream(myStream); + } + + // Now either the strmHead is >= the strmTail, or there is enough data at the end of the buffer to fulfill numBytes + + // Calculate the number of bytes available + if (myStream->strmTail <= myStream->strmHead) { + bytesAvail = (int32)(myStream->strmHead - myStream->strmTail); + } else { + // No extra data is available at the beginning of the stream buffer, since we "unwrapped" the stream + bytesAvail = (int32)(myStream->strmWrap - myStream->strmTail); + } + + // Now check and see if we have enough bytes available + if (bytesAvail >= numBytes) { + // Set the destination pointer + *dest = (uint8 *)myStream->strmTail; + myStream->strmLastRead = myStream->strmTail; + + // Update the strmTail pointer + myStream->strmTail += numBytes; + + // If there is no data left at the end of the stream buffer, reset the strmTail and strmWrap pointers + if (myStream->strmTail == myStream->strmWrap) { + myStream->strmTail = myStream->strmBuff; + myStream->strmWrap = myStream->endStrmBuff; + } + + return numBytes; + } else { + // Else we will have to read more data from disc + // If this has happened, since we "unwrapped" the stream buff, we can guarantee that strmTail < strmHead + + // Calculate how much more must be read in + bytesNeeded = numBytes - bytesAvail; + + // Make sure we have enough room at the end of the buffer to accommodate + if ((int32)(myStream->endStrmBuff - myStream->strmHead) < bytesNeeded) { + // We need to memmove the contents of the stream to the beginning of the buff to allow + // F_stream_read() to return a pointer to a contiguous block + + // Move the data to the beginning of the stream buffer, and reset the head and tail pointers + memmove((void *)myStream->strmBuff, myStream->strmTail, bytesAvail); + myStream->strmTail = myStream->strmBuff; + myStream->strmHead = (uint8 *)((int32)myStream->strmTail + bytesAvail); + } + + // If the client is using a blockSizeArray, hopefully bytesNeeded will be equal to the next blockSize + if (myStream->blockSizeArray && (*myStream->blockSizeArray == bytesNeeded) && (myStream->numBlocksToRead > 0)) { + myStream->blockSizeArray++; + myStream->numBlocksToRead--; + } else { + // Otherwise we just trashed the whole point of using a calculated blockSizeArray + myStream->blockSizeArray = nullptr; + myStream->numBlocksToRead = -1; + myStream->nextReadSize = numBytes; + } + + // Read in the bytesNeeded + bufferHandle = myStream->strmHead; + bytesRead = myStream->srcFile->read((Handle)&bufferHandle, bytesNeeded); + + if (bytesRead < bytesNeeded) { + // If we could not read that much in, close the srcFile + delete myStream->srcFile; + myStream->srcFile = nullptr; + } + + // Set the destination pointer and update the stream pointers + *dest = (uint8 *)myStream->strmTail; + myStream->strmLastRead = myStream->strmTail; + myStream->strmHead += bytesRead; + myStream->strmTail = myStream->strmHead; + + // Return the number of bytes successfully available + return (bytesRead + bytesAvail); + } +} + +void f_stream_Close(strmRequest *myStream) { + // Parameter verification + if (!myStream) { + return; + } + + // Close the stream and throw out the stream buffer + if (myStream->srcFile) { + delete myStream->srcFile; + } + + // Kill the stream buffer + HUnLock(myStream->strmHandle); + DisposeHandle(myStream->strmHandle); + myStream->strmBuff = nullptr; + + + // Remove the stream request from the list of requests + if (myStream->next) { + myStream->next->prev = myStream->prev; + } else { + _G(lastStream) = myStream->prev; + } + if (myStream->prev) { + myStream->prev->next = myStream->next; + } else { + _G(firstStream) = myStream->next; + } + + // Final, turf the stream request + mem_free(myStream); +} + +void f_stream_Process(int32 numToProcess) { + strmRequest *myStream, *firstProcessStream; + int32 buffEndBytesAvail = 0, buffStartBytesAvail = 0; + int32 bytesRead, bytesAvail, nextReadSize; + bool buffWrap, useBlockSizeArray; + void *bufferHandle; + + // No sense wasting time if there are no stream requests to process + if (!_G(firstStream)) { + return; + } + + // Loop through until either the end of the list of requests, or we've serviced the "numToProcess" + firstProcessStream = _G(firstStream); + myStream = _G(firstStream); + while (myStream && (numToProcess > 0)) { + + // Make sure we still have an open srcFile + if (myStream->srcFile && (myStream->numBlocksToRead != 0)) { + buffWrap = false; + useBlockSizeArray = false; + + // Calculate the amount of empty space in the stream buff + // If all the empty space in the stream buff is between the head and the lastRead... + if (myStream->strmLastRead >= myStream->strmHead) { + bytesAvail = (int32)myStream->strmLastRead - (int32)myStream->strmHead; + + // strmTail and strmHead can never equal unless the buffer is completely empty, therefore, + // make sure the amout of bytes available won't cause strmHead to become equal to strmTail + if ((bytesAvail > 0) && (myStream->strmLastRead == myStream->strmTail)) { + bytesAvail--; + } + } else { + // Else all the empty space is wrapped around the end of the buffer + buffWrap = true; + + // Calculate how much space is available at the start and at the end of the buffer + buffEndBytesAvail = (int32)myStream->endStrmBuff - (int32)myStream->strmHead; + buffStartBytesAvail = (int32)myStream->strmLastRead - (int32)myStream->strmBuff; + + // As above, ensure strmHead won't become equal to strmTail + if ((buffStartBytesAvail > 0) && (myStream->strmLastRead == myStream->strmTail)) { + buffStartBytesAvail--; + } + + // Calculate the total bytes available + bytesAvail = buffEndBytesAvail + buffStartBytesAvail; + } + + // Now find the number of bytes to read - either from the blockSizeArray... + if (myStream->blockSizeArray) { + useBlockSizeArray = true; + nextReadSize = *myStream->blockSizeArray; + } else { + // ...or directly from the nextReadSize field of the stream request + nextReadSize = myStream->nextReadSize; + } + + // See if we can simply read the next chunk into the strmHead, without worrying about "wrapping" the buffer + if ((buffWrap && (buffEndBytesAvail >= nextReadSize)) || + ((!buffWrap) && (bytesAvail >= nextReadSize))) { + // Read the bytes into the stream buffer + bufferHandle = myStream->strmHead; + bytesRead = myStream->srcFile->read((Handle)&bufferHandle, nextReadSize); + + // If we could not read that much in, close the srcFile + if (bytesRead < nextReadSize) { + delete myStream->srcFile; + myStream->srcFile = nullptr; + } + + // Update the stream head + myStream->strmHead += bytesRead; + + // Update the blockSizeArray pointer if necessary + if (useBlockSizeArray) { + myStream->blockSizeArray++; + myStream->numBlocksToRead--; + } + } else if (buffWrap) { + // Else if the empty space is wrapped, we may still be able to store the next data chunk, otherwise no more room + + // See if we can wrap the next data chunk around + if (!myStream->wrapStream) { + + // No wrapping allowed, so do we have room for it at the beginning of the stream + if (buffStartBytesAvail >= nextReadSize) { + + //we can read it in at the beginning, so set the strmWrap pointer + myStream->strmWrap = myStream->strmHead; + + // Read the bytes into the stream buffer + //bytesRead = (int32)fread((void*)myStream->strmBuff, 1, nextReadSize, myStream->srcFile); + bufferHandle = myStream->strmBuff; + bytesRead = myStream->srcFile->read((Handle)&bufferHandle, nextReadSize); + + // If we could not read that much in, close the srcFile + if (bytesRead < nextReadSize) { + delete myStream->srcFile; + myStream->srcFile = nullptr; + } + + // Update the stream head + myStream->strmHead = (uint8 *)((int32)myStream->strmBuff + bytesRead); + + // Update the blockSizeArray pointer if necessary + if (useBlockSizeArray) { + myStream->blockSizeArray++; + myStream->numBlocksToRead--; + } + } + } else if (bytesAvail >= nextReadSize) { + // Else we might have to read in part at the end, and part at the beginning of the stream buffer + + // Read into the end of the stream buffer + if (buffEndBytesAvail > 0) { + + // Read into the end of the buffer + bufferHandle = myStream->strmHead; + bytesRead = (int32)myStream->srcFile->read((Handle)&bufferHandle, buffEndBytesAvail); + + // If we could not read that much in, close the srcFile and update the head pointer + if (bytesRead < buffEndBytesAvail) { + delete myStream->srcFile; + myStream->srcFile = nullptr; + myStream->strmHead += bytesRead; + } + } + + // Make sure we didn't close the srcFile in the last read + if (myStream->srcFile) { + + // Read into the beginning of the buffer + bufferHandle = myStream->strmBuff; + bytesRead = myStream->srcFile->read((Handle)&bufferHandle, nextReadSize - buffEndBytesAvail); + + // If we could not read that much in, close the srcFile + if (bytesRead < (nextReadSize - buffEndBytesAvail)) { + delete myStream->srcFile; + myStream->srcFile = nullptr; + } + + // Update the head pointer + myStream->strmHead = (uint8 *)((int32)myStream->strmBuff + bytesRead); + + // Update the blockSizeArray pointer if necessary + if (useBlockSizeArray) { + myStream->blockSizeArray++; + myStream->numBlocksToRead--; + } + } + } + } + } + + // If we were able, we serviced the above stream request. Get the next request and decriment the counter + myStream = myStream->next; + numToProcess--; + } + + // See if we ran out of processes or if the counter ran out + if (myStream) { + // This implies the counter ran out. Move the front of the list to myStream->prev to the end of the list + myStream->prev->next = nullptr; + _G(lastStream)->next = _G(firstStream); + _G(firstStream)->prev = _G(lastStream); + _G(lastStream) = myStream->prev; + myStream->prev = nullptr; + _G(firstStream) = myStream; + } +} + } // namespace M4 diff --git a/engines/m4/fileio/fstream.h b/engines/m4/fileio/fstream.h index ff8936895e9..6729f53d339 100644 --- a/engines/m4/fileio/fstream.h +++ b/engines/m4/fileio/fstream.h @@ -23,6 +23,7 @@ #define M4_FILEIO_FSTREAM_H #include "common/file.h" +#include "m4/mem/reloc.h" #include "m4/m4_types.h" namespace M4 { @@ -36,7 +37,7 @@ public: int32 read(Handle bufferHandle, int32 n); bool seek(uint32 n); - bool seek_ahead(uint32 n); + bool seek_ahead(int32 n); uint32 get_pos(); }; @@ -45,7 +46,7 @@ struct strmRequest { strmRequest *prev; StreamFile *srcFile; int32 strmSize; - Handle strmHandle; + MemHandle strmHandle; uint8 *strmBuff; uint8 *endStrmBuff; uint8 *strmHead; @@ -59,6 +60,13 @@ struct strmRequest { }; extern bool f_stream_Init(); +extern void f_stream_Shutdown(); + +extern strmRequest *f_stream_Open(StreamFile *srcFile, int32 fileOffset, int32 strmMinBuffSize, int32 strmBuffSize, + int32 numBlocksToRead, int32 *blockSizeArray, int32 initialRead, bool wrapStream); +extern int32 f_stream_Read(strmRequest *myStream, uint8 **dest, int32 numBytes); +extern void f_stream_Close(strmRequest *myStream); +extern void f_stream_Process(int32 numToProcess); } // namespace M4 diff --git a/engines/m4/graphics/gr_series.cpp b/engines/m4/graphics/gr_series.cpp index 91d9988c332..f106cdb9683 100644 --- a/engines/m4/graphics/gr_series.cpp +++ b/engines/m4/graphics/gr_series.cpp @@ -47,4 +47,10 @@ bool series_show_frame(int32 spriteHash, int32 index, Buffer *destBuff, int32 x, return series_draw_sprite(spriteHash, index, destBuff, x, y); } +machine *series_play_xy(char *seriesName, int32 loopCount, uint32 flags, + int32 x, int32 y, int32 s, int32 layer, int32 frameRate, int16 triggerNum) { + error("TODO: series_play_xy"); + return nullptr; +} + } // namespace M4 diff --git a/engines/m4/graphics/gr_series.h b/engines/m4/graphics/gr_series.h index 49c7e591c58..7359ef12434 100644 --- a/engines/m4/graphics/gr_series.h +++ b/engines/m4/graphics/gr_series.h @@ -27,6 +27,12 @@ namespace M4 { +constexpr uint32 FORWARD = 0; +constexpr uint32 PINGPONG = 1; +constexpr uint32 BACKWARD = 2; +constexpr uint32 STICK = 4; +constexpr uint32 NO_TOSS = 8; + extern int32 series_load(const char *seriesName, int32 assetIndex, RGB8 *myPal); extern void series_unload(int32 assetIndex); extern bool series_draw_sprite(int32 spriteHash, int32 index, Buffer *destBuff, int32 x, int32 y); diff --git a/engines/m4/graphics/krn_pal.cpp b/engines/m4/graphics/krn_pal.cpp index f3a65ff2733..70344c9c854 100644 --- a/engines/m4/graphics/krn_pal.cpp +++ b/engines/m4/graphics/krn_pal.cpp @@ -19,20 +19,501 @@ * */ +#include "m4/m4_types.h" +#include "m4/adv_r/adv_control.h" +#include "m4/core/errors.h" +#include "m4/core/imath.h" +#include "m4/events/keys.h" #include "m4/graphics/krn_pal.h" #include "m4/graphics/gr_pal.h" +#include "m4/graphics/gr_series.h" +#include "m4/gui/gui_sys.h" +#include "m4/gui/gui_vmng.h" #include "m4/vars.h" namespace M4 { -void krn_ChangeBufferLuminance(Buffer *target, int32 percent) { - int32 x, y, r, g, b, i; - uint8 *inverse_palette, pixel, *tempPtr; - frac16 fracPercent; - RGB8 *pal; - uint8 luminancePal[256]; +#define _GP(X) _G(krnPal)._##X - //paremeter verification +#define BACKGROUND_HEIGHT (int32)639 + +#define GREY_START 32 +#define NUM_GREYS 32 // gotta have 32 greys to fade to (hardcoded algorithm) +#define GREY_END GREY_START+NUM_GREYS + +#define FREE_START GREY_END+1 +#define FREE_END 255 +#define NUM_FREE FREE_END-(FREE_START)+1 + +static void krn_pal_game_task() { + int32 status; + + ScreenContext *game_buff_ptr = vmng_screen_find(_G(gameDrawBuff), &status); + if (!game_buff_ptr) + error_show(FL, 'BUF!'); + + CycleEngines(_G(game_bgBuff)->get_buffer(), &(_G(currentSceneDef).depth_table[0]), + _G(screenCodeBuff), (uint8 *)&_G(master_palette)[0], _G(inverse_pal)->get_ptr(), true); + + _G(inverse_pal)->release(); + _G(game_bgBuff)->release(); + + _G(digi).task(); + _G(midi).task(); + + gui_system_event_handler(); + + f_stream_Process(2); +} + +static int32 screen_height(Buffer *grey_screen) { + return imath_min(BACKGROUND_HEIGHT + _G(kernel).letter_box_y, grey_screen->h); +} + +static void grey_fade(RGB8 *pal, int32 to_from_flag, int32 from, int32 to, int32 steps, int32 delay) { + int i; +#ifdef TODO + int j; + clock_t begin_time; + RGB8 *working = (RGB8 *)mem_alloc(sizeof(RGB8) * 256, STR_FADEPAL); + + // perform the fade + for (i = 1; i < steps; i++) { + for (j = from; j <= to; j++) { + if (to_from_flag == TO_GREY) { // fade to grey from full color + working[j].r = (Byte)((int)pal[j].r + ((((int)_GP(fadeToMe)[j].r - (int)pal[j].r) * i) / steps)); + working[j].g = (Byte)((int)pal[j].g + ((((int)_GP(fadeToMe)[j].g - (int)pal[j].g) * i) / steps)); + working[j].b = (Byte)((int)pal[j].b + ((((int)_GP(fadeToMe)[j].b - (int)pal[j].b) * i) / steps)); + } else if (to_from_flag == TO_COLOR) { // fade from grey to full color + working[j].r = (Byte)((int)_GP(fadeToMe)[j].r + ((((int)pal[j].r - (int)_GP(fadeToMe)[j].r) * i) / steps)); + working[j].g = (Byte)((int)_GP(fadeToMe)[j].g + ((((int)pal[j].g - (int)_GP(fadeToMe)[j].g) * i) / steps)); + working[j].b = (Byte)((int)_GP(fadeToMe)[j].b + ((((int)pal[j].b - (int)_GP(fadeToMe)[j].b) * i) / steps)); + } else { //fade from grey to black + working[j].r = (Byte)((int)_GP(fadeToMe)[j].r - ((((int)_GP(fadeToMe)[j].r) * i) / steps)); + working[j].g = (Byte)((int)_GP(fadeToMe)[j].g - ((((int)_GP(fadeToMe)[j].g) * i) / steps)); + working[j].b = (Byte)((int)_GP(fadeToMe)[j].b - ((((int)_GP(fadeToMe)[j].b) * i) / steps)); + } + } + + gr_pal_set_range(working, from, to - from + 1); ///set pal 21-255 + + // Time delay of "delay" milliseconds + begin_time = clock(); + while ((((clock() - begin_time) * 1000) / CLOCKS_PER_SEC) < delay) + krn_pal_game_task(); + } +#else + error("TODO: grey_fade"); +#endif + + // Eliminate round off error + if (to_from_flag == TO_GREY) { + gr_pal_set_range(_GP(fadeToMe), from, to - from + 1); ///set pal 21-255 + } else if (to_from_flag == TO_COLOR) { + gr_pal_set_range(pal, from, to - from + 1); ///set pal 21-255 + } else { + for (i = from; i <= to; i++) { + pal[i].r = pal[i].g = pal[i].b = 0; + } + gr_pal_set_range(pal, from, to - from + 1); ///set pal 21-255 + } +#ifdef TODO + mem_free(working); +#endif +} + + + +// screen is the currently displayed screen +// screenPicture is the data to restore the screen with +// note: color 0 doesn't fade. + +static void create_luminance_map(RGB8 *pal) { + for (int i = GREY_START; i <= FREE_END; i++) { + Byte luminance = (Byte)((pal[i].r + pal[i].g + pal[i].b) / 3); + _GP(fadeToMe)[i].g = (Byte)imath_min(255, luminance); // MattP new green screen! + _GP(fadeToMe)[i].r = _GP(fadeToMe)[i].b = 0; + } +} + + +static HotkeyCB remember_esc_key; + + +// finds the best macthes for the in the greys in the grey ramp range using the free range greys +// used to map greys out of the grey ramp area, and then again to map the grey ramp out of the grey ramp area! +static void make_translation_table(RGB8 *pal) { + int32 i, j, bestMatch, minDist; + + for (i = 0; i < NUM_GREYS; i++) { + bestMatch = FREE_START; // assume the first of the free indexes is best match to start with + minDist = 255; // assume that it's really far away to start with + + if (!(i & 0x3ff)) { + digi_read_another_chunk(); + midi_loop(); + } + + // look for best match in the free indexes for the greys in GREY_START-GREY_END range (we need these available) + int32 matchGrey = pal[GREY_START + i].g; // MattP use green instead of red cause we're having a green screen + + for (j = FREE_START; j <= FREE_END; j++) { + int32 tryGrey = pal[j].g; + if (imath_abs(tryGrey - matchGrey) < minDist) { + minDist = imath_abs(tryGrey - matchGrey); + bestMatch = j; + } + if (minDist == 0) + break; // no need to continue searching if we found a perfect match + } + _GP(translation)[i] = (uint8)bestMatch; + } +} + +void krn_fade_to_grey(RGB8 *pal, int32 steps, int32 delay) { + int32 i, j, bestMatch, minDist; + uint8 *tempPtr; + + if (_G(kernel).fading_to_grey) { + return; + } + _G(kernel).fading_to_grey = true; + + Buffer *grey_screen = _G(gameDrawBuff)->get_buffer(); + + _GP(fadeToMe) = (RGB8 *)mem_alloc(sizeof(RGB8) * 256, STR_FADEPAL); + _GP(trick) = (RGB8 *)mem_alloc(sizeof(RGB8) * 256, STR_FADEPAL); + _GP(picPal) = (RGB8 *)mem_alloc(sizeof(RGB8) * 256, STR_FADEPAL); + + memcpy(_GP(picPal), pal, sizeof(RGB8) * 256); + create_luminance_map(pal); + + grey_fade(pal, TO_GREY, 21, 255, steps, delay); + + // make _GP(translation) table + // to translate colors using entries 59-255 into 21-58 range + + for (i = 0; i < 64; i++) { + bestMatch = 63; + minDist = 255; + + if (!(i & 0x3ff)) { + _G(digi).task(); + _G(midi).task(); + } + + for (j = 59; j <= 255; j++) { + if (imath_abs((_GP(fadeToMe)[j].r >> 2) - i) < minDist) { + minDist = imath_abs((_GP(fadeToMe)[j].r >> 2) - i); + bestMatch = j; + } + if (minDist == 0) + break; // no need to continue searching if we found a perfect match + } + + _GP(translation)[i] = (uint8)bestMatch; + } + + // palette now grey scale. Remap any pixels which are in the range 21-58 to the range 53-255 + // because we need to use those palette entries soon + + tempPtr = grey_screen->data; + // note: this loop should be y0 to y1, x0 to x1, not a stride*h loop. + for (i = 0; i < (grey_screen->stride * grey_screen->h); i++) { + if ((*tempPtr >= GREY_START) && (*tempPtr <= GREY_END)) { + // must move the pixel index to the best match in FREE_START-FREE_END range with _GP(translation) table + *tempPtr = _GP(translation)[*tempPtr - GREY_START]; + } + tempPtr++; + + if (!(i & 0x3ff)) { + _G(digi).task(); + _G(midi).task(); + } + + } + RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y); + + // make new trickPal with grey-scale ramp entries and load it into VGA registers + memcpy(_GP(trick), _GP(fadeToMe), sizeof(RGB8) * 256); // MattP _GP(trick) pal is the greyed version plus the grey ramp overlayed on top + int8 grey_step = 256 / NUM_GREYS; + int8 grey_ramp = 0; + for (i = GREY_START; i <= GREY_END; i++) { + // _GP(trick)[i].r = _GP(trick)[i].g = _GP(trick)[i].b = (Byte) (grey_ramp); old grey ramp + _GP(trick)[i].g = (Byte)(grey_ramp); // MattP new green screen + _GP(trick)[i].r = _GP(trick)[i].b = 0; + grey_ramp += grey_step; + } + gr_pal_set_range(_GP(trick), GREY_START, NUM_GREYS); ///set pal GREY_START-GREY_END + + remap_buffer_with_luminance_map(grey_screen, 0, 0, grey_screen->W - 1, screen_height(grey_screen) - 1); + _G(gameDrawBuff)->release(); + RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y); +} + + +void krn_fade_from_grey(RGB8 *pal, int32 steps, int32 delay, int32 fadeType) { + uint8 *tempPtr; + int32 i; + + if (!_G(kernel).fading_to_grey) { + return; + } + + // Get the screen + Buffer *grey_screen = _G(gameDrawBuff)->get_buffer(); + + // load original faded greys into the free indexes (no pixels have these indexs yet) + gr_pal_set_range(_GP(fadeToMe), FREE_START, NUM_FREE); // Load _GP(fadeToMe) colors into VGA + + make_translation_table(_GP(trick)); // Mattp this is used in fade_to_grey too! + + // for every pixel in the screen, move any pixel in the GREY_START-GREY_END range out in to the free range + tempPtr = grey_screen->data; + // note: this loop should be y0 to y1, x0 to x1, not a stride*h loop. + for (i = 0; i < (grey_screen->stride * grey_screen->h); ++i) { + if (!(i & 0x3ff)) { + _G(digi).task(); + _G(midi).task(); + } + + // if the pixel is within the GREY range, move it to where the _GP(translation) table says + if ((*tempPtr >= GREY_START) && (*tempPtr <= GREY_END)) { + *tempPtr = _GP(translation)[*tempPtr - GREY_START]; + } + tempPtr++; + } + + // Term_message ("remaped indexes out of grey ramp"); + RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y); + // Term_message ("setting grey ramp indexes back to picture greys"); + gr_pal_set_range(_GP(fadeToMe), GREY_START, NUM_GREYS); // get the rest of the original re-luminance colors + + //recopy screenPicture to screen to restore original pixels + krn_UnsetGreyVideoMode(); + RestoreScreens(0, 0, MAX_VIDEO_X, MAX_VIDEO_Y); + + memcpy(pal, _GP(picPal), sizeof(RGB8) * 256); + + ws_RefreshWoodscriptBuffer(_G(game_bgBuff)->get_buffer(), &(_G(currentSceneDef).depth_table[0]), _G(screenCodeBuff), + (uint8 *)&_G(master_palette)[0], _G(inverse_pal)->get_ptr()); + _G(game_bgBuff)->release(); + _G(inverse_pal)->release(); + + RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y); + + grey_fade(pal, fadeType, GREY_START, FREE_END, steps, delay); + + mem_free((char *)_GP(trick)); + mem_free((char *)_GP(fadeToMe)); + mem_free((char *)_GP(picPal)); + _G(kernel).fading_to_grey = false; + _G(gameDrawBuff)->release(); + gr_pal_set(_G(master_palette)); +} + +bool examining_inventory_object = false; + + +void kernel_examine_inventory_object(char *picName, RGB8 *pal, int steps, int delay, + int32 x, int32 y, int32 triggerNum, char *digi_name, int32 digi_trigger) { + + remember_esc_key = GetSystemHotkey(KEY_ESCAPE); + RemoveSystemHotkey(KEY_ESCAPE); + + interface_hide(); + + _GP(exam_saved_hotspots) = _G(currentSceneDef).hotspots; + _G(currentSceneDef).hotspots = NULL; + + _GP(myFadeTrigger) = kernel_trigger_create(triggerNum); + + krn_fade_to_grey(pal, steps, delay); + + _GP(seriesHash) = series_load(picName, -1, pal); // Preload sprite so we can unload it + gr_pal_set_range(pal, FREE_START, 197); // Set that series colors into VGA + RestoreScreens(MIN_VIDEO_X, MIN_VIDEO_Y, MAX_VIDEO_X, MAX_VIDEO_Y); + + Buffer *grey_screen = _G(gameDrawBuff)->get_buffer(); + krn_SetGreyVideoMode( + // Grey rectangle + 0, 0, MAX_VIDEO_X, screen_height(grey_screen) + _G(kernel).letter_box_y, + // Color rectangle + x, y, x + ws_get_sprite_width(_GP(seriesHash), 0) - 1, y + ws_get_sprite_height(_GP(seriesHash), 0) - 1); + _G(gameDrawBuff)->release(); + + // Play the sprite series as a loop + int32 status; + ScreenContext *game_buff_ptr = vmng_screen_find(_G(gameDrawBuff), &status); + _GP(seriesAnim8) = series_play_xy(picName, -1, FORWARD, + x - game_buff_ptr->x1, y - game_buff_ptr->y1, 100, 0, 7, -1); + + if (digi_name) { + digi_play(digi_name, 1, 255, digi_trigger); + } + + player_set_commands_allowed(true); + + CycleEngines(_G(game_bgBuff)->get_buffer(), &(_G(currentSceneDef).depth_table[0]), + _G(screenCodeBuff), (uint8 *)&_G(master_palette)[0], _G(inverse_pal)->get_ptr(), true); + + game_pause(true); + + _G(inverse_pal)->release(); + _G(game_bgBuff)->release(); + + PauseEngines(); +} + +void kernel_unexamine_inventory_object(RGB8 *pal, int steps, int delay) { + if (!_GP(seriesAnim8) || _GP(seriesHash) < 0) + return; + + player_set_commands_allowed(false); + game_pause(false); + UnpauseEngines(); + + TerminateMachine(_GP(seriesAnim8)); + series_unload(_GP(seriesHash)); + _GP(seriesAnim8) = NULL; + _GP(seriesHash) = 0; + + Buffer *grey_screen = _G(gameDrawBuff)->get_buffer(); + krn_SetGreyVideoMode(0, 0, MAX_VIDEO_X, screen_height(grey_screen) + _G(kernel).letter_box_y, -1, -1, -1, -1); + _G(gameDrawBuff)->release(); + + krn_pal_game_task(); + + krn_fade_from_grey(pal, steps, delay, TO_COLOR); + + krn_pal_game_task(); + + // set in kernel_examine_inventory_object (above) + kernel_trigger_dispatch(_GP(myFadeTrigger)); + + // gr_pal_set(master_palette); + + RestoreScreens(0, 0, MAX_VIDEO_X, MAX_VIDEO_Y); + + _G(currentSceneDef).hotspots = _GP(exam_saved_hotspots); + + interface_show(); + AddSystemHotkey(KEY_ESCAPE, remember_esc_key); +} + + +// This is an inplace remap +// _GP(fadeToMe) must already have been set up to correspond to the image on the screen +void remap_buffer_with_luminance_map(Buffer *src, int32 x1, int32 y1, int32 x2, int32 y2) { + uint8 *ptr; + int32 x, y; + if ((!src) || (!src->data)) return; + if ((x2 - x1 < 0) || (y2 - y1 < 0)) return; + if (x2 - x1 + 1 > src->W) x2 = src->W - 1; + if (y2 - y1 + 1 > src->h) y2 = src->h - 1; + + x2 -= x1; + y2 -= y1; + for (y = 0; y <= y2; y++) { + ptr = &src->data[(y + y1) * src->stride + x1]; + for (x = 0; x <= x2; x++) // for each pixel in row + + // remap the greyed out pixel to the closest grey in GREY_START to GREY_END range + // shift right 3, takes a 255 value and makes it out of 32 (the number of greys in reduced grey ramp) + ptr[x] = (uint8)(GREY_START + (_GP(fadeToMe)[ptr[x]].g >> 3)); // MattP use green instead of red cause we're having a green screen + + if (!(y & 0xff)) { + _G(digi).task(); + _G(midi).task(); + } + } +} + +void krn_SetGreyVideoMode(int32 grey_x1, int32 grey_y1, int32 grey_x2, int32 grey_y2, int32 color_x1, int32 color_y1, int32 color_x2, int32 color_y2) { + _GP(greyAreaX1) = grey_x1; + _GP(greyAreaY1) = grey_y1; + _GP(greyAreaX2) = grey_x2; + _GP(greyAreaY2) = grey_y2; + + _GP(colorAreaX1) = color_x1; + _GP(colorAreaY1) = color_y1; + _GP(colorAreaX2) = color_x2; + _GP(colorAreaY2) = color_y2; + + _GP(greyVideoMode) = true; +} + +void krn_UnsetGreyVideoMode() { + _GP(greyAreaX1) = -1; + _GP(greyAreaY1) = -1; + _GP(greyAreaX2) = -1; + _GP(greyAreaY2) = -1; + + _GP(colorAreaX1) = -1; + _GP(colorAreaY1) = -1; + _GP(colorAreaX2) = -1; + _GP(colorAreaY2) = -1; + + _GP(greyVideoMode) = false; +} + +bool krn_GetGreyMode() { + return _GP(greyVideoMode); +} + +void krn_UpdateGreyArea(Buffer *greyOutThisBuffer, int32 scrnX, int32 scrnY, int32 greyX1, int32 greyY1, int32 greyX2, int32 greyY2) { + bool finished; + int32 x1, y1, x2, y2; + + if ((!_GP(greyVideoMode)) || (!greyOutThisBuffer) || (!greyOutThisBuffer->data)) { + return; + } + x1 = imath_max(greyX1 + scrnX, _GP(greyAreaX1)); + y1 = imath_max(greyY1 + scrnY, _GP(greyAreaY1)); + x2 = imath_min(greyX2 + scrnX, _GP(greyAreaX2)); + y2 = imath_min(greyY2 + scrnY, _GP(greyAreaY2)); + if ((x1 > x2) || (y1 > y2)) return; + finished = false; + if (!finished) { + if (y1 < _GP(colorAreaY1)) { + remap_buffer_with_luminance_map(greyOutThisBuffer, + x1 - scrnX, y1 - scrnY, x2 - scrnX, imath_min(y2, _GP(colorAreaY1) - 1) - scrnY); + y1 = imath_min(y2, _GP(colorAreaY1)); + if (y1 >= y2) finished = true; + } + } + if (!finished) { + if (y2 > _GP(colorAreaY2)) { + remap_buffer_with_luminance_map(greyOutThisBuffer, + x1 - scrnX, imath_max(y1, _GP(colorAreaY2) + 1) - scrnY, x2 - scrnX, y2 - scrnY); + y2 = imath_max(y1, _GP(colorAreaY2)); + if (y1 >= y2) finished = true; + } + } + if (!finished) { + if (x1 < _GP(colorAreaX1)) { + remap_buffer_with_luminance_map(greyOutThisBuffer, + x1 - scrnX, y1 - scrnY, imath_min(x2, _GP(colorAreaX1) - 1) - scrnX, y2 - scrnY); + x1 = imath_min(x2, _GP(colorAreaX1)); + if (x1 >= x2) finished = true; + } + } + if (!finished) { + if (x2 > _GP(colorAreaX2)) { + remap_buffer_with_luminance_map(greyOutThisBuffer, + imath_max(x1, _GP(colorAreaX2) + 1) - scrnX, y1 - scrnY, x2 - scrnX, y2 - scrnY); + } + } +} + +void krn_ChangeBufferLuminance(Buffer *target, int32 percent) { + int32 x, y, r, g, b, i; + uint8 *inverse_palette, pixel, *tempPtr; + frac16 fracPercent; + RGB8 *pal; + uint8 luminancePal[256]; + + // Paremeter verification if ((!target) || (!target->data)) { return; } @@ -46,17 +527,17 @@ void krn_ChangeBufferLuminance(Buffer *target, int32 percent) { return; } - //calculate the frac16 form of the percent + // Calculate the frac16 form of the percent fracPercent = (percent * 255) / 100; - //get the palette and the inverse palette + // Get the palette and the inverse palette pal = &_G(master_palette)[0]; inverse_palette = _G(inverse_pal)->get_ptr(); if ((!pal) || (!inverse_palette)) { return; } - //calculate the luminance Pal table + // Calculate the luminance Pal table for (i = 0; i < 256; i++) { r = ((((pal[i].r * fracPercent) >> 10) >> 1)) & 0x1f; g = ((((pal[i].g * fracPercent) >> 10) >> 1)) & 0x1f; @@ -64,8 +545,8 @@ void krn_ChangeBufferLuminance(Buffer *target, int32 percent) { luminancePal[i] = inverse_palette[(r << 10) + (g << 5) + b]; } - // note: this loop should be y0 to y1, x0 to x1, not a stride*h loop. - //loop through every pixel replacing it with the index into the luminance table + // Note: this loop should be y0 to y1, x0 to x1, not a stride*h loop. + // Loop through every pixel replacing it with the index into the luminance table tempPtr = target->data; for (y = 0; y < target->h; y++) { for (x = 0; x < target->stride; x++) { @@ -78,4 +559,340 @@ void krn_ChangeBufferLuminance(Buffer *target, int32 percent) { _G(inverse_pal)->release(); } + +static void pal_fade_callback(frac16 myMessage) { + _G(pal_fade_in_progress) = false; + kernel_trigger_dispatch((uint32)myMessage); +} + +void pal_fade_init(RGB8 *origPalette, int32 firstPalEntry, int32 lastPalEntry, + int32 targetPercent, int32 numTicks, uint32 triggerNum) { + if ((!origPalette) || (firstPalEntry < 0) || (lastPalEntry > 255) || (firstPalEntry > lastPalEntry)) + return; + if ((targetPercent < 0) || (targetPercent > 100)) + return; + + _GP(myFadeReq) = true; + _GP(myFadeFinished) = false; + _GP(myFadeStartTime) = timer_read_60(); + _GP(myFadeEndDelayTime) = timer_read_60(); + _GP(myFadeStartIndex) = firstPalEntry; + _GP(myFadeEndIndex) = lastPalEntry; + _GP(myFadeEndTime) = _GP(myFadeStartTime) + numTicks; + _GP(myFadeTrigger) = kernel_trigger_create(triggerNum); + _GP(myFadeStartPercentFrac) = _GP(myFadeCurrPercentFrac); + _GP(myFadePercentFrac) = DivSF16(targetPercent << 16, 0x640000); + + // Disable_end_user_hot_keys(); + _G(pal_fade_in_progress) = true; +} + +static void pal_fade_update(RGB8 *origPalette) { + int32 i, currTime; + frac16 tempFrac, tempFrac2; + + currTime = timer_read_60(); + + if (currTime >= _GP(myFadeEndDelayTime)) { // If the delay has expired, fade more + if (currTime >= _GP(myFadeEndTime)) { + tempFrac2 = _GP(myFadePercentFrac); + _GP(myFadeStartPercentFrac) = _GP(myFadePercentFrac); + _GP(myFadeFinished) = true; + } else if (currTime <= _GP(myFadeStartTime)) return; + else { + tempFrac = DivSF16((currTime - _GP(myFadeStartTime)) << 16, (_GP(myFadeEndTime) - _GP(myFadeStartTime)) << 16); + tempFrac2 = MulSF16(tempFrac, _GP(myFadePercentFrac) - _GP(myFadeStartPercentFrac)) + _GP(myFadeStartPercentFrac); + } + + _GP(myFadeCurrPercentFrac) = tempFrac2; + + for (i = _GP(myFadeStartIndex); i <= _GP(myFadeEndIndex); i++) { + _GP(myFXPalette)[i].r = (Byte)(MulSF16(origPalette[i].r << 16, tempFrac2) >> 16); + _GP(myFXPalette)[i].g = (Byte)(MulSF16(origPalette[i].g << 16, tempFrac2) >> 16); + _GP(myFXPalette)[i].b = (Byte)(MulSF16(origPalette[i].b << 16, tempFrac2) >> 16); + } + + // Recalculate the end delay time again + _GP(myFadeEndDelayTime) = currTime + _GP(myFadeDelayTicks); // Recalculate the end delay time again + + // Must reresh the DAC + _GP(myFadeDACrefresh) = true; + } +} + +void clear_DAC() { + RGB8 color; + + color.r = color.b = color.g = 0; + for (int i = 0; i < 256; i++) + gr_pal_set_entry(i, &color); +} + +void pal_fade_set_start(RGB8 *origPalette, int32 percent) { + pal_fade_init(origPalette, _G(kernel).first_fade, 255, percent, 0, (uint)-1); + pal_fade_update(origPalette); + pal_fx_update(); +} + +static void pal_cycle_callback(frac16 myMessage) { + kernel_trigger_dispatch((uint32)myMessage); +} + +void pal_cycle_init(int32 firstPalEntry, int32 lastPalEntry, + int32 delayTicks, int32 totalTicks, uint32 triggerNum) { + if ((firstPalEntry < 0) || (lastPalEntry > 255) || (firstPalEntry > lastPalEntry)) { + // This should generate an error + return; + } + if (delayTicks <= 0) { + // This should generate an error (Nick debug plarg MattP) + return; + } + + _GP(myCycleReq) = true; + _GP(myCycleFinished) = false; + _GP(myCycleDelayTicks) = delayTicks; + _GP(myCycleStartTime) = timer_read_60(); + _GP(myCycleEndDelayTime) = timer_read_60(); + _GP(myCycleStartIndex) = firstPalEntry; + _GP(myCycleEndIndex) = lastPalEntry; + _GP(myCycleTrigger) = kernel_trigger_create(triggerNum); // Returned when _GP(myCycleEndTime) is reached + + if (totalTicks > 0) { // If totalTicks > 0, calculate end time + _GP(myCycleEndTime) = _GP(myCycleStartTime) + totalTicks; + _GP(myCycleNeverStopCycling) = false; + + } else if (totalTicks < 0) { // If totalTicks < 0, never stop the cycling + _GP(myCycleNeverStopCycling) = true; + + } else { // If totalTicks is 0, stop cycling now + _GP(myCycleReq) = false; + _GP(myCycleFinished) = true; + } +} + +bool pal_cycle_active() { + return _GP(myCycleReq); +} + +void pal_cycle_stop() { + _GP(myCycleReq) = false; +} + +void pal_cycle_resume() { + _GP(myCycleReq) = true; +} + +static void pal_cycle_update() { + int32 i, currTime; + RGB8 firstColour; + + currTime = timer_read_60(); // Get current time + + if (_GP(myCycleNeverStopCycling) == false) { // If there is an end time to get to... + + if (currTime >= _GP(myCycleEndTime)) { // See if we have reached it + _GP(myCycleFinished) = true; // Mark cycling as finished + return; // Return + } + } else { + // See if we should colour cycle right now + if (currTime >= _GP(myCycleEndDelayTime)) { // If the delay has expired, colour cycle + // Cycle the master palette + firstColour.r = _G(master_palette)[_GP(myCycleStartIndex)].r; // Remember first colour + firstColour.g = _G(master_palette)[_GP(myCycleStartIndex)].g; + firstColour.b = _G(master_palette)[_GP(myCycleStartIndex)].b; + for (i = _GP(myCycleStartIndex); i < _GP(myCycleEndIndex); ++i) { // Shift colours down one in palette + _G(master_palette)[i].r = _G(master_palette)[i + 1].r; + _G(master_palette)[i].g = _G(master_palette)[i + 1].g; + _G(master_palette)[i].b = _G(master_palette)[i + 1].b; + } + _G(master_palette)[_GP(myCycleEndIndex)].r = firstColour.r; // Set last colour to the first colour + _G(master_palette)[_GP(myCycleEndIndex)].g = firstColour.g; + _G(master_palette)[_GP(myCycleEndIndex)].b = firstColour.b; + + // Then cycle the FX palette + firstColour.r = _GP(myFXPalette)[_GP(myCycleStartIndex)].r; // Remember first colour + firstColour.g = _GP(myFXPalette)[_GP(myCycleStartIndex)].g; + firstColour.b = _GP(myFXPalette)[_GP(myCycleStartIndex)].b; + for (i = _GP(myCycleStartIndex); i < _GP(myCycleEndIndex); ++i) { // Shift colours down one in palette + _GP(myFXPalette)[i].r = _GP(myFXPalette)[i + 1].r; + _GP(myFXPalette)[i].g = _GP(myFXPalette)[i + 1].g; + _GP(myFXPalette)[i].b = _GP(myFXPalette)[i + 1].b; + } + _GP(myFXPalette)[_GP(myCycleEndIndex)].r = firstColour.r; // Set last colour to the first colour + _GP(myFXPalette)[_GP(myCycleEndIndex)].g = firstColour.g; + _GP(myFXPalette)[_GP(myCycleEndIndex)].b = firstColour.b; + + + // Recalculate the end delay time again + _GP(myCycleEndDelayTime) = currTime + _GP(myCycleDelayTicks); // Recalculate the end delay time again + + // must reresh the DAC + _GP(myCycleDACrefresh) = true; + } + } +} + +// pal_fx_update() is called each game loop +// +void pal_fx_update() { + int32 startA = 0, endA = 0, startB = 0, endB = 0, startDAC = 0, endDAC = 0; + + if (!_GP(myCycleReq) && !_GP(myFadeReq)) + // Crap out quickly if no effects required + return; + + // Perform any effect required and track index ranges + if (_GP(myCycleReq)) { + pal_cycle_update(); // Do the cycling (cycles master_palette and _GP(myFXPalette)) + if (_GP(myCycleDACrefresh)) { // If it needs the DAC to be refreshed, + startA = _GP(myCycleStartIndex); // remember the range + endA = _GP(myCycleEndIndex); + _GP(myCycleDACrefresh) = false; + } + } + + if (_GP(myFadeReq)) { + pal_fade_update(&_G(master_palette)[0]); // Do the fading (sets myFXPalette to faded master_palette) + if (_GP(myFadeDACrefresh)) { // If it needs the DAC to be refreshed, + startB = _GP(myFadeStartIndex); // remember the range + endB = _GP(myFadeEndIndex); + _GP(myFadeDACrefresh) = false; + } + } + + // Check ranges to perform minimum calls of gr_pal_set_range() (to minimize snow on monitor due to OUT instructions) + if (endA < startB || endB < startA) { // if A and B ranges don't overlap + + if (!(startA == 0 && endA == 0)) // if this is not the degenerate case (just the transparent color) + gr_pal_set_range(&_GP(myFXPalette)[0], startA, endA - startA + 1); // set A range of the DAC + + if (!(startB == 0 && endB == 0)) // if this is not the degenerate case (just the transparent color) + gr_pal_set_range(&_GP(myFXPalette)[0], startB, endB - startB + 1); // set B range of the DAC + + } else { + // They overlap, so find the extent of the overlap + (startA < startB) ? (startDAC = startA) : (startDAC = startB); // which start is less + (endA > endB) ? (endDAC = endA) : (endDAC = endB); // which end is more + + if (!(startDAC == 0 && endDAC == 0)) // if this is not the degenerate case (just the transparent color) + gr_pal_set_range(&_GP(myFXPalette)[0], startDAC, endDAC - startDAC + 1); // set the whole range of the DAC + } + + // Turn off flags and call callbacks if effects are finished + if (_GP(myFadeReq) && _GP(myFadeFinished)) { + _GP(myFadeReq) = false; + pal_fade_callback(_GP(myFadeTrigger)); + } + + if (_GP(myCycleReq) && _GP(myCycleFinished)) { + _GP(myCycleReq) = false; + pal_cycle_callback(_GP(myCycleTrigger)); + } +} + +//========================================================================================== +// +// DAC_tint_effect() is used to effect the screen colours (not the master palette) temporarily +// until something else updates the DAC e.g. refresh_DAC() +// + +void DAC_tint_range(RGB8 *tintColor, int32 percent, int32 firstPalEntry, int32 lastPalEntry, bool transparent) { + int32 i; + int32 r, g, b, dr, dg, db; + RGB8 color, targetColor; + int32 percent_r, percent_g, percent_b; + + if ((firstPalEntry < 0) || (lastPalEntry > 255) || (firstPalEntry > lastPalEntry)) { + // this should generate an error (Nick debug plarg MattP) + term_message("*** palette index error"); + return; + } + + term_message("Colour tint DAC to: %d %d %d, %d percent, range (%d - %d)", + tintColor->r, tintColor->g, tintColor->b, percent, firstPalEntry, lastPalEntry); // debug + percent = DivSF16(percent << 16, 100 << 16); // convert percent to frac16 format + + targetColor.r = tintColor->r; + targetColor.g = tintColor->g; + targetColor.b = tintColor->b; + + term_message("Doing palette....."); + + if (!transparent) { + for (i = firstPalEntry; i <= lastPalEntry; ++i) { + + // calculate deltas for RGB's and put them in frac16 format + dr = (targetColor.r - _G(master_palette)[i].r) << 16; + dg = (targetColor.g - _G(master_palette)[i].g) << 16; + db = (targetColor.b - _G(master_palette)[i].b) << 16; + + // new = orig + (delta * percent) + r = _G(master_palette)[i].r + (MulSF16(percent, dr) >> 16); + g = _G(master_palette)[i].g + (MulSF16(percent, dg) >> 16); + b = _G(master_palette)[i].b + (MulSF16(percent, db) >> 16); + + // check for under/overflow + if (r > 255) r = 255; if (r < 0) r = 0; + if (g > 255) g = 255; if (g < 0) g = 0; + if (b > 255) b = 255; if (b < 0) b = 0; + + color.r = (byte)r; + color.g = (byte)g; + color.b = (byte)b; + + gr_pal_set_entry(i, &color); // set the new colour to the DAC + } + + } else { + + // This is for filtering colors. For example, a completely red filter + // (255, 0, 0) will block out the blue and green parts of the palette. + // 50% of the same filter will block out only 50% of the blue and + // green, but leaving all of the rest blue. + for (i = firstPalEntry; i <= lastPalEntry; ++i) { + + // Converting rgb to a frac16 ( << 16) dividing by 256 ( >> 8) + // (the range of the palette values) + percent_r = (targetColor.r) << 8; + percent_g = (targetColor.g) << 8; + percent_b = (targetColor.b) << 8; + + // This is the difference between the color and the full effect + // of the filter at 100%, as a frac16. + dr = (_G(master_palette)[i].r << 16) - (MulSF16(percent_r, _G(master_palette)[i].r << 16)); + dg = (_G(master_palette)[i].g << 16) - (MulSF16(percent_g, _G(master_palette)[i].g << 16)); + db = (_G(master_palette)[i].b << 16) - (MulSF16(percent_b, _G(master_palette)[i].b << 16)); + + // Scaling the effect to the right percentage. This is a frac16. + dr = MulSF16(dr, percent); + dg = MulSF16(dg, percent); + db = MulSF16(db, percent); + + // Subtract the result to palette. + r = (_G(master_palette)[i].r - (dr >> 16)); + g = (_G(master_palette)[i].g - (dg >> 16)); + b = (_G(master_palette)[i].b - (db >> 16)); + + // check for under/overflow + if (r > 255) r = 255; if (r < 0) r = 0; + if (g > 255) g = 255; if (g < 0) g = 0; + if (b > 255) b = 255; if (b < 0) b = 0; + + color.r = (byte)r; + color.g = (byte)g; + color.b = (byte)b; + + gr_pal_set_entry(i, &color); // Set new colors to DAC. + } + } + +} + +void DAC_restore() { + term_message("DAC restored");//debug + gr_pal_set_range(&_G(master_palette)[0], 0, 256); +} + } // namespace M4 diff --git a/engines/m4/graphics/krn_pal.h b/engines/m4/graphics/krn_pal.h index f277520ce2d..d87efc36086 100644 --- a/engines/m4/graphics/krn_pal.h +++ b/engines/m4/graphics/krn_pal.h @@ -23,9 +23,85 @@ #define M4_GRAPHICS_KRN_PAL_H #include "m4/m4_types.h" +#include "m4/adv_r/adv_hotspot.h" +#include "m4/wscript/ws_machine.h" namespace M4 { +#define TO_GREY (int32)0 +#define TO_COLOR (int32)1 +#define TO_BLACK (int32)2 + +struct KernelPal_Globals { + RGB8 _myFXPalette[256]; + + bool _myCycleReq = false; + bool _myCycleFinished = true; + bool _myCycleDACrefresh = false; + int32 _myCycleDelayTicks = 6; // 10 times a second + int32 _myCycleStartTime = 0; + int32 _myCycleEndTime = 0; + int32 _myCycleEndDelayTime = 0; + int32 _myCycleStartIndex; + int32 _myCycleEndIndex; + uint32 _myCycleTrigger = 0; + int32 _myCycleNeverStopCycling = false; + + bool _myFadeReq = false; + bool _myFadeFinished = true; + bool _myFadeDACrefresh = false; + int32 _myFadeDelayTicks = 3; // 20 times a second + int32 _myFadeStartTime = 0; + int32 _myFadeEndTime = 0; + int32 _myFadeEndDelayTime = 0; + int32 _myFadeStartIndex = 0; + int32 _myFadeEndIndex = 0; + uint32 _myFadeTrigger = 0; + frac16 _myFadeStartPercentFrac = 0x10000; + frac16 _myFadeCurrPercentFrac = 0x10000; + frac16 _myFadePercentFrac = 0; + + HotSpotRec *_exam_saved_hotspots = nullptr; + + RGB8 *_fadeToMe = nullptr; + RGB8 *_trick = nullptr; + RGB8 *_picPal = nullptr; + int32 _seriesHash = 0; + machine *_seriesAnim8 = nullptr; + uint8 _translation[32]; // Only 32 greys in ramp + + int32 _colorAreaX1 = -1; + int32 _colorAreaY1 = -1; + int32 _colorAreaX2 = -1; + int32 _colorAreaY2 = -1; + + int32 _greyAreaX1 = -1; + int32 _greyAreaY1 = -1; + int32 _greyAreaX2 = -1; + int32 _greyAreaY2 = -1; + + bool _greyVideoMode = false; +}; + +extern void pal_fade_set_start(RGB8 *origPalette, int32 percent); +extern void pal_fade_init(RGB8 *origPalette, int32 firstPalEntry, int32 lastPalEntry, int32 targetPercent, int32 numTicks, uint32 triggerNum); + +extern void pal_cycle_init(int32 firstPalEntry, int32 lastPalEntry, int32 delayTicks, int32 totalTicks, uint32 triggerNum); +extern bool pal_cycle_active(); // Returns true if color cycling is on +extern void pal_cycle_stop(); // Stops color cycling +extern void pal_cycle_resume(); // Starts color cycling +extern void pal_fx_update(); // Handles fading and cycling MattP + +extern void kernel_examine_inventory_object(char *picName, RGB8 *pal, int steps, int delay, + int32 x, int32 y, int32 triggerNum, char *digi_name, int32 digi_trigger); +extern void kernel_unexamine_inventory_object(RGB8 *pal, int steps, int delay); + +extern void remap_buffer_with_luminance_map(Buffer *src, int32 x1, int32 y1, int32 x2, int32 y2); +extern void krn_SetGreyVideoMode(int32 grey_x1, int32 grey_y1, int32 grey_x2, int32 grey_y2, int32 color_x1, int32 color_y1, int32 color_x2, int32 color_y2); +extern void krn_UnsetGreyVideoMode(void); +extern bool krn_GetGreyMode(void); +extern void krn_UpdateGreyArea(Buffer *greyOutThisBuffer, int32 scrnX, int32 scrnY, + int32 greyX1, int32 greyY1, int32 greyX2, int32 greyY2); extern void krn_ChangeBufferLuminance(Buffer *target, int32 percent); } // namespace M4 diff --git a/engines/m4/graphics/rend.cpp b/engines/m4/graphics/rend.cpp new file mode 100644 index 00000000000..6c83d83eff3 --- /dev/null +++ b/engines/m4/graphics/rend.cpp @@ -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 3 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, see . + * + */ + +#include "common/textconsole.h" +#include "m4/graphics/rend.h" + +namespace M4 { + +void GetUpdateRectangle(int32 x, int32 y, int32 hot_x, int32 hot_y, int32 scale_x, int32 scale_y, int32 Width, int32 Height, M4Rect *UpdateRect) { + error("TODO: GetUpdateRectangle"); +} + +void render_sprite_to_8BBM(RendGrBuff *Destination, DrawRequestX *dr, RendCell *Frame, M4Rect *ClipRectangle, M4Rect *UpdateRect) { + error("TODO: render_sprite_to_8BBM"); +} + +} // namespace M4 diff --git a/engines/m4/graphics/rend.h b/engines/m4/graphics/rend.h new file mode 100644 index 00000000000..e0815d8e82d --- /dev/null +++ b/engines/m4/graphics/rend.h @@ -0,0 +1,68 @@ +/* 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 3 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, see . + * + */ + +#ifndef M4_GRAPHICS_REND_H +#define M4_GRAPHICS_REND_H + +#include "m4/m4_types.h" +#include "m4/gui/gui.h" + +namespace M4 { + +struct RGBcolor { + uint8 b, g, r; +}; + +struct RendGrBuff { + uint32 Width; + uint32 Height; + void *PixMap; +}; + +struct DrawRequestX { + int32 x; // X position relative to GrBuff(0, 0) + int32 y; // Y position relative to GrBuff(0, 0) + int32 scale_x; // X scale factor (can be negative for reverse draw) + int32 scale_y; // Y scale factor (can't be negative) + uint8 *depth_map; // Depth code array for destination (doesn't care if srcDepth is 0) + RGBcolor *Pal; // Palette for shadow draw (doesn't care if SHADOW bit is not set in Src.encoding) + uint8 *ICT; // Inverse Color Table (doesn't care if SHADOW bit is not set in Src.encoding) + uint8 depth; // Depth code for source (0 if no depth processing) +}; + +struct RendCell { + uint32 Pack; + uint32 Stream; + long hot_x; + long hot_y; + uint32 Width; + uint32 Height; + uint32 Comp; + uint32 Reserved[8]; + uint8 *data; +}; + +extern void GetUpdateRectangle(int32 x, int32 y, int32 hot_x, int32 hot_y, int32 scale_x, int32 scale_y, int32 Width, int32 Height, M4Rect *UpdateRect); +extern void render_sprite_to_8BBM(RendGrBuff *Destination, DrawRequestX *dr, RendCell *Frame, M4Rect *ClipRectangle, M4Rect *UpdateRect); + +} // namespace M4 + +#endif diff --git a/engines/m4/gui/gui.h b/engines/m4/gui/gui.h index 3bf4dea8fc1..b30b5c30e1e 100644 --- a/engines/m4/gui/gui.h +++ b/engines/m4/gui/gui.h @@ -29,8 +29,8 @@ namespace M4 { struct M4sprite { - struct M4sprite *next; - struct M4sprite *prev; + M4sprite *next; + M4sprite *prev; int32 x; int32 y; int32 w; @@ -39,7 +39,7 @@ struct M4sprite { int32 yOffset; uint8 encoding; uint8 *data; - Handle sourceHandle; + MemHandle sourceHandle; int32 sourceOffset; }; @@ -60,7 +60,7 @@ struct RectList { }; struct matte { - struct matte *nextMatte; + matte *nextMatte; void *myScreen; int32 x1; int32 y1; diff --git a/engines/m4/m4_types.h b/engines/m4/m4_types.h index d108c7da2b6..e1ee094163b 100644 --- a/engines/m4/m4_types.h +++ b/engines/m4/m4_types.h @@ -26,6 +26,8 @@ namespace M4 { +#define MAX_STRING_SIZE 144 +#define MAX_STRING_LEN 144 #define MAX_FILENAME_SIZE 144 #define MIN_VIDEO_X 0 #define MIN_VIDEO_Y 0 diff --git a/engines/m4/mem/reloc.cpp b/engines/m4/mem/reloc.cpp index 75d0adcfce0..05ade98b876 100644 --- a/engines/m4/mem/reloc.cpp +++ b/engines/m4/mem/reloc.cpp @@ -51,4 +51,8 @@ void DisposeHandle(MemHandle handle) { free(handle); } +uint32 MaxMem(Size *growBytes) { + return 7999999; +} + } // namespace M4 diff --git a/engines/m4/mem/reloc.h b/engines/m4/mem/reloc.h index ce2dea6a7ce..e957a50d19b 100644 --- a/engines/m4/mem/reloc.h +++ b/engines/m4/mem/reloc.h @@ -28,6 +28,7 @@ namespace M4 { typedef void **MemHandle; +typedef int32 Size; inline void HLock(Handle h) {} inline void HUnLock(Handle h) {} @@ -42,6 +43,7 @@ extern MemHandle MakeNewHandle(size_t size, const Common::String &); extern bool mem_ReallocateHandle(MemHandle h, size_t size, const Common::String &name); extern MemHandle NewHandle(size_t size, const Common::String &); extern void DisposeHandle(MemHandle handle); +extern uint32 MaxMem(Size *growBytes); } // namespace M4 diff --git a/engines/m4/module.mk b/engines/m4/module.mk index 1459cba097d..0381e7e300f 100644 --- a/engines/m4/module.mk +++ b/engines/m4/module.mk @@ -29,6 +29,7 @@ MODULE_OBJS = \ core/param.o \ core/rooms.o \ core/term.o \ + dbg/dbg_wscript.o \ events/mickey.o \ events/mouse_handler.o \ fileio/extensions.o \ @@ -45,6 +46,7 @@ MODULE_OBJS = \ graphics/gr_series.o \ graphics/graphics.o \ graphics/krn_pal.o \ + graphics/rend.o \ gui/gui_buffer.o \ gui/gui_dialog.o \ gui/gui_item.o \ @@ -69,6 +71,7 @@ MODULE_OBJS = \ wscript/ws_machine.o \ wscript/ws_timer.o \ wscript/wscript.o \ + wscript/wst_regs.o \ burger/gui/game_menu.o \ burger/gui/gui_cheapo.o \ burger/gui/interface.o \ diff --git a/engines/m4/platform/sound/digi.cpp b/engines/m4/platform/sound/digi.cpp index 5612baf203e..fc48ec70dc2 100644 --- a/engines/m4/platform/sound/digi.cpp +++ b/engines/m4/platform/sound/digi.cpp @@ -52,5 +52,36 @@ void Digi::unload(const Common::String &name) { warning("TODO: Digi::unload"); } +void Digi::task() { + warning("TODO: Digi::task"); +} + +int32 Digi::play(const char *name, int32 channel, int32 vol, int32 trigger, int32 room_num) { + error("TODO: Digi::play"); + return 0; +} + +int32 Digi::play_loop(const char *name, int32 channel, int32 vol, int32 trigger, int32 room_num) { + error("TODO: Digi::play_loop"); + return 0; +} + +void Digi::read_another_chunk() { + warning("TODO: Digi::read_another_chunk"); +} + } // namespace Sound + +int32 digi_play(const char *name, int32 channel, int32 vol, int32 trigger, int32 room_num) { + return _G(digi).play(name, channel, vol, trigger, room_num); +} + +int32 digi_play_loop(const char *name, int32 channel, int32 vol, int32 trigger, int32 room_num) { + return _G(digi).play_loop(name, channel, vol, trigger, room_num); +} + +void digi_read_another_chunk() { + return _G(digi).read_another_chunk(); +} + } // namespace M4 diff --git a/engines/m4/platform/sound/digi.h b/engines/m4/platform/sound/digi.h index 89b51d50cbc..93f297bee35 100644 --- a/engines/m4/platform/sound/digi.h +++ b/engines/m4/platform/sound/digi.h @@ -40,9 +40,27 @@ private: public: void preload_sounds(const char **names); void unload_sounds(); + void task(); + + // digi_play and digi_play_loop play a particular sound file in a given channel, + // at a particular volume. The room_num parameter tells us what directory the sound + // is stored in (all sounds are AIFFs). Trigger is an integer that is fed into + // kernel_dispatch_trigger when the sound has finished playing + // If the sound has been preloaded it will be played from memory, otherwise it will + // be streamed from disk + + int32 play(const char *name, int32 channel, int32 vol, int32 trigger, int32 room_num = -1); + int32 play_loop(const char *name, int32 channel, int32 vol, int32 trigger, int32 room_num = -1); + + void read_another_chunk(); }; } // namespace Sound + +extern int32 digi_play(const char *name, int32 channel, int32 vol, int32 trigger, int32 room_num = -1); +extern int32 digi_play_loop(const char *name, int32 channel, int32 vol, int32 trigger, int32 room_num = -1); +extern void digi_read_another_chunk(); + } // namespace M4 #endif diff --git a/engines/m4/platform/sound/midi.cpp b/engines/m4/platform/sound/midi.cpp index 5d4bede4e8e..79833e18f54 100644 --- a/engines/m4/platform/sound/midi.cpp +++ b/engines/m4/platform/sound/midi.cpp @@ -25,5 +25,18 @@ namespace M4 { namespace Sound { +void Midi::task() { + warning("TODO: Midi::task"); +} + +void Midi::loop() { + warning("TODO: Midi::loop"); +} + } // namespace Sound + +void midi_loop() { + _G(midi).loop(); +} + } // namespace M4 diff --git a/engines/m4/platform/sound/midi.h b/engines/m4/platform/sound/midi.h index 2d092041895..b14e1e53992 100644 --- a/engines/m4/platform/sound/midi.h +++ b/engines/m4/platform/sound/midi.h @@ -30,9 +30,14 @@ namespace Sound { class Midi { public: + void task(); + void loop(); }; } // namespace Sound + +extern void midi_loop(); + } // namespace M4 #endif diff --git a/engines/m4/vars.cpp b/engines/m4/vars.cpp index aaf0896d508..70a215047a0 100644 --- a/engines/m4/vars.cpp +++ b/engines/m4/vars.cpp @@ -24,6 +24,7 @@ #include "m4/adv_r/adv.h" #include "m4/adv_r/adv_been.h" #include "m4/core/errors.h" +#include "m4/dbg/debug.h" #include "m4/graphics/gr_pal.h" #include "m4/gui/gui_buffer.h" #include "m4/gui/gui_dialog.h" @@ -47,6 +48,7 @@ Vars::~Vars() { game_systems_shutdown(); sysfile_shutdown(); + f_stream_Shutdown(); player_been_shutdown(); gui_system_shutdown(); gui_buffer_system_shutdown(); @@ -54,6 +56,7 @@ Vars::~Vars() { mem_stash_shutdown(); param_shutdown(); woodscript_shutdown(); + dbg_ws_shutdown(); g_vars = nullptr; } @@ -80,6 +83,14 @@ bool Vars::init() { if (!LoadWSAssets("stream script", &_master_palette[0])) error_show(FL, 'FNF!', "stream script"); + grab_fonts(); + gr_font_set(_font_inter); + + if (_cheat_keys_enabled) { + if (!dbg_ws_init(_kernel.start_up_with_dbg_ws, _font_tiny_prop, _globals)) + error(FL, 'DWIF'); + } + main_cold_data_init(); create_mouse_watch_dialog(); global_menu_system_init(); diff --git a/engines/m4/vars.h b/engines/m4/vars.h index 3cfb8a15e31..1019934148f 100644 --- a/engines/m4/vars.h +++ b/engines/m4/vars.h @@ -42,6 +42,7 @@ #include "m4/fileio/sys_file.h" #include "m4/graphics/gr_color.h" #include "m4/graphics/gr_font.h" +#include "m4/graphics/krn_pal.h" #include "m4/gui/gui_dialog.h" #include "m4/gui/gui_item.h" #include "m4/gui/gui_mouse.h" @@ -61,7 +62,7 @@ class Vars; extern Vars *g_vars; -class Vars : public Mouse_Globals, public WS_Globals, public Timer_Globals { +class Vars : public Mouse_Globals, public Timer_Globals { private: void game_systems_initialize(byte flags); void game_systems_shutdown(); @@ -101,9 +102,11 @@ public: Dialog_Globals _dialog; Item_Globals _items; Converstation_Globals _conversations; + WS_Globals _ws; Triggers _triggers; Sound::Digi _digi; Sound::Midi _midi; + KernelPal_Globals _krnPal; bool _cheating_enabled = false; bool _cheat_keys_enabled = false; @@ -173,6 +176,7 @@ public: #define _GV() (*g_vars->getGlobals()) #define _GI() (*g_vars->getInterface()) #define _GW() (*g_vars->getWalker()) +#define _GWS(X) _G(ws)._##X } // namespace M4 diff --git a/engines/m4/wscript/ws_cruncher.cpp b/engines/m4/wscript/ws_cruncher.cpp index 6587fd00642..8e9a731b98a 100644 --- a/engines/m4/wscript/ws_cruncher.cpp +++ b/engines/m4/wscript/ws_cruncher.cpp @@ -21,75 +21,1575 @@ #include "m4/wscript/ws_cruncher.h" #include "m4/wscript/ws_hal.h" +#include "m4/wscript/wst_regs.h" #include "m4/core/errors.h" +#include "m4/core/imath.h" +#include "m4/dbg/debug.h" #include "m4/mem/mem.h" #include "m4/vars.h" namespace M4 { -#define VERIFY_INTIALIZED(s) if (!_G(cruncherInitialized)) error_show(FL, 'WSCI', "%s failed.", s); +#define VERIFY_INTIALIZED(s) if (!_GWS(cruncherInitialized)) error_show(FL, 'WSCI', "%s failed.", s); static int32 dataFormats[] = { 0, 5, 8, 12, 16 }; +static const frac16 sinCosTable[320] = { + (frac16)0, (frac16)1608, (frac16)3215, (frac16)4821, + (frac16)6423, (frac16)8022, (frac16)9616, (frac16)11204, + (frac16)12785, (frac16)14359, (frac16)15923, (frac16)17479, + (frac16)19024, (frac16)20557, (frac16)22078, (frac16)23586, + (frac16)25079, (frac16)26557, (frac16)28020, (frac16)29465, + (frac16)30893, (frac16)32302, (frac16)33692, (frac16)35061, + (frac16)36409, (frac16)37736, (frac16)39039, (frac16)40319, + (frac16)41575, (frac16)42806, (frac16)44011, (frac16)45189, + (frac16)46340, (frac16)47464, (frac16)48558, (frac16)49624, + (frac16)50660, (frac16)51665, (frac16)52639, (frac16)53581, + (frac16)54491, (frac16)55368, (frac16)56212, (frac16)57022, + (frac16)57797, (frac16)58538, (frac16)59243, (frac16)59913, + (frac16)60547, (frac16)61144, (frac16)61705, (frac16)62228, + (frac16)62714, (frac16)63162, (frac16)63571, (frac16)63943, + (frac16)64276, (frac16)64571, (frac16)64826, (frac16)65043, + (frac16)65220, (frac16)65358, (frac16)65457, (frac16)65516, + (frac16)65536, (frac16)65516, (frac16)65457, (frac16)65358, + (frac16)65220, (frac16)65043, (frac16)64826, (frac16)64571, + (frac16)64276, (frac16)63943, (frac16)63571, (frac16)63162, + (frac16)62714, (frac16)62228, (frac16)61705, (frac16)61144, + (frac16)60547, (frac16)59913, (frac16)59243, (frac16)58538, + (frac16)57797, (frac16)57022, (frac16)56212, (frac16)55368, + (frac16)54491, (frac16)53581, (frac16)52639, (frac16)51665, + (frac16)50660, (frac16)49624, (frac16)48558, (frac16)47464, + (frac16)46340, (frac16)45189, (frac16)44011, (frac16)42806, + (frac16)41575, (frac16)40319, (frac16)39039, (frac16)37736, + (frac16)36409, (frac16)35061, (frac16)33692, (frac16)32302, + (frac16)30893, (frac16)29465, (frac16)28020, (frac16)26557, + (frac16)25079, (frac16)23586, (frac16)22078, (frac16)20557, + (frac16)19024, (frac16)17479, (frac16)15923, (frac16)14359, + (frac16)12785, (frac16)11204, (frac16)9616, (frac16)8022, + (frac16)6423, (frac16)4821, (frac16)3215, (frac16)1608, + (frac16)0, (frac16)-1608, (frac16)-3215, (frac16)-4821, + (frac16)-6423, (frac16)-8022, (frac16)-9616, (frac16)-11204, + (frac16)-12785, (frac16)-14359, (frac16)-15923, (frac16)-17479, + (frac16)-19024, (frac16)-20557, (frac16)-22078, (frac16)-23586, + (frac16)-25079, (frac16)-26557, (frac16)-28020, (frac16)-29465, + (frac16)-30893, (frac16)-32302, (frac16)-33692, (frac16)-35061, + (frac16)-36409, (frac16)-37736, (frac16)-39039, (frac16)-40319, + (frac16)-41575, (frac16)-42806, (frac16)-44011, (frac16)-45189, + (frac16)-46340, (frac16)-47464, (frac16)-48558, (frac16)-49624, + (frac16)-50660, (frac16)-51665, (frac16)-52639, (frac16)-53581, + (frac16)-54491, (frac16)-55368, (frac16)-56212, (frac16)-57022, + (frac16)-57797, (frac16)-58538, (frac16)-59243, (frac16)-59913, + (frac16)-60547, (frac16)-61144, (frac16)-61705, (frac16)-62228, + (frac16)-62714, (frac16)-63162, (frac16)-63571, (frac16)-63943, + (frac16)-64276, (frac16)-64571, (frac16)-64826, (frac16)-65043, + (frac16)-65220, (frac16)-65358, (frac16)-65457, (frac16)-65516, + (frac16)-65536, (frac16)-65516, (frac16)-65457, (frac16)-65358, + (frac16)-65220, (frac16)-65043, (frac16)-64826, (frac16)-64571, + (frac16)-64276, (frac16)-63943, (frac16)-63571, (frac16)-63162, + (frac16)-62714, (frac16)-62228, (frac16)-61705, (frac16)-61144, + (frac16)-60547, (frac16)-59913, (frac16)-59243, (frac16)-58538, + (frac16)-57797, (frac16)-57022, (frac16)-56212, (frac16)-55368, + (frac16)-54491, (frac16)-53581, (frac16)-52639, (frac16)-51665, + (frac16)-50660, (frac16)-49624, (frac16)-48558, (frac16)-47464, + (frac16)-46340, (frac16)-45189, (frac16)-44011, (frac16)-42806, + (frac16)-41575, (frac16)-40319, (frac16)-39039, (frac16)-37736, + (frac16)-36409, (frac16)-35061, (frac16)-33692, (frac16)-32302, + (frac16)-30893, (frac16)-29465, (frac16)-28020, (frac16)-26557, + (frac16)-25079, (frac16)-23586, (frac16)-22078, (frac16)-20557, + (frac16)-19024, (frac16)-17479, (frac16)-15923, (frac16)-14359, + (frac16)-12785, (frac16)-11204, (frac16)-9616, (frac16)-8022, + (frac16)-6423, (frac16)-4821, (frac16)-3215, (frac16)-1608, + (frac16)0, (frac16)1608, (frac16)3215, (frac16)4821, + (frac16)6423, (frac16)8022, (frac16)9616, (frac16)11204, + (frac16)12785, (frac16)14359, (frac16)15923, (frac16)17479, + (frac16)19024, (frac16)20557, (frac16)22078, (frac16)23586, + (frac16)25079, (frac16)26557, (frac16)28020, (frac16)29465, + (frac16)30893, (frac16)32302, (frac16)33692, (frac16)35061, + (frac16)36409, (frac16)37736, (frac16)39039, (frac16)40319, + (frac16)41575, (frac16)42806, (frac16)44011, (frac16)45189, + (frac16)46340, (frac16)47464, (frac16)48558, (frac16)49624, + (frac16)50660, (frac16)51665, (frac16)52639, (frac16)53581, + (frac16)54491, (frac16)55368, (frac16)56212, (frac16)57022, + (frac16)57797, (frac16)58538, (frac16)59243, (frac16)59913, + (frac16)60547, (frac16)61144, (frac16)61705, (frac16)62228, + (frac16)62714, (frac16)63162, (frac16)63571, (frac16)63943, + (frac16)64276, (frac16)64571, (frac16)64826, (frac16)65043, + (frac16)65220, (frac16)65358, (frac16)65457, (frac16)65516 +}; + +static const frac16 *sinTable = &(sinCosTable[0]); +static const frac16 *cosTable = &(sinCosTable[64]); + int32 *ws_GetDataFormats() { return &dataFormats[0]; } bool ws_InitCruncher(void) { - // int32 i; - - //make sure the cruncher has not been initialized - if (_G(cruncherInitialized)) + // Make sure the cruncher has not been initialized + if (_GWS(cruncherInitialized)) error_show(FL, 'WSCR'); // Register the end of sequence struct with the stash manager - mem_register_stash_type(&_G(memtypeEOS), sizeof(EOSreq), 32, "+EOS"); - if (_G(memtypeEOS) < 0) + mem_register_stash_type(&_GWS(memtypeEOS), sizeof(EOSreq), 32, "+EOS"); + if (_GWS(memtypeEOS) < 0) error_show(FL, 'WSCE'); - if ((_G(myCruncher) = (cruncher *)mem_alloc(sizeof(cruncher), "cruncher")) == NULL) + if ((_GWS(myCruncher) = (cruncher *)mem_alloc(sizeof(cruncher), "cruncher")) == nullptr) error_show(FL, 'OOM!', "%ld bytes.", sizeof(cruncher)); - _G(myCruncher)->backLayerAnim8 = NULL; - _G(myCruncher)->frontLayerAnim8 = NULL; - _G(myCruncher)->firstAnim8ToCrunch = NULL; - _G(myCruncher)->lastAnim8ToCrunch = NULL; + _GWS(myCruncher)->backLayerAnim8 = nullptr; + _GWS(myCruncher)->frontLayerAnim8 = nullptr; + _GWS(myCruncher)->firstAnim8ToCrunch = nullptr; + _GWS(myCruncher)->lastAnim8ToCrunch = nullptr; // Set up stack - _G(stackSize) = 2048; - if ((_G(stackBase) = (uint32 *)mem_alloc(_G(stackSize), "crunchstack")) == NULL) { - error_show(FL, 'OOM!', "%ld bytes.", _G(stackSize)); + _GWS(stackSize) = 2048; + if ((_GWS(stackBase) = (uint32 *)mem_alloc(_GWS(stackSize), "crunchstack")) == nullptr) { + error_show(FL, 'OOM!', "%ld bytes.", _GWS(stackSize)); } - _G(stackTop) = _G(stackBase); - _G(stackLimit) = (uint32 *)((byte *)_G(stackBase) + (uint32)_G(stackSize)); + _GWS(stackTop) = _GWS(stackBase); + _GWS(stackLimit) = (uint32 *)((byte *)_GWS(stackBase) + (uint32)_GWS(stackSize)); - _G(cruncherInitialized) = true; + _GWS(cruncherInitialized) = true; return true; } -void ws_KillCruncher(void) { +Anim8 *ws_AddAnim8ToCruncher(machine *m, int32 sequHash) { + Anim8 *myAnim8; + frac16 *myRegs; + int32 numLocalVars; + int32 i; + + // Make sure the cruncher has been initialized + VERIFY_INTIALIZED("ws_AddAnim8ToCruncher()"); + + // Allocate an anim8 structure + if ((myAnim8 = (Anim8 *)mem_alloc(sizeof(Anim8), "Anim8")) == nullptr) { + ws_LogErrorMsg(FL, "Out of memory - mem requested: %ld.", sizeof(Anim8)); + return nullptr; + } + + // Find the sequence + if ((myAnim8->sequHandle = ws_GetSEQU((uint32)sequHash, &numLocalVars, &myAnim8->pcOffset)) == nullptr) { + return nullptr; + } + + // Allocate an array of registers + if ((myRegs = (frac16 *)mem_alloc(sizeof(frac16) * (IDX_COUNT + numLocalVars), "Anim8 regs")) == nullptr) { + ws_LogErrorMsg(FL, "Out of memory - mem requested: %ld.", sizeof(frac16) * (IDX_COUNT + numLocalVars)); + return nullptr; + } + + // Initialize the Anim8 structure + myAnim8->active = true; + myAnim8->sequHash = sequHash; + myAnim8->myMachine = m; // Pointer back to myMachine + myAnim8->eosReqOffset = -1; + myAnim8->myParent = m->parentAnim8; // The parent anim8 + myAnim8->myCCB = nullptr; + myAnim8->dataHash = m->dataHash; // The array of data + myAnim8->dataHandle = m->dataHandle; + myAnim8->dataOffset = m->dataOffset; + myAnim8->startTime = 0; + myAnim8->switchTime = 0; + myAnim8->flags = 0; + myAnim8->numLocalVars = numLocalVars; + myAnim8->myRegs = myRegs; + myAnim8->returnStackIndex = 0; + + // Link it into the execution list + myAnim8->next = nullptr; + myAnim8->prev = _GWS(myCruncher)->lastAnim8ToCrunch; + if (_GWS(myCruncher)->lastAnim8ToCrunch) { + _GWS(myCruncher)->lastAnim8ToCrunch->next = myAnim8; + } else { + _GWS(myCruncher)->firstAnim8ToCrunch = myAnim8; + } + _GWS(myCruncher)->lastAnim8ToCrunch = myAnim8; + + // Now link it into the layering list + myAnim8->myLayer = 0; + myAnim8->infront = nullptr; + myAnim8->behind = _GWS(myCruncher)->frontLayerAnim8; + if (_GWS(myCruncher)->frontLayerAnim8) { + _GWS(myCruncher)->frontLayerAnim8->infront = myAnim8; + } else { + _GWS(myCruncher)->backLayerAnim8 = myAnim8; + } + _GWS(myCruncher)->frontLayerAnim8 = myAnim8; + + // Now clear the registers, but set the scale = 100% + for (i = 0; i < IDX_COUNT + numLocalVars; i++) { + myAnim8->myRegs[i] = 0; + } + myAnim8->myRegs[IDX_S] = 0x10000; + myAnim8->myRegs[IDX_MACH_ID] = m->machID; + + return myAnim8; +} + +bool ws_ChangeAnim8Program(machine *m, int32 newSequHash) { + Anim8 *myAnim8; + int32 numLocalVars; + + // Make sure the cruncher has been initialized + VERIFY_INTIALIZED("ws_ChangeAnim8Program()"); + + // Parameter verification + if ((!m) || (!m->myAnim8)) { + error_show(FL, 'WSMI'); + } + + myAnim8 = m->myAnim8; + + // Find the sequence + if ((myAnim8->sequHandle = ws_GetSEQU((uint32)newSequHash, &numLocalVars, &myAnim8->pcOffset)) == nullptr) { + return false; + } + + // Now see if we've started a sequence requiring more vars than the prev + if (myAnim8->numLocalVars < numLocalVars) { + ws_LogErrorMsg(FL, "Can't launch a sequence with more local vars than the previous sequence."); + return false; + } + + // Intialize the Anim8 + myAnim8->switchTime = 0; + myAnim8->active = 1; + myAnim8->eosReqOffset = -1; + myAnim8->sequHash = newSequHash; + myAnim8->returnStackIndex = 0; + + return true; +} + +void ws_RemoveAnim8FromCruncher(Anim8 *myAnim8) { + EOSreq *tempEOSreq, *prevEOSreq; + + // Make sure the cruncher has been initialized + VERIFY_INTIALIZED("ws_RemoveAnim8FromCruncher()"); + + if (!myAnim8) + error_show(FL, 'WSAI'); + + // In case we are crunching the current list of EOS requests, remove any for this machine + tempEOSreq = _GWS(EOSreqList); + prevEOSreq = nullptr; + while (tempEOSreq && (tempEOSreq->myAnim8 != myAnim8)) { + tempEOSreq = tempEOSreq->next; + } + + if (tempEOSreq) { + if (tempEOSreq->next) { + tempEOSreq->next->prev = tempEOSreq->prev; + } + if (tempEOSreq->prev) { + tempEOSreq->prev->next = tempEOSreq->next; + } else { + _GWS(EOSreqList) = tempEOSreq->next; + } + + mem_free_to_stash((void *)tempEOSreq, _GWS(memtypeEOS)); + } + + // Incase we are in the middle of crunching + if (myAnim8 == _GWS(crunchNext)) { + _GWS(crunchNext) = myAnim8->next; + } + + // Remove myAnim8 from the crunch list + if (myAnim8->prev) { + myAnim8->prev->next = myAnim8->next; + } else { + _GWS(myCruncher)->firstAnim8ToCrunch = myAnim8->next; + } + + if (myAnim8->next) { + myAnim8->next->prev = myAnim8->prev; + } else { + _GWS(myCruncher)->lastAnim8ToCrunch = myAnim8->prev; + } + + // Now remove it from the anim8 layer + if (myAnim8->infront) { + myAnim8->infront->behind = myAnim8->behind; + } else { + _GWS(myCruncher)->frontLayerAnim8 = myAnim8->behind; + } + if (myAnim8->behind) { + myAnim8->behind->infront = myAnim8->infront; + } else { + _GWS(myCruncher)->backLayerAnim8 = myAnim8->infront; + } + + // Clean up and free the CCB for this anim8 + if (myAnim8->myCCB) { + KillCCB(myAnim8->myCCB, true); + } + + // Deallocate the register list + mem_free(myAnim8->myRegs); + + // Finally, turf myAnim8 + mem_free(myAnim8); +} + + +// This procedure flags the anim8 slot as inactive, but still owned by the machine +bool ws_PauseAnim8(Anim8 *myAnim8) { + // Make sure the cruncher has been initialized + VERIFY_INTIALIZED("ws_PauseAnim8()"); + + if (!myAnim8) + error_show(FL, 'WSAI'); + + myAnim8->active = false; + HideCCB(myAnim8->myCCB); + return true; +} + +// This procedure reactivates the anim8 slot owned by the machine +bool ws_ResumeAnim8(Anim8 *myAnim8) { + // Make sure the cruncher has been initialized + VERIFY_INTIALIZED("ws_PauseAnim8()"); + + if (!myAnim8) + error_show(FL, 'WSAI'); + + myAnim8->active = true; + if (myAnim8->myCCB) { + ShowCCB(myAnim8->myCCB); + myAnim8->myCCB->flags |= CCB_SKIP; + } + + return true; +} + +static bool ExtractArg(Anim8 *myAnim8, int32 myFormat, int32 myData, frac16 **argPtr, frac16 *argValue) { + int32 myIndex; + Anim8 *parentAnim8; + frac16 *dataArray; + + // If the format indicates the argument is a local source (parent, register, or data) + if (myFormat == FMT_LOCAL_SRC) { + if (!myAnim8) { + ws_LogErrorMsg(FL, "INTERNAL ERROR - ExtractArg() failed - An invalid Anim8* was passed."); + return false; + } + + // Find out if the index has been previously stored in a special index register + if (myData & REG_SET_IDX_REG) { + myIndex = _GWS(indexReg); + } else { + // Else the index is part of the data segment for this arg + myIndex = myData & REG_SET_IDX; + } + + // Find the right register set + switch (myData & LOCAL_FMT) { + case LOCAL_FMT_PARENT: + parentAnim8 = myAnim8->myParent; + + // Range check to make sure we don't index off into hyperspace + if ((!parentAnim8) || (myIndex >= IDX_COUNT + parentAnim8->numLocalVars)) { + if (!parentAnim8) { + ws_LogErrorMsg(FL, "Trying to access a parent register - no parent exists"); + } else { + ws_LogErrorMsg(FL, "Parent Reg Index out of range - max: %ld, requested %ld.", + IDX_COUNT + parentAnim8->numLocalVars, myIndex); + } + return false; + } + *argPtr = (frac16 *)((uint32)parentAnim8->myRegs + (myIndex << 2)); + break; + + case LOCAL_FMT_REG: + // Range check to make sure we don't index off into hyperspace + if ((myIndex >= IDX_COUNT + myAnim8->numLocalVars)) { + ws_LogErrorMsg(FL, "Register Index out of range - max: %ld, requested %ld.", + IDX_COUNT + myAnim8->numLocalVars, myIndex); + return false; + } + *argPtr = (frac16 *)((uint32)myAnim8->myRegs + (myIndex << 2)); + break; + + case LOCAL_FMT_DATA: + // Ensure we have a dataHandle + if ((!myAnim8->dataHandle) || (!*(myAnim8->dataHandle))) { + ws_LogErrorMsg(FL, "Trying to access a DATA field when no DATA has been set"); + return false; + } + + // Dereferrence the dataHandle, add the offset to find the array of data for this anim8 + dataArray = (frac16 *)((int32) * (myAnim8->dataHandle) + myAnim8->dataOffset); + + // Copy the data field into _GWS(dataArg1), and set _GWS(myArg1) to point to this location + *argValue = dataArray[myIndex]; + *argPtr = argValue; + break; + } + } else if (myFormat == FMT_GLOBAL_SRC) { + // Else if the format indicates the argument is from the _GWS(ws_globals) register set + // Find out if the index has been previously stored in a special index register + if (myData & REG_SET_IDX_REG) { + myIndex = _GWS(indexReg); + } else { + // Else the index is part of the data segment for this arg + myIndex = myData & REG_SET_IDX; + } + + // Finally, set _GWS(myArg1) to point to the location in the _GWS(ws_globals) array, whichever index + *argPtr = &(_GWS(ws_globals)[myIndex]); + } else { + // Else the argument is not a variable, but an actual value + + // The top bit of the data segment is a negative flag, the format determines how far the other + // 15 bits of the data segment are shifted left, so the value requested is in frac16 format. + // The value is stored in a static frac16 (_GWS(dataArg1)), and... + if (myData & OP_DATA_SIGN) { + *argValue = -(myData & OP_DATA_VALUE) << (dataFormats[myFormat - 3]); + } else { + *argValue = (myData & OP_DATA_VALUE) << (dataFormats[myFormat - 3]); + } + + //_GWS(myArg1) will point to this location + *argPtr = argValue; + } + + return true; +} + +// The instruction number is returned by this function, and the arguments are pointed to by these externable globals +// Frac16 *_GWS(myArg1); +// Frac16 *_GWS(myArg2); +// Frac16 *_GWS(myArg3); +// +// + +int32 ws_PreProcessPcode(uint32 **PC, Anim8 *myAnim8) { + int32 myInstruction, myFormat, myData; // myIndex; + uint32 *myPC, opCode, word2; + + if (!PC) { + ws_LogErrorMsg(FL, "INTERNAL ERROR - ws_PreProcessPcode() failed - An invalid PC was passed."); + return -1; + } + + myPC = *PC; + + // Get the opCode + opCode = *myPC++; + + // Get the instruction number + myInstruction = (opCode & OP_INSTR) >> 25; + + // Get the format for the first arg + myFormat = (opCode & OP_FORMAT1) >> 22; + + // Get the data for the first arg + myData = opCode & OP_LOW_DATA; + + // Verify we have an argument + if (myFormat) { + if (!ExtractArg(myAnim8, myFormat, myData, &_GWS(myArg1), &_GWS(dataArg1))) { + return -1; + } + } else { + // Otherwise this argument is called with no args + _GWS(myArg1) = nullptr; + _GWS(myArg2) = nullptr; + _GWS(myArg3) = nullptr; + *PC = myPC; + return myInstruction; + } + + // Check for arg2 + myFormat = (opCode & OP_FORMAT2) >> 19; + if (myFormat) { + word2 = *myPC++; + myData = (word2 & OP_HIGH_DATA) >> 16; + if (!ExtractArg(myAnim8, myFormat, myData, &_GWS(myArg2), &_GWS(dataArg2))) { + return -1; + } + } else { + _GWS(myArg2) = nullptr; + _GWS(myArg3) = nullptr; + *PC = myPC; + return myInstruction; + } + + // Finally check for arg3 + myFormat = (opCode & OP_FORMAT3) >> 16; + if (myFormat) { + myData = word2 & OP_LOW_DATA; + if (!ExtractArg(myAnim8, myFormat, myData, &_GWS(myArg3), &_GWS(dataArg3))) { + return -1; + } + } else { + _GWS(myArg3) = nullptr; + *PC = myPC; + return myInstruction; + } + + *PC = myPC; + return myInstruction; +} + + +static void op_END(Anim8 *myAnim8) { + _GWS(terminated) = true; + _GWS(keepProcessing) = false; +} + +static void op_CLEAR(Anim8 *myAnim8) { + int32 i; + + // Now clear the registers, but set the scale = 100% + for (i = 0; i <= IDX_COUNT + myAnim8->numLocalVars; i++) { + myAnim8->myRegs[i] = 0; + } + myAnim8->myRegs[IDX_S] = 0x10000; + myAnim8->myRegs[IDX_MACH_ID] = myAnim8->myMachine->machID; +} + +static void op_SET(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = arg2 or arg1 = rand(arg2, arg3)"); + } + if (_GWS(myArg3)) { + *_GWS(myArg1) = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)); + } else { + *_GWS(myArg1) = *_GWS(myArg2); + } +} + +static void op_COMPARE(Anim8 *myAnim8) { + frac16 myArg; + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: cmp arg1, arg2 or cmp arg1, rand(arg2, arg3) **sets CCR"); + } + if (_GWS(myArg3)) { + myArg = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)); + } else { + myArg = *_GWS(myArg2); + } + if (*_GWS(myArg1) < myArg) { + _GWS(compareCCR) = -1; + } else if (*_GWS(myArg1) > myArg) { + _GWS(compareCCR) = 1; + } else { + _GWS(compareCCR) = 0; + } +} + +static void op_ADD(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 += arg2 or arg1 += rand(arg2, arg3)"); + } + if (_GWS(myArg3)) { + *_GWS(myArg1) += imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)); + } else { + *_GWS(myArg1) += *_GWS(myArg2); + } +} + +static void op_SUB(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 -= arg2 or arg1 -= rand(arg2, arg3)"); + } + if (_GWS(myArg3)) { + *_GWS(myArg1) -= imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)); + } else { + *_GWS(myArg1) -= *_GWS(myArg2); + } +} + +static void op_MULT(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 *= arg2 or arg1 *= rand(arg2, arg3)"); + } + if (_GWS(myArg3)) { + *_GWS(myArg1) = MulSF16(*_GWS(myArg1), imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3))); + } else { + *_GWS(myArg1) = MulSF16(*_GWS(myArg1), *_GWS(myArg2)); + } +} + +static void op_DIV(Anim8 *myAnim8) { + frac16 divisor; + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 /= arg2 or arg1 /= rand(arg2, arg3)"); + } + if (_GWS(myArg3)) { + divisor = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)); + } else { + divisor = *_GWS(myArg2); + } + if (divisor == 0) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0253, nullptr); + } else { + *_GWS(myArg1) = DivSF16(*_GWS(myArg1), divisor); + } +} + +static void op_SIN(Anim8 *myAnim8) { + int32 tempAngle; + + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = sin(arg2) or arg1 = sin(rand(arg2, arg3))"); + } + if (_GWS(myArg3)) { + tempAngle = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)) >> 16; + } else { + tempAngle = *_GWS(myArg2) >> 16; + } + if (tempAngle < 0) { + tempAngle = 0x0100 - ((-tempAngle) & 0xff); + } else { + tempAngle &= 0xff; + } + + *_GWS(myArg1) = -(int)cosTable[tempAngle]; +} + +static void op_COS(Anim8 *myAnim8) { + int32 tempAngle; + + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = cos(arg2) or arg1 = cos(rand(arg2, arg3))"); + } + if (_GWS(myArg3)) { + tempAngle = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)) >> 16; + } else { + tempAngle = *_GWS(myArg2) >> 16; + } + if (tempAngle < 0) { + tempAngle = 0x0100 - ((-tempAngle) & 0xff); + } else { + tempAngle &= 0xff; + } + + *_GWS(myArg1) = sinTable[tempAngle]; +} + +static void op_AND(Anim8 *myAnim8) { + frac16 myArg; + + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 &= arg2 or arg1 &= rand(arg2, arg3) **also sets CCR"); + } + if (_GWS(myArg3)) { + myArg = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)); + } else { + myArg = *_GWS(myArg2); + } + if ((*_GWS(myArg1)) & myArg) { + _GWS(compareCCR) = 0; + } else { + _GWS(compareCCR) = 1; + } + + *_GWS(myArg1) &= myArg; +} + +static void op_OR(Anim8 *myAnim8) { + frac16 myArg; + + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 |= arg2 or arg1 |= rand(arg2, arg3) **also sets CCR"); + } + if (_GWS(myArg3)) { + myArg = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)); + } else { + myArg = *_GWS(myArg2); + } + if ((*_GWS(myArg1)) | myArg) { + _GWS(compareCCR) = 0; + } else { + _GWS(compareCCR) = 1; + } + + *_GWS(myArg1) |= myArg; +} + +static void op_NOT(Anim8 *myAnim8) { + if (!_GWS(myArg1)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: arg1 = (arg1 ? 0 : 1) **also sets CCR"); + } + if (*_GWS(myArg1) == 0) { + *_GWS(myArg1) = 0x10000; + _GWS(compareCCR) = 1; + } else { + *_GWS(myArg1) = 0; + _GWS(compareCCR) = 0; + } +} + +static void op_ABS(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = abs(arg2)"); + } + if (*_GWS(myArg2) < 0) { + *_GWS(myArg1) = -(int)(*_GWS(myArg2)); + } else { + *_GWS(myArg1) = *_GWS(myArg2); + } +} + +static void op_MIN(Anim8 *myAnim8) { + if (!_GWS(myArg3)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0252, "functionality: arg1 = min(arg2, arg3)"); + } + if (*_GWS(myArg2) < *_GWS(myArg3)) { + *_GWS(myArg1) = *_GWS(myArg2); + } else { + *_GWS(myArg1) = *_GWS(myArg3); + } +} + +static void op_MAX(Anim8 *myAnim8) { + if (!_GWS(myArg3)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0252, "functionality: arg1 = max(arg2, arg3)"); + } + if (*_GWS(myArg2) < *_GWS(myArg3)) { + *_GWS(myArg1) = *_GWS(myArg3); + } else { + *_GWS(myArg1) = *_GWS(myArg2); + } +} + +static void op_MOD(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 %= arg2 or arg1 = arg2%arg3"); + } + if (_GWS(myArg3)) { + *_GWS(myArg1) = (*_GWS(myArg1)) % (imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3))); + } else { + *_GWS(myArg1) = (*_GWS(myArg1)) % (*_GWS(myArg2)); + } +} + +static void op_FLOOR(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = floor(arg2) or arg1 = floor(rand(arg2,arg3))"); + } + if (_GWS(myArg3)) { + *_GWS(myArg1) = (imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3))) & 0xffff0000; + } else { + *_GWS(myArg1) = (*_GWS(myArg2)) & 0xffff0000; + } +} + +static void op_ROUND(Anim8 *myAnim8) { + frac16 myArg; + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = round(arg2) or arg1 = round(rand(arg2,arg3))"); + } + if (_GWS(myArg3)) { + myArg = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)); + } else { + myArg = *_GWS(myArg2); + } + if ((myArg & 0xffff) >= 0x8000) { + *_GWS(myArg1) = (myArg + 0x10000) & 0xffff0000; + } else { + *_GWS(myArg1) = myArg & 0xffff0000; + } +} + +static void op_CEIL(Anim8 *myAnim8) { + frac16 myArg; + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = ceil(arg2) or arg1 = ceil(rand(arg2,arg3))"); + } + if (_GWS(myArg3)) { + myArg = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)); + } else { + myArg = *_GWS(myArg2); + } + if ((myArg & 0xffff) > 0) { + *_GWS(myArg1) = (myArg + 0x10000) & 0xffff0000; + } else { + *_GWS(myArg1) = myArg & 0xffff0000; + } +} + +static void op_POINT(Anim8 *myAnim8) { + if (!_GWS(myArg3)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0252, "functionality: arg1 = angle of line segment (x, y) , (arg2, arg3)"); + } + *_GWS(myArg1) = Atan2F16(-(int)(*_GWS(myArg3)) + myAnim8->myRegs[IDX_Y], *_GWS(myArg2) - myAnim8->myRegs[IDX_X]); +} + +static void op_DIST2D(Anim8 *myAnim8) { + int32 temp1, temp2; + if (!_GWS(myArg3)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0252, "functionality: arg1 = distance from (x, y) to (arg2, arg3)"); + } + temp1 = (int32)(imath_abs(*_GWS(myArg2) - myAnim8->myRegs[IDX_X])); + temp2 = (int32)(imath_abs(*_GWS(myArg3) - myAnim8->myRegs[IDX_Y])); + if ((temp1 >= 0x800000) || (temp2 >= 0x800000)) { + temp1 >>= 16; + temp2 >>= 16; + *_GWS(myArg1) = (frac16)(SqrtF16(temp1 * temp1 + temp2 * temp2) << 16); + } else { + *_GWS(myArg1) = SqrtF16(SquareSF16(temp1) + SquareSF16(temp2)) << 8; + } +} + +static void op_CRUNCH(Anim8 *myAnim8) { + frac16 myArg; + if (_GWS(myArg2)) { + myArg = imath_ranged_rand16(*_GWS(myArg1), *_GWS(myArg2)); + } else if (_GWS(myArg1)) { + myArg = *_GWS(myArg1); + } else { + myArg = 0; + } + myAnim8->startTime = _GWS(ws_globals)[GLB_TIME]; + if (myArg >= 0) { + myAnim8->switchTime = _GWS(ws_globals)[GLB_TIME] + (myArg >> 16); + } else { + myAnim8->switchTime = -1; + } + if (myAnim8->transTime <= 0x10000) { + myAnim8->flags &= ~((TAG_BEZ | TAG_TARGS) << 16); + } + _GWS(keepProcessing) = false; +} + +static void op_BRANCH(Anim8 *myAnim8) { + int32 myOffset; + + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "check the CCR, arg1 is the branch type, arg2 is the PC offset"); + } + myOffset = *_GWS(myArg2) >> 14; + switch (*_GWS(myArg1) >> 16) { + case BRANCH_BR: + myAnim8->pcOffset += myOffset; + break; + case BRANCH_BLT: + if (_GWS(compareCCR) < 0) myAnim8->pcOffset += myOffset; + break; + case BRANCH_BLE: + if (_GWS(compareCCR) <= 0) myAnim8->pcOffset += myOffset; + break; + case BRANCH_BE: + if (_GWS(compareCCR) == 0) myAnim8->pcOffset += myOffset; + break; + case BRANCH_BNE: + if (_GWS(compareCCR) != 0) myAnim8->pcOffset += myOffset; + break; + case BRANCH_BGE: + if (_GWS(compareCCR) >= 0) myAnim8->pcOffset += myOffset; + break; + case BRANCH_BGT: + if (_GWS(compareCCR) > 0) myAnim8->pcOffset += myOffset; + break; + } +} + +static void op_SETCEL(Anim8 *myAnim8) { + int32 myIndex; + CCB *myCCB; + frac16 *myRegs; + + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "setcel(arg1, arg2) or setcel(arg1, rand(arg2, arg3))"); + } + if (_GWS(myArg3)) { + myIndex = imath_ranged_rand(*_GWS(myArg2) >> 16, *_GWS(myArg3) >> 16); + } else if (_GWS(myArg2)) { + myIndex = *_GWS(myArg2) >> 16; + } else { + myIndex = (*_GWS(myArg1) & 0xff0000) >> 16; + } + + if (!myAnim8->myCCB) { + // Allocate and initialize a CCB structure + if ((myAnim8->myCCB = (CCB *)mem_alloc(sizeof(CCB), "CCB")) == nullptr) { + ws_LogErrorMsg(FL, "Out of memory - mem requested: %ld bytes.", sizeof(CCB)); + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x02fe, "setcel() failed."); + } + if (!InitCCB(myAnim8->myCCB)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025d, "setcel() failed."); + } + } + + myCCB = myAnim8->myCCB; + if (myCCB->flags & CCB_DISC_STREAM) { + ws_CloseSSstream(myCCB); + } + ShowCCB(myCCB); + myCCB->flags |= CCB_SKIP; + if ((myAnim8->myCCB = GetWSAssetCEL((uint32)(*_GWS(myArg1)) >> 24, (uint32)myIndex, myCCB)) == nullptr) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025b, "setcel() failed."); + } + myRegs = myAnim8->myRegs; + if (myRegs[IDX_W] < 0) { + myRegs[IDX_W] = -myCCB->source->w << 16; + } else { + myRegs[IDX_W] = myCCB->source->w << 16; + } + myRegs[IDX_H] = myCCB->source->h << 16; + // MyRegs[IDX_CELS_HASH] = ((uint32)*_GWS(myArg1)) >> 8; + // MyRegs[IDX_CELS_INDEX] = myIndex << 16; + _GWS(mapTheCel) = true; +} + + +static void op_SEQ_SEND_MSG(Anim8 *myAnim8) { + frac16 msgValue; + + //_GWS(myArg1) is the recipient machine hash, _GWS(myArg2) is the msg hash, _GWS(myArg3) (if exists) is the msg value + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: send to machine arg1, the message arg2 or the message arg2, arg3"); + } + + if (_GWS(myArg3)) { + msgValue = *_GWS(myArg3); + } else { + msgValue = 0; + } + + SendWSMessage(*_GWS(myArg2), msgValue, nullptr, (*_GWS(myArg1)) >> 16, nullptr, 1); + return; +} + + +static void op_PUSH(Anim8 *myAnim8) { + uint32 *data; + int32 direction, numOfArgs, i; //,startReg + if (!_GWS(myArg1)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: push arg1 or start with arg1, and push a total of arg2 values"); + } + direction = 1; + if (_GWS(myArg2)) { + if (*_GWS(myArg2) > 0) numOfArgs = (*_GWS(myArg2)) >> 16; + else { + numOfArgs = -(int)(*_GWS(myArg2)) >> 16; + direction = -1; + } + } else { + numOfArgs = 1; + } + + if (((byte *)_GWS(stackLimit) - (byte *)_GWS(stackTop)) < (numOfArgs << 2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0254, "overflow during push instruction"); + return; + } + if (_GWS(myArg2)) { + data = (uint32 *)_GWS(myArg1); + for (i = 0; i < numOfArgs; i++) { + *_GWS(stackTop)++ = *data; + data += direction; + } + } else { + *_GWS(stackTop)++ = (uint32)(*_GWS(myArg1)); + } +} + +static void op_POP(Anim8 *myAnim8) { + uint32 *data; + int32 direction, numOfArgs, i; // startReg, + if (!_GWS(myArg1)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: pop into arg1 or start with arg1, and pop a total of arg2 values"); + } + direction = 1; + if (_GWS(myArg2)) { + if (*_GWS(myArg2) > 0) numOfArgs = (*_GWS(myArg2)) >> 16; + else { + numOfArgs = -(int)(*_GWS(myArg2)) >> 16; + direction = -1; + } + } else numOfArgs = 1; + if (((byte *)_GWS(stackTop) - (byte *)_GWS(stackBase)) < (numOfArgs << 2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0255, "underflow during pop instruction"); + } + if (_GWS(myArg2)) { + data = (uint32 *)_GWS(myArg1); + for (i = 0; i < numOfArgs; i++) { + *data = *(--_GWS(stackTop)); + data += direction; + } + } else { + *_GWS(myArg1) = (frac16)(*(--_GWS(stackTop))); + } +} + +static void op_JSR(Anim8 *myAnim8) { + int32 dummy; + + if (myAnim8->returnStackIndex >= JSR_STACK_MAX) { + ws_LogErrorMsg(FL, "Max number of nested jsr instructions is: %ld", JSR_STACK_MAX); + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0256, "jsr() failed"); + } + myAnim8->returnHashes[myAnim8->returnStackIndex] = myAnim8->sequHash; + myAnim8->returnOffsets[myAnim8->returnStackIndex] = myAnim8->pcOffset; + myAnim8->returnStackIndex++; + + // Find the sequence + if ((myAnim8->sequHandle = ws_GetSEQU((uint32)*_GWS(myArg1) >> 16, &dummy, &myAnim8->pcOffset)) == nullptr) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025f, "jsr() failed"); + } + myAnim8->sequHash = (uint32)*_GWS(myArg1) >> 16; + + dbg_LaunchSequence(myAnim8); +} + +static void op_RETURN(Anim8 *myAnim8) { + int32 dummy, dummy2; + uint32 returnSequHash, returnOffset; + + if (myAnim8->returnStackIndex <= 0) { + op_END(myAnim8); + return; + } + + myAnim8->returnStackIndex--; + returnSequHash = myAnim8->returnHashes[myAnim8->returnStackIndex]; + returnOffset = myAnim8->returnOffsets[myAnim8->returnStackIndex]; + + // Find the sequence + if ((myAnim8->sequHandle = ws_GetSEQU((uint32)returnSequHash, &dummy, &dummy2)) == nullptr) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025f, "return() failed"); + } + myAnim8->sequHash = returnSequHash; + myAnim8->pcOffset = returnOffset; + + dbg_LaunchSequence(myAnim8); +} + +static void op_GET_CELS_COUNT(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = series_count(arg2)"); + } + *_GWS(myArg1) = GetWSAssetCELCount((uint32)(*_GWS(myArg2)) >> 24) << 16; +} + +static void op_GET_CELS_FRAME_RATE(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = series_frame_rate(arg2)"); + } + *_GWS(myArg1) = GetWSAssetCELFrameRate((uint32)(*_GWS(myArg2)) >> 24); +} + +static void op_GET_CELS_PIX_SPEED(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: arg1 = series_pix_speed(arg2)"); + } + *_GWS(myArg1) = GetWSAssetCELPixSpeed((uint32)(*_GWS(myArg2)) >> 24); +} + +static void op_SET_INDEX(Anim8 *myAnim8) { + if (!_GWS(myArg1)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: index_reg = arg1"); + } + _GWS(indexReg) = *_GWS(myArg1) >> 16; +} + +static void op_SET_LAYER(Anim8 *myAnim8) { + Anim8 *tempAnim8; + int32 myLayer, newLayer; + if (!_GWS(myArg1)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: set_layer(arg1)"); + } + + newLayer = *_GWS(myArg1) >> 16; + myLayer = myAnim8->myLayer; + + if (myLayer == newLayer) { + return; + } + + // If we are moving myAnim8 closer to the front (screen) + if ((newLayer < myLayer) && (myAnim8->infront)) { + + //search "upward" to find the new position for myAnim8 + tempAnim8 = myAnim8->infront; + while (tempAnim8 && (tempAnim8->myLayer > newLayer)) { + tempAnim8 = tempAnim8->infront; + } + + // If myAnim8 is to be moved + if (tempAnim8 != myAnim8->infront) { + + // Remove myAnim8 from the list + myAnim8->infront->behind = myAnim8->behind; + if (myAnim8->behind) { + myAnim8->behind->infront = myAnim8->infront; + } else { + _GWS(myCruncher)->backLayerAnim8 = myAnim8->infront; + } + + // If it belongs at the top layer of the list + if (!tempAnim8) { + myAnim8->infront = nullptr; + myAnim8->behind = _GWS(myCruncher)->frontLayerAnim8; + _GWS(myCruncher)->frontLayerAnim8->infront = myAnim8; + _GWS(myCruncher)->frontLayerAnim8 = myAnim8; + } + + // Else it belongs after tempAnim8 + else { + myAnim8->infront = tempAnim8; + myAnim8->behind = tempAnim8->behind; + tempAnim8->behind->infront = myAnim8; + tempAnim8->behind = myAnim8; + } + } + } + + // Else we are moving myAnim8 close to the back (further from the screen) + else if ((newLayer > myLayer) && (myAnim8->behind)) { + + //search "downward" to find the new position for myAnim8 + tempAnim8 = myAnim8->behind; + while (tempAnim8 && (tempAnim8->myLayer < newLayer)) { + tempAnim8 = tempAnim8->behind; + } + + // If myAnim8 is to be moved + if (tempAnim8 != myAnim8->behind) { + + // Remove it from the list + if (myAnim8->infront) { + myAnim8->infront->behind = myAnim8->behind; + } else { + _GWS(myCruncher)->frontLayerAnim8 = myAnim8->behind; + } + myAnim8->behind->infront = myAnim8->infront; + + // If it belongs at the bottom of the list + if (!tempAnim8) { + myAnim8->infront = _GWS(myCruncher)->backLayerAnim8; + myAnim8->behind = nullptr; + _GWS(myCruncher)->backLayerAnim8->behind = myAnim8; + _GWS(myCruncher)->backLayerAnim8 = myAnim8; + } + + // Else it belongs right before tempAnim8 + else { + myAnim8->infront = tempAnim8->infront; + myAnim8->behind = tempAnim8; + tempAnim8->infront->behind = myAnim8; + tempAnim8->infront = myAnim8; + } + } + } + + // Now, make sure the layer is set in both myAnim8, and the register + myAnim8->myLayer = newLayer; + myAnim8->myRegs[IDX_LAYER] = newLayer << 16; +} + +static void op_SET_DEPTH(Anim8 *myAnim8) { + int32 myDepth; + if (!_GWS(myArg1)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: set_depth(arg1)"); + } + if (!_GWS(myDepthTable)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x02ff, "op_SET_DEPTH() failed - no depth table."); + } + for (myDepth = 0; myDepth < 15; myDepth++) { + if (_GWS(myDepthTable)[myDepth + 1] < (int)(*_GWS(myArg1) >> 16)) break; + } + _GWS(dataArg1) = (myAnim8->myRegs[IDX_LAYER] & 0xffffff) + (myDepth << 24); + _GWS(myArg1) = &_GWS(dataArg1); + op_SET_LAYER(myAnim8); +} + +static void op_SET_DATA(Anim8 *myAnim8) { + if (!_GWS(myArg2)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0251, "functionality: set_data(arg1, arg2)"); + } + if ((myAnim8->dataHandle = ws_GetDATA(*_GWS(myArg1) >> 16, *_GWS(myArg2) >> 16, &myAnim8->dataOffset)) == nullptr) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025f, "set_data() failed."); + } +} + +static void op_OPEN_STREAM_SS(Anim8 *myAnim8) { + CCB *myCCB; + + if (!_GWS(myArg1)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0250, "functionality: stream_series(arg1)"); + } + + if (!myAnim8->myCCB) { + // Allocate and initialize a CCB structure + if ((myAnim8->myCCB = (CCB *)mem_alloc(sizeof(CCB), "CCB")) == nullptr) { + ws_LogErrorMsg(FL, "Out of memory - mem requested: %ld.", sizeof(CCB)); + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x02fe, "open_ss_stream() failed."); + } + if (!InitCCB(myAnim8->myCCB)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025d, "open_ss_stream() failed."); + } + } + + myCCB = myAnim8->myCCB; + ShowCCB(myCCB); + myCCB->flags |= CCB_SKIP; + if (!ws_OpenSSstream((StreamFile *)(*_GWS(myArg1)), myAnim8)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0258, "open_ss_stream() failed."); + } + if (myAnim8->myRegs[IDX_W] < 0) myAnim8->myRegs[IDX_W] = -myCCB->source->w << 16; + else myAnim8->myRegs[IDX_W] = myCCB->source->w << 16; + myAnim8->myRegs[IDX_H] = myCCB->source->h << 16; + _GWS(mapTheCel) = true; +} + +static void op_NEXT_STREAM_SS(Anim8 *myAnim8) { + CCB *myCCB; + if (!myAnim8->myCCB) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0253, "next_ss_stream() failed."); + } + myCCB = myAnim8->myCCB; + myCCB->flags |= CCB_SKIP; + if (!ws_GetNextSSstreamCel(myAnim8)) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x0259, "next_ss_stream() failed."); + } + if (myAnim8->myRegs[IDX_W] < 0) { + myAnim8->myRegs[IDX_W] = -myCCB->source->w << 16; + } else { + myAnim8->myRegs[IDX_W] = myCCB->source->w << 16; + } + myAnim8->myRegs[IDX_H] = myCCB->source->h << 16; + _GWS(mapTheCel) = true; +} + +static void op_CLOSE_STREAM_SS(Anim8 *myAnim8) { + CCB *myCCB; + if (!myAnim8->myCCB) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x02f3, "close_ss_stream() failed."); + } + myCCB = myAnim8->myCCB; + ws_CloseSSstream(myCCB); + HideCCB(myCCB); +} + + +void (*pCodeJmpTable[])(Anim8 *myAnim8) = { + &op_END, //0 + &op_CLEAR, //1 + &op_SET, //2 + &op_COMPARE, //3 + &op_ADD, //4 + &op_SUB, //5 + &op_MULT, //6 + &op_DIV, //7 + &op_AND, //8 + &op_OR, //9 + &op_NOT, //10 + &op_SIN, //11 + &op_COS, //12 + &op_ABS, //13 + &op_MIN, //14 + &op_MAX, //15 + &op_MOD, //16 + &op_FLOOR, //17 + &op_ROUND, //18 + &op_CEIL, //19 + &op_POINT, //20 + &op_DIST2D, //21 + &op_CRUNCH, //22 + &op_BRANCH, //23 + &op_SETCEL, //24 + &op_SEQ_SEND_MSG, //25 + &op_PUSH, //26 + &op_POP, //27 + &op_JSR, //28 + &op_RETURN, //29 + &op_GET_CELS_COUNT, //30 + &op_GET_CELS_FRAME_RATE, //31 + &op_GET_CELS_PIX_SPEED, //32 + &op_SET_INDEX, //33 + &op_SET_LAYER, //34 + &op_SET_DEPTH, //35 + &op_SET_DATA, //36 + &op_OPEN_STREAM_SS, //37 + &op_NEXT_STREAM_SS, //38 + &op_CLOSE_STREAM_SS //39 +}; + + +// The guts of the engine. This proc executes an anim8s program. +bool CrunchAnim8(Anim8 *myAnim8); +bool CrunchAnim8(Anim8 *myAnim8) { + bool moveTheCel = false; + frac16 timeElapsed, percentDist; + int32 tempAngle; + frac16 oldX, oldY, oldS; + int32 oldW, oldH, oldR; + int32 myInstruction; + frac16 *myRegs; + uint32 *myPC, *oldPC; + + // Get the register set for myAnim8 + myRegs = myAnim8->myRegs; + + // Initialize the globals (flags, mostly) used in processing op codes + _GWS(keepProcessing) = false; + _GWS(terminated) = false; + _GWS(mapTheCel) = false; + _GWS(compareCCR) = 0; + + //store the old values, so we can tell if we need to remap the Sprite + oldX = myRegs[IDX_X]; + oldY = myRegs[IDX_Y]; + oldS = myRegs[IDX_S]; + oldW = myRegs[IDX_W] >> 16; + oldH = myRegs[IDX_H] >> 16; + oldR = myRegs[IDX_R] >> 16; + + // Check to see if we are still in an execution loop, or if it is time to + // Interpret further instructions + if ((myAnim8->switchTime >= 0) && ((int)_GWS(ws_globals)[GLB_TIME] >= myAnim8->switchTime)) { + _GWS(keepProcessing) = true; + } + + // Decrement the timer register + myRegs[IDX_TIMER] -= (_GWS(ws_globals)[GLB_WATCH_DOG] << 16); + + // Interpret pCode instructions until we hit something signalling to stop + while (_GWS(keepProcessing)) { + dbg_SetCurrSequInstr(myAnim8, _GWS(compareCCR)); + + myPC = (uint32 *)((int32)*(myAnim8->sequHandle) + myAnim8->pcOffset); + oldPC = myPC; + _GWS(pcOffsetOld) = myAnim8->pcOffset; + + if ((myInstruction = ws_PreProcessPcode(&myPC, myAnim8)) < 0) { + ws_Error(myAnim8->myMachine, ERR_SEQU, 0x025c, nullptr); + } + + myAnim8->pcOffset += (int32)myPC - (int32)oldPC; + pCodeJmpTable[myInstruction](myAnim8); + } + + if (_GWS(terminated)) { + if (_GWS(mapTheCel) || (oldR != (int)(myRegs[IDX_R] >> 16)) || + (oldW != (int)(myRegs[IDX_W] >> 16)) || (oldH != (int)(myRegs[IDX_H] >> 16)) || + (oldS != (int)myRegs[IDX_S])) { + myAnim8->flags |= TAG_MAP_CEL; + } else if ((oldX != myRegs[IDX_X]) || (oldY != myRegs[IDX_Y])) { + myAnim8->flags |= TAG_MOVE_CEL; + } + + return false; + } + + if (myAnim8->flags) { + timeElapsed = (_GWS(ws_globals)[GLB_TIME] - myAnim8->startTime) << 16; + + // This must be checked before TAG_TARGS because a bez path can use a target scale and rotate + // And we don't want to accidently setup a target x or y. + // NOTE: for both bez paths, and targets, the time for the anim8 to reach the target or traverse + // the path is stored in IDX_TRANS_TIME, however, in order to determine how far aint32 the + // path we should be, we normally compute the elapsed time divided by the trans time. + // therefore we eliminate a division if we store the reciprocated trans time. + // This also serves as an initialization flag - if the trans time is > 1. + if ((myAnim8->flags >> 16) &TAG_BEZ) { + if (myAnim8->transTime > 0x10000) { + myAnim8->transTime = RecipUF16(myAnim8->transTime); + GetBezCoeffs(&myRegs[IDX_BEZ_CTRL], &myRegs[IDX_BEZ_COEFF]); + if ((myAnim8->flags >> 16) &TAG_TARGS) { + myAnim8->start_s = myRegs[IDX_S]; + myAnim8->start_r = myRegs[IDX_R]; + } + } + percentDist = MulSF16(timeElapsed, myAnim8->transTime); + if (percentDist < 0x10000) { + GetBezPoint(&myRegs[IDX_X], &myRegs[IDX_Y], &myRegs[IDX_BEZ_COEFF], percentDist); + if ((myAnim8->flags >> 16) &TAG_TARGS) { + myRegs[IDX_S] = myAnim8->start_s + MulSF16(percentDist, myRegs[IDX_TARG_S] - myAnim8->start_s); + myRegs[IDX_R] = myAnim8->start_r + MulSF16(percentDist, myRegs[IDX_TARG_R] - myAnim8->start_r); + _GWS(mapTheCel) = true; + } else moveTheCel = true; + } else if ((myRegs[IDX_X] != myRegs[IDX_BEZ_CTRL + 6]) || (myRegs[IDX_Y] != myRegs[IDX_BEZ_CTRL + 7])) { + myRegs[IDX_X] = myRegs[IDX_BEZ_CTRL + 6]; + myRegs[IDX_Y] = myRegs[IDX_BEZ_CTRL + 7]; + if ((myAnim8->flags >> 16) &TAG_TARGS) { + myRegs[IDX_S] = myRegs[IDX_TARG_S]; + myRegs[IDX_R] = myRegs[IDX_TARG_R]; + _GWS(mapTheCel) = true; + } else moveTheCel = true; + } + } + // Vectors must be checked before deltas, since vectors are converted to deltas at + // Initialization. + else if (((myAnim8->flags >> 16) &TAG_VECTORS) && (timeElapsed == 0)) { + myAnim8->start_x = myRegs[IDX_X]; + myAnim8->start_y = myRegs[IDX_Y]; + if ((myAnim8->flags >> 16) &TAG_DELTAS) { + myAnim8->start_s = myRegs[IDX_S]; + myAnim8->start_r = myRegs[IDX_R]; + } + tempAngle = (myRegs[IDX_THETA] >> 16) & 0xff; + myRegs[IDX_DELTA_X] = MulSF16(myRegs[IDX_VELOCITY], sinTable[tempAngle]); + myRegs[IDX_DELTA_Y] = MulSF16(myRegs[IDX_VELOCITY], -(int)cosTable[tempAngle]); + myAnim8->flags |= (TAG_DELTAS << 16); + } else if ((myAnim8->flags >> 16) &TAG_DELTAS) { + if (timeElapsed == 0) { + myAnim8->start_x = myRegs[IDX_X]; + myAnim8->start_y = myRegs[IDX_Y]; + myAnim8->start_s = myRegs[IDX_S]; + myAnim8->start_r = myRegs[IDX_R]; + } else { + myRegs[IDX_X] = myAnim8->start_x + MulSF16(timeElapsed, myRegs[IDX_DELTA_X]); + myRegs[IDX_Y] = myAnim8->start_y + MulSF16(timeElapsed, myRegs[IDX_DELTA_Y]); + if (myRegs[IDX_DELTA_R] || myRegs[IDX_DELTA_S]) { + myRegs[IDX_S] = myAnim8->start_s + MulSF16(timeElapsed, myRegs[IDX_DELTA_S]); + myRegs[IDX_R] = myAnim8->start_r + MulSF16(timeElapsed, myRegs[IDX_DELTA_R]); + _GWS(mapTheCel) = true; + } else moveTheCel = true; + } + } else if ((myAnim8->flags >> 16) &TAG_TARGS) { + if (myAnim8->transTime > 0x10000) { + myAnim8->start_s = myRegs[IDX_S]; + myAnim8->start_r = myRegs[IDX_R]; + myAnim8->start_x = myRegs[IDX_X]; + myAnim8->start_y = myRegs[IDX_Y]; + myAnim8->transTime = RecipUF16(myAnim8->transTime); + } + percentDist = MulSF16(timeElapsed, myAnim8->transTime); + if (percentDist < 0x10000) { + myRegs[IDX_X] = myAnim8->start_x + MulSF16(percentDist, myRegs[IDX_TARG_X] - myAnim8->start_x); + myRegs[IDX_Y] = myAnim8->start_y + MulSF16(percentDist, myRegs[IDX_TARG_Y] - myAnim8->start_y); + if (myRegs[IDX_TARG_R] || (myRegs[IDX_TARG_S] != myRegs[IDX_S])) { + myRegs[IDX_S] = myAnim8->start_s + MulSF16(percentDist, myRegs[IDX_TARG_S] - myAnim8->start_s); + myRegs[IDX_R] = myAnim8->start_r + MulSF16(percentDist, myRegs[IDX_TARG_R] - myAnim8->start_r); + _GWS(mapTheCel) = true; + } else moveTheCel = true; + } else { + myRegs[IDX_X] = myRegs[IDX_TARG_X]; + myRegs[IDX_Y] = myRegs[IDX_TARG_Y]; + myRegs[IDX_S] = myRegs[IDX_TARG_S]; + myRegs[IDX_R] = myRegs[IDX_TARG_R]; + myAnim8->flags &= ~TAG_TARGS; + _GWS(mapTheCel) = true; + } + } + } + if (_GWS(mapTheCel) || (oldR != (int)(myRegs[IDX_R] >> 16)) || (oldW != (int)(myRegs[IDX_W] >> 16)) || + (oldH != (int)(myRegs[IDX_H] >> 16)) || (oldS != (int)myRegs[IDX_S])) { + _GWS(mapTheCel) = true; + } else if ((oldX != myRegs[IDX_X]) || (oldY != myRegs[IDX_Y])) { + moveTheCel = true; + } + if (moveTheCel || _GWS(mapTheCel) || (myAnim8->flags & (TAG_MAP_CEL | TAG_MOVE_CEL))) { + Cel_msr(myAnim8); + } + return true; +} + +void ws_CrunchAnim8s(int16 *depth_table) { + Anim8 *currAnim8; + EOSreq *tempEOSreq; + + // Make sure the cruncher has been initialized + VERIFY_INTIALIZED("ws_CrunchAnim8s()"); + + //set up some of the _GWS(ws_globals) vars used for processing + _GWS(myDepthTable) = depth_table; + + _GWS(crunchNext) = nullptr; + currAnim8 = _GWS(myCruncher)->firstAnim8ToCrunch; + while (currAnim8) { + _GWS(crunchNext) = currAnim8->next; + if (currAnim8->active) { + if (!CrunchAnim8(currAnim8)) { + // If false was returned, this implies that an "END" op has been reached. + // Remove from the active list. Note: the machine still points to the anim8. + currAnim8->active = false; + if (currAnim8->eosReqOffset >= 0) { + // If the above field has a value, this implies that an On end sequence + //signal has been requested. If so, report back to the machine. + if ((tempEOSreq = (EOSreq *)mem_get_from_stash(_GWS(memtypeEOS), "+EOS")) == nullptr) return; + tempEOSreq->myAnim8 = currAnim8; + tempEOSreq->prev = nullptr; + tempEOSreq->next = _GWS(EOSreqList); + if (_GWS(EOSreqList)) { + _GWS(EOSreqList)->prev = tempEOSreq; + } + _GWS(EOSreqList) = tempEOSreq; + } + } + } + currAnim8 = _GWS(crunchNext); + } + _GWS(crunchNext) = nullptr; +} + +void ws_CrunchEOSreqs(void) { + int32 pcOffset, pcCount; + machine *myXM; + EOSreq *tempEOSreq; + + // Make sure the cruncher has been initialized + VERIFY_INTIALIZED("ws_CrunchEOSreqs()"); + + // Loop through, and handle all the eos requests + tempEOSreq = _GWS(EOSreqList); + while (tempEOSreq) { + _GWS(EOSreqList) = _GWS(EOSreqList)->next; + if (_GWS(EOSreqList)) { + _GWS(EOSreqList)->prev = nullptr; + } + pcOffset = tempEOSreq->myAnim8->eosReqOffset; + pcCount = tempEOSreq->myAnim8->eosReqCount; + myXM = tempEOSreq->myAnim8->myMachine; + tempEOSreq->myAnim8->eosReqOffset = -1; + mem_free_to_stash((void *)tempEOSreq, _GWS(memtypeEOS)); + ws_StepWhile(myXM, pcOffset, pcCount); + tempEOSreq = _GWS(EOSreqList); + } +} + +void ws_KillCruncher() { Anim8 *myAnim8; - //make sure the cruncher has been initialized + // Make sure the cruncher has been initialized VERIFY_INTIALIZED("ws_KillCruncher()"); - myAnim8 = _G(myCruncher)->firstAnim8ToCrunch; + myAnim8 = _GWS(myCruncher)->firstAnim8ToCrunch; while (myAnim8) { - _G(myCruncher)->firstAnim8ToCrunch = myAnim8->next; + _GWS(myCruncher)->firstAnim8ToCrunch = myAnim8->next; if (myAnim8->myCCB) { KillCCB(myAnim8->myCCB, false); } - mem_free((void *)myAnim8->myRegs); - myAnim8 = _G(myCruncher)->firstAnim8ToCrunch; + mem_free(myAnim8->myRegs); + myAnim8 = _GWS(myCruncher)->firstAnim8ToCrunch; + } + mem_free(_GWS(myCruncher)); + if (_GWS(stackBase)) { + mem_free(_GWS(stackBase)); } - mem_free(_G(myCruncher)); + _GWS(cruncherInitialized) = false; +} - if (_G(stackBase)) - mem_free(_G(stackBase)); +// This proc was designed to allow the state machine to issue an OnEndSeq request +// In which the cruncher will signal the state machine should an anim8 ever finish. +bool ws_OnEndSeqRequest(Anim8 *myAnim8, int32 pcOffset, int32 pcCount) { - _G(cruncherInitialized) = false; + // Make sure the cruncher has been initialized + VERIFY_INTIALIZED("ws_OnEndSeqRequest()"); + + myAnim8->eosReqOffset = pcOffset; + myAnim8->eosReqCount = pcCount; + + return true; +} + + +void ws_CancelOnEndSeq(Anim8 *myAnim8) { + + // Make sure the cruncher has been initialized + VERIFY_INTIALIZED("ws_CancelOnEndSeq()"); + + myAnim8->eosReqOffset = -1; } } // End of namespace M4 diff --git a/engines/m4/wscript/ws_cruncher.h b/engines/m4/wscript/ws_cruncher.h index 5b95ea397ac..af1572bfb87 100644 --- a/engines/m4/wscript/ws_cruncher.h +++ b/engines/m4/wscript/ws_cruncher.h @@ -28,6 +28,38 @@ namespace M4 { +#define OP_INSTR 0xfe000000 +#define OP_FORMAT1 0x01c00000 +#define OP_FORMAT2 0x00380000 +#define OP_FORMAT3 0x00070000 +#define OP_HIGH_DATA 0xffff0000 +#define OP_LOW_DATA 0x0000ffff +#define OP_DATA_SIGN 0x8000 +#define OP_DATA_VALUE 0x7fff + +#define FMT_NOTHING 0 +#define FMT_LOCAL_SRC 1 +#define FMT_GLOBAL_SRC 2 +#define FMT_INT15 3 +#define FMT_4_11 4 +#define FMT_7_8 5 +#define FMT_11_4 6 +#define FMT_15_0 7 +#define REG_SET_IDX 0x0fff +#define REG_SET_IDX_REG 0x8000 +#define LOCAL_FMT 0x7000 +#define LOCAL_FMT_PARENT 0x0000 +#define LOCAL_FMT_REG 0x1000 +#define LOCAL_FMT_DATA 0x2000 + +#define BRANCH_BR 0 +#define BRANCH_BLT 1 +#define BRANCH_BLE 2 +#define BRANCH_BE 3 +#define BRANCH_BNE 4 +#define BRANCH_BGE 5 +#define BRANCH_BGT 6 + struct EOSreq { EOSreq *next = nullptr; EOSreq *prev = nullptr; @@ -70,10 +102,29 @@ struct WSCruncher_Globals { }; extern int32 *ws_GetDataFormats(); - extern bool ws_InitCruncher(); extern void ws_KillCruncher(); +extern Anim8 *ws_AddAnim8ToCruncher(machine *m, int32 sequHash); + +/** + * This procedure assumes a machine has a slot with it's own memory + */ +extern bool ws_ChangeAnim8Program(machine *m, int32 newSequHash); + +/** + * This procedure flags the anim8 slot as empty + */ +extern void ws_RemoveAnim8FromCruncher(Anim8 *myAnim8); + +extern bool ws_PauseAnim8(Anim8 *myAnim8); +extern bool ws_ResumeAnim8(Anim8 *myAnim8); +extern int32 ws_PreProcessPcode(uint32 **PC, Anim8 *myAnim8); +extern void ws_CrunchAnim8s(int16 *depth_table); +extern void ws_CrunchEOSreqs(); +extern bool ws_OnEndSeqRequest(Anim8 *myAnim8, int32 pcOffset, int32 pcCount); +extern void ws_CancelOnEndSeq(Anim8 *myAnim8); + } // End of namespace M4 #endif diff --git a/engines/m4/wscript/ws_hal.cpp b/engines/m4/wscript/ws_hal.cpp index ac1182852d4..1ee7effe65f 100644 --- a/engines/m4/wscript/ws_hal.cpp +++ b/engines/m4/wscript/ws_hal.cpp @@ -19,34 +19,454 @@ * */ +#include "common/savefile.h" +#include "common/textconsole.h" #include "m4/wscript/ws_hal.h" +#include "m4/wscript/wst_regs.h" #include "m4/core/errors.h" +#include "m4/core/imath.h" +#include "m4/dbg/debug.h" +#include "m4/graphics/rend.h" #include "m4/gui/gui_vmng.h" #include "m4/vars.h" +#include "m4/m4.h" namespace M4 { bool ws_InitHAL() { - _G(deadRectList) = NULL; + _GWS(deadRectList) = nullptr; return true; } void ws_KillHAL() { - vmng_DisposeRectList(&_G(deadRectList)); + vmng_DisposeRectList(&_GWS(deadRectList)); +} + +void ws_DumpMachine(machine *m, Common::WriteStream *logFile) { + Anim8 *myAnim8; + CCB *myCCB; + frac16 *myRegs; + int32 i; + double tempFloat; + + if (!m || !logFile) + return; + + // Print out the machine name, hash, and physical address + logFile->writeString(Common::String::format("Machine Name: %s\n\tHash: %ld\n\tAddress: 0x%08lx\n\n", m->machName, m->myHash, (int32)m)); + + // If we have an anim8 for this machine + if (m->myAnim8) { + myAnim8 = m->myAnim8; + + // Print out the anim8 hash, and physical address + logFile->writeString(Common::String::format("Sequence Hash: %ld\n\tAddress: 0x%08lx\n\n", myAnim8->sequHash, (int32)myAnim8)); + + // And if this anim8 has registers + if (myAnim8->myRegs) { + myRegs = myAnim8->myRegs; + logFile->writeString("Registers:\n"); + + // Loop through the main set of registers, and dump out the contents + for (i = 0; i < IDX_COUNT; i++) { + tempFloat = (float)(myRegs[i] >> 16) + (float)((float)(myRegs[i] & 0xffff) / (float)65536); + logFile->writeString(Common::String::format("\t%ld\t%s:\t\t%.2f\t\t0x%08lx\n", i, myRegLabels[i], tempFloat, myRegs[i])); + } + + // If the anim8 has extra local regs + if (myAnim8->numLocalVars > 0) { + for (i = IDX_COUNT; i < IDX_COUNT + myAnim8->numLocalVars; i++) { + tempFloat = (float)(myRegs[i] >> 16) + (float)((float)(myRegs[i] & 0xffff) / (float)65536); + logFile->writeString(Common::String::format("\t%ld\tlocal.%ld:\t\t%.2f\t\t0x%08lx\n", i, i - IDX_COUNT, tempFloat, myRegs[i])); + } + } + logFile->writeString(Common::String::format("\n")); + } + + // If this anim8 has a CCB + if (myAnim8->myCCB) { + myCCB = myAnim8->myCCB; + + logFile->writeString(Common::String::format("Sprite Series Name: %s\tAddress:0x%08lx\tFlags0x%08lx\n", myCCB->seriesName, (uint32)myCCB, myCCB->flags)); + logFile->writeString(Common::String::format("\tCurrent Location: (%ld, %ld), (%ld, %ld)\n", myCCB->currLocation->x1, myCCB->currLocation->y1, + myCCB->currLocation->x2, myCCB->currLocation->y2)); + logFile->writeString(Common::String::format("\tNew Location: (%ld, %ld), (%ld, %ld)\n", myCCB->newLocation->x1, myCCB->newLocation->y1, + myCCB->newLocation->x2, myCCB->newLocation->y2)); + logFile->writeString(Common::String::format("\tscale: %ld\n", myCCB->scaleX)); + logFile->writeString(Common::String::format("\tlayer: %ld\n", myCCB->layer)); + } + } +} + +void ws_Error(machine *m, int32 errorType, trigraph errorCode, const char *errMsg) { + Common::OutSaveFile *logFile; + char description[MAX_STRING_SIZE]; + + // Find the error description + error_look_up(errorCode, description); + + // Open the logFile + logFile = g_system->getSavefileManager()->openForSaving("ws_mach.log"); + + // Give the WS debugger a chance to indicate the error to the apps programmer + dbg_WSError(logFile, m, errorType, description, errMsg, _GWS(pcOffsetOld)); + + // Dump out the machine to the logFile + ws_DumpMachine(m, logFile); + + // Close the logFile + if (logFile) + f_io_close(logFile); + + // Now we fatal abort + error_show(FL, errorCode, errMsg); +} + +void ws_LogErrorMsg(char *sourceFile, int32 lineNum, const char *fmt, ...) { + Common::OutSaveFile *logFile; + Common::String msgBuff; + va_list ap; + + va_start(ap, fmt); + msgBuff == Common::String::vformat(fmt, ap); + va_end(ap); + + logFile = g_system->getSavefileManager()->openForSaving("ws_mach.log"); + if (logFile) { + logFile->writeString(Common::String::format("Source Code Index: %s:%ld\n", sourceFile, lineNum)); + logFile->writeString(Common::String::format("%s\n\n", msgBuff.c_str())); + + logFile->finalize(); + delete logFile; + } + + term_message("Source Code Index: %s:%ld", sourceFile, lineNum); + term_message("%s", msgBuff.c_str()); +} + +machine *kernel_timer_callback(int32 ticks, int16 trigger, MessageCB callMe) { + _GWS(ws_globals)[GLB_TEMP_1] = (frac16)(ticks << 16); + _GWS(ws_globals)[GLB_TEMP_2] = (frac16)trigger; + + return (TriggerMachineByHash(1, nullptr, -1, -1, callMe, false, "timer callback")); +} + +void DrawSprite(CCB *myCCB, Anim8 *myAnim8, Buffer *halScrnBuf, GrBuff *screenCodeBuff, uint8 *myPalette, uint8 *ICT, + M4Rect *clipRect, M4Rect *updateRect) { +#ifdef TODO + M4sprite *source; + + // Temporary var to prevent excessive dereferences + source = myCCB->source; + + if (!(myCCB->flags & CCB_DISC_STREAM)) { + //make sure the sprite is still in memory + if (!source->sourceHandle || !*(source->sourceHandle)) { + ws_LogErrorMsg(FL, "Sprite series is no longer in memory."); + ws_Error(myAnim8->myMachine, ERR_INTERNAL, 0x02ff, "Error during ws_DoDisplay()"); + } + // Lock the sprite handle + HLock(source->sourceHandle); + source->data = (uint8 *)((int32) * (source->sourceHandle) + source->sourceOffset); + } + + RendGrBuff Destination; + DrawRequestX dr; + RendCell Frame; + + Destination.Width = halScrnBuf->stride; + Destination.Height = halScrnBuf->h; + Destination.PixMap = (void *)halScrnBuf->data; + + dr.x = myAnim8->myRegs[IDX_X] >> 16; + dr.y = myAnim8->myRegs[IDX_Y] >> 16; + dr.scale_x = myCCB->scaleX; + dr.scale_y = myCCB->scaleY; + dr.depth_map = screenCodeBuff->data; + dr.Pal = (RGBcolor *)myPalette; + dr.ICT = ICT; + dr.depth = myCCB->layer >> 8; + + Frame.hot_x = myCCB->source->xOffset; + Frame.hot_y = myCCB->source->yOffset; + Frame.Width = source->w; + Frame.Height = source->h; + if ((!myPalette) || (!ICT)) { + Frame.Comp = (uint32)(source->encoding & 0x7f); + } else { + Frame.Comp = (uint32)source->encoding; + } + Frame.data = source->data; + + // And draw the sprite + render_sprite_to_8BBM(&Destination, &dr, &Frame, clipRect, updateRect); + myCCB->flags &= ~CCB_REDRAW; + + if (!(myCCB->flags & CCB_DISC_STREAM)) { + //unlock the sprite's handle + HUnLock(source->sourceHandle); + } + +#else + error("TODO: DrawSprite"); +#endif +} + +void ws_DoDisplay(Buffer *background, int16 *depth_table, GrBuff *screenCodeBuff, + uint8 *myPalette, uint8 *ICT, bool updateVideo) { + CCB *myCCB; + ScreenContext *myScreen; + RectList *myRect; + RectList *drawRectList = nullptr; + int32 status, scrnX1, scrnY1; + int32 restoreBgndX1, restoreBgndY1, restoreBgndX2, restoreBgndY2; + Anim8 *myAnim8; + M4Rect *currRect, intersectRect, noClipRect, dummyRect; + bool greyMode; +#ifdef TODO + Buffer drawSpriteBuff; + bool finished; + M4sprite *source; + uint8 myDepth; + M4Rect *newRect; + DrawRequest spriteDrawReq; +#endif + + if (((myScreen = vmng_screen_find(_G(gameDrawBuff), &status)) == nullptr) || (status != SCRN_ACTIVE)) { + return; + } + + Buffer *halScrnBuf = _G(gameDrawBuff)->get_buffer(); + noClipRect.x1 = 0; + noClipRect.y1 = 0; + noClipRect.x2 = halScrnBuf->W - 1; + noClipRect.y2 = halScrnBuf->h - 1; + + scrnX1 = myScreen->x1; + scrnY1 = myScreen->y1; + + greyMode = krn_GetGreyMode(); + + // Intialize the drawRectList to the deadRectList + drawRectList = _GWS(deadRectList); + _GWS(deadRectList) = nullptr; + + // The drawRectList already contains all the areas of the screen that need the + // background updated + // Update the background behind the current rect list - if we are in greyMode, we do this later + if (background && background->data) { + myRect = drawRectList; + while (myRect) { + restoreBgndX1 = imath_max(myRect->x1, 0); + restoreBgndY1 = imath_max(myRect->y1, 0); + restoreBgndX2 = imath_min(myRect->x2, background->W - 1); + restoreBgndY2 = imath_min(myRect->y2, background->h - 1); + gr_buffer_rect_copy(background, halScrnBuf, restoreBgndX1, restoreBgndY1, + restoreBgndX2 - restoreBgndX1 + 1, restoreBgndY2 - restoreBgndY1 + 1); + myRect = myRect->next; + } + } + + // Now we loop back to front and update the area of each sprite that intersects the update list, + // or simply draw the sprite if it has been marked for redraw + myAnim8 = _GWS(myCruncher)->backLayerAnim8; + while (myAnim8) { + + myCCB = myAnim8->myCCB; + currRect = myCCB->currLocation; + if (myCCB && myCCB->source && (!(myCCB->flags & CCB_NO_DRAW))) { + if (myCCB->flags & CCB_REDRAW) { + + // Draw the sprite + DrawSprite(myCCB, myAnim8, halScrnBuf, screenCodeBuff, myPalette, ICT, &noClipRect, currRect); + + // Add it's new location to the update list + vmng_AddRectToRectList(&drawRectList, currRect->x1, currRect->y1, currRect->x2, currRect->y2); + } else { + + // Loop through the update list, intersect each rect with the sprites + // current location, and update redraw all overlapping areas + myRect = drawRectList; + while (myRect) { + intersectRect.x1 = imath_max(myRect->x1, currRect->x1); + intersectRect.y1 = imath_max(myRect->y1, currRect->y1); + intersectRect.x2 = imath_min(myRect->x2, currRect->x2); + intersectRect.y2 = imath_min(myRect->y2, currRect->y2); + + // Now see if there was an intersection + if ((intersectRect.x1 <= intersectRect.x2) && (intersectRect.y1 <= intersectRect.y2)) { + // Draw just the intersected region + DrawSprite(myCCB, myAnim8, halScrnBuf, screenCodeBuff, myPalette, ICT, &intersectRect, &dummyRect); + } + + // Next rect + myRect = myRect->next; + } + } + } + myAnim8 = myAnim8->infront; + } + + myRect = drawRectList; + while (myRect) { + if (updateVideo) { + if (greyMode) { + krn_UpdateGreyArea(halScrnBuf, scrnX1, scrnY1, myRect->x1, myRect->y1, myRect->x2, myRect->y2); + } + RestoreScreens(scrnX1 + myRect->x1, scrnY1 + myRect->y1, scrnX1 + myRect->x2, scrnY1 + myRect->y2); + } + myRect = myRect->next; + } + + _G(gameDrawBuff)->release(); + + // Turf the drawRectList + vmng_DisposeRectList(&drawRectList); +} + +void ws_hal_RefreshWoodscriptBuffer(cruncher *myCruncher, Buffer *background, int16 *depth_table, GrBuff *screenCodes, uint8 *myPalette, uint8 *ICT) { + error("TODO: ws_hal_RefreshWoodscriptBuffer"); +} + +void GetBezCoeffs(frac16 *ctrlPoints, frac16 *coeffs) { + frac16 x0, x0mult3, x1mult3, x1mult6, x2mult3, x3; + frac16 y0, y0mult3, y1mult3, y1mult6, y2mult3, y3; + + x0 = ctrlPoints[0]; + x0mult3 = (x0 << 1) + x0; + x1mult3 = (ctrlPoints[2] << 1) + ctrlPoints[2]; + x1mult6 = x1mult3 << 1; + x2mult3 = (ctrlPoints[4] << 1) + ctrlPoints[4]; + x3 = ctrlPoints[6]; + + y0 = ctrlPoints[1]; + y0mult3 = (y0 << 1) + y0; + y1mult3 = (ctrlPoints[3] << 1) + ctrlPoints[3]; + y1mult6 = y1mult3 << 1; + y2mult3 = (ctrlPoints[5] << 1) + ctrlPoints[5]; + y3 = ctrlPoints[7]; + + coeffs[0] = -(int)x0 + x1mult3 - x2mult3 + x3; + coeffs[2] = x0mult3 - x1mult6 + x2mult3; + coeffs[4] = -(int)x0mult3 + x1mult3; + coeffs[6] = x0; + + coeffs[1] = -(int)y0 + y1mult3 - y2mult3 + y3; + coeffs[3] = y0mult3 - y1mult6 + y2mult3; + coeffs[5] = -(int)y0mult3 + y1mult3; + coeffs[7] = y0; + + return; +} + +void GetBezPoint(frac16 *x, frac16 *y, frac16 *coeffs, frac16 tVal) { + + *x = coeffs[6] + + MulSF16(tVal, (coeffs[4] + + MulSF16(tVal, (coeffs[2] + + MulSF16(tVal, coeffs[0]))))); + + *y = coeffs[7] + + MulSF16(tVal, (coeffs[5] + + MulSF16(tVal, (coeffs[3] + + MulSF16(tVal, coeffs[1]))))); + + return; +} + +bool InitCCB(CCB *myCCB) { + myCCB->flags = CCB_SKIP; + myCCB->source = nullptr; + if ((myCCB->currLocation = (M4Rect *)mem_alloc(sizeof(M4Rect), "Rectangle")) == nullptr) { + return false; + } + myCCB->currLocation->x1 = -1; + myCCB->currLocation->y1 = -1; + myCCB->currLocation->x2 = -1; + myCCB->currLocation->y2 = -1; + if ((myCCB->newLocation = (M4Rect *)mem_alloc(sizeof(M4Rect), "Rectangle")) == nullptr) { + return false; + } + myCCB->newLocation->x1 = -1; + myCCB->newLocation->y1 = -1; + myCCB->newLocation->x2 = -1; + myCCB->newLocation->y2 = -1; + + myCCB->maxArea = nullptr; + myCCB->scaleX = 0; + myCCB->scaleY = 0; + myCCB->layer = 0; + myCCB->streamSSHeader = nullptr; + myCCB->streamSpriteSource = nullptr; + myCCB->myStream = nullptr; + myCCB->seriesName = nullptr; + + return true; +} + +void HideCCB(CCB *myCCB) { + if (!myCCB) + return; + myCCB->flags |= CCB_HIDE; + + if ((myCCB->flags & CCB_STREAM) && myCCB->maxArea) { + vmng_AddRectToRectList(&_GWS(deadRectList), myCCB->maxArea->x1, myCCB->maxArea->y1, myCCB->maxArea->x2, myCCB->maxArea->y2); + mem_free(myCCB->maxArea); + myCCB->maxArea = nullptr; + } else { + vmng_AddRectToRectList(&_GWS(deadRectList), myCCB->currLocation->x1, myCCB->currLocation->y1, myCCB->currLocation->x2, myCCB->currLocation->y2); + } +} + +void ShowCCB(CCB *myCCB) { + if (!myCCB) + return; + + myCCB->flags &= ~CCB_HIDE; +} + +void MoveCCB(CCB *myCCB, frac16 deltaX, frac16 deltaY) { + if (!myCCB || !myCCB->source) { + error_show(FL, 'WSIC'); + } + + myCCB->newLocation->x1 = myCCB->currLocation->x1 + (deltaX >> 16); + myCCB->newLocation->y1 = myCCB->currLocation->y1 + (deltaY >> 16); + myCCB->newLocation->x2 = myCCB->currLocation->x2 + (deltaX >> 16); + myCCB->newLocation->y2 = myCCB->currLocation->y2 + (deltaY >> 16); + + if (myCCB->flags & CCB_STREAM) { + if (!myCCB->maxArea) { + if ((myCCB->maxArea = (M4Rect *)mem_alloc(sizeof(M4Rect), "Rectangle")) == nullptr) { + error_show(FL, 'OOM!'); + } + myCCB->maxArea->x1 = myCCB->newLocation->x1; + myCCB->maxArea->y1 = myCCB->newLocation->y1; + myCCB->maxArea->x2 = myCCB->newLocation->x2; + myCCB->maxArea->y2 = myCCB->newLocation->y2; + } else { + myCCB->maxArea->x1 = imath_min(myCCB->maxArea->x1, myCCB->newLocation->x1); + myCCB->maxArea->y1 = imath_min(myCCB->maxArea->y1, myCCB->newLocation->y1); + myCCB->maxArea->x2 = imath_max(myCCB->maxArea->x2, myCCB->newLocation->x2); + myCCB->maxArea->y2 = imath_max(myCCB->maxArea->y2, myCCB->newLocation->y2); + } + } + + if ((myCCB->source->w != 0) && (myCCB->source->h != 0)) { + myCCB->flags |= CCB_REDRAW; + } } void KillCCB(CCB *myCCB, bool restoreFlag) { - error("TODO: KillCCB"); -#ifdef TODO if (!myCCB) { error_show(FL, 'WSIC'); } if (restoreFlag && (!(myCCB->flags & CCB_SKIP)) && (!(myCCB->flags & CCB_HIDE))) { if ((myCCB->flags & CCB_STREAM) && myCCB->maxArea) { - vmng_AddRectToRectList(&_G(deadRectList), myCCB->maxArea->x1, myCCB->maxArea->y1, + vmng_AddRectToRectList(&_GWS(deadRectList), myCCB->maxArea->x1, myCCB->maxArea->y1, myCCB->maxArea->x2, myCCB->maxArea->y2); } else { - vmng_AddRectToRectList(&_G(deadRectList), myCCB->currLocation->x1, myCCB->currLocation->y1, + vmng_AddRectToRectList(&_GWS(deadRectList), myCCB->currLocation->x1, myCCB->currLocation->y1, myCCB->currLocation->x2, myCCB->currLocation->y2); } } @@ -54,19 +474,83 @@ void KillCCB(CCB *myCCB, bool restoreFlag) { ws_CloseSSstream(myCCB); } if (myCCB->currLocation) { - mem_free((void *)myCCB->currLocation); + mem_free(myCCB->currLocation); } if (myCCB->newLocation) { - mem_free((void *)myCCB->newLocation); + mem_free(myCCB->newLocation); } if (myCCB->maxArea) { - mem_free((void *)myCCB->maxArea); + mem_free(myCCB->maxArea); } if (myCCB->source) { - mem_free((char *)myCCB->source); + mem_free(myCCB->source); } - mem_free((void *)myCCB); -#endif + + mem_free(myCCB); +} + +void Cel_msr(Anim8 *myAnim8) { + CCB *myCCB; + frac16 *myRegs; + int32 scaler; + + if (!myAnim8) { + error_show(FL, 'WSAI'); + } + + myCCB = myAnim8->myCCB; + if ((!myCCB) || (!myCCB->source)) { + error_show(FL, 'WSIC'); + } + + if ((myCCB->source->w == 0) || (myCCB->source->h == 0)) { + return; + } + + myRegs = myAnim8->myRegs; + if (!myRegs) { + error_show(FL, 'WSAI'); + } + + scaler = FixedMul(myRegs[IDX_S], 100 << 16) >> 16; + + myCCB->scaleX = myRegs[IDX_W] < 0 ? -scaler : scaler; + myCCB->scaleY = scaler; + + GetUpdateRectangle(myRegs[IDX_X] >> 16, myRegs[IDX_Y] >> 16, myCCB->source->xOffset, myCCB->source->yOffset, + myCCB->scaleX, myCCB->scaleY, myCCB->source->w, myCCB->source->h, myCCB->newLocation); + if (myCCB->flags & CCB_STREAM) { + if (!myCCB->maxArea) { + if ((myCCB->maxArea = (M4Rect *)mem_alloc(sizeof(M4Rect), "Rectangle")) == nullptr) { + error_show(FL, 'OOM!'); + } + myCCB->maxArea->x1 = myCCB->newLocation->x1; + myCCB->maxArea->y1 = myCCB->newLocation->y1; + myCCB->maxArea->x2 = myCCB->newLocation->x2; + myCCB->maxArea->y2 = myCCB->newLocation->y2; + } else { + myCCB->maxArea->x1 = imath_min(myCCB->maxArea->x1, myCCB->newLocation->x1); + myCCB->maxArea->y1 = imath_min(myCCB->maxArea->y1, myCCB->newLocation->y1); + myCCB->maxArea->x2 = imath_max(myCCB->maxArea->x2, myCCB->newLocation->x2); + myCCB->maxArea->y2 = imath_max(myCCB->maxArea->y2, myCCB->newLocation->y2); + } + } else { + vmng_AddRectToRectList(&_GWS(deadRectList), myCCB->currLocation->x1, myCCB->currLocation->y1, + myCCB->currLocation->x2, myCCB->currLocation->y2); + } + + myAnim8->flags &= ~(TAG_MAP_CEL | TAG_MOVE_CEL); + myCCB->layer = imath_max(0, myAnim8->myLayer); + myCCB->flags &= ~CCB_SKIP; + myCCB->flags |= CCB_REDRAW; + return; +} + +void ws_OverrideCrunchTime(machine *m) { + if ((!m) || (!m->myAnim8)) { + return; + } + m->myAnim8->switchTime = 0; } } // End of namespace M4 diff --git a/engines/m4/wscript/ws_hal.h b/engines/m4/wscript/ws_hal.h index c11dd7be373..36cad42320d 100644 --- a/engines/m4/wscript/ws_hal.h +++ b/engines/m4/wscript/ws_hal.h @@ -24,17 +24,47 @@ #define M4_WSCRIPT_WS_HAL_H #include "m4/wscript/ws_machine.h" +#include "m4/wscript/ws_cruncher.h" namespace M4 { +#define CCB_SKIP 0x0001 +#define CCB_HIDE 0x0002 +#define CCB_REDRAW 0x0004 +#define CCB_STREAM 0x0008 +#define CCB_DISC_STREAM 0x0010 + +#define CCB_NO_DRAW (CCB_SKIP | CCB_HIDE) + +#define ERR_INTERNAL 0 +#define ERR_SEQU 1 +#define ERR_MACH 2 + struct WSHal_Globals { RectList *_deadRectList; - int32 _pcOffsetOld; }; extern bool ws_InitHAL(); extern void ws_KillHAL(); + +extern void ws_DoDisplay(Buffer *background, int16 *depth_table, GrBuff *screenCodeBuff, + uint8 *myPalette, uint8 *ICT, bool updateVideo); +extern void ws_hal_RefreshWoodscriptBuffer(cruncher *myCruncher, Buffer *background, int16 *depth_table, GrBuff *screenCodes, uint8 *myPalette, uint8 *ICT); +extern void GetBezCoeffs(frac16 *ctrlPoints, frac16 *coeffs); +extern void GetBezPoint(frac16 *x, frac16 *y, frac16 *coeffs, frac16 tVal); +extern bool InitCCB(CCB *myCCB); +extern void HideCCB(CCB *myCCB); +extern void ShowCCB(CCB *myCCB); +//extern void SetLastCCB(CCB *myCCB); +extern void MoveCCB(CCB *myCCB, frac16 deltaX, frac16 deltaY); extern void KillCCB(CCB *myCCB, bool restoreFlag); +extern void Cel_msr(Anim8 *myAnim8); +extern void ws_OverrideCrunchTime(machine *m); + +extern bool CheckAddr(); +extern void ws_Error(machine *m, int32 errorType, quadchar errorCode, const char *errMsg); +extern void ws_DumpMachine(machine *m); +extern void ws_LogErrorMsg(char *sourceFile, int32 lineNum, char *fmt, ...); } // End of namespace M4 diff --git a/engines/m4/wscript/ws_load.cpp b/engines/m4/wscript/ws_load.cpp index 09efb239740..2fd2443d373 100644 --- a/engines/m4/wscript/ws_load.cpp +++ b/engines/m4/wscript/ws_load.cpp @@ -21,6 +21,7 @@ #include "m4/wscript/ws_load.h" #include "m4/wscript/ws_machine.h" +#include "m4/wscript/wst_regs.h" #include "m4/core/errors.h" #include "m4/core/imath.h" #include "m4/graphics/graphics.h" @@ -29,6 +30,30 @@ namespace M4 { +#define CHUNK_MACH 0x4D414348 //'MACH' +#define CHUNK_SEQU 0x53455155 //'SEQU' +#define CHUNK_DATA 0x44415441 //'DATA' +#define CHUNK_CELS 0x43454C53 //'CELS' + +#define CHUNK_NECS 0x4E454353 //INTEL 'SCEN' +#define CHUNK_HCAM 0x4843414D //INTEL 'MACH' +#define CHUNK_UQES 0x55514553 //INTEL 'SEQU' +#define CHUNK_SLEC 0x534C4543 //INTEL 'CELS' +#define CHUNK_ATAD 0x41544144 //INTEL 'DATA' + +#define MACH_NUM_STATES 0 +#define MACH_OFFSETS 1 + +#define SEQU_NUM_VARS 0 +#define SEQU_SEQU_START 1 + +#define DATA_REC_COUNT 0 +#define DATA_REC_SIZE 1 +#define DATA_REC_START 2 + +#define MAX_ASSET_HASH 255 + + static bool GetNextint32(char **assetPtr, char *endOfAssetBlock, uint32 **returnVal); static int32 ProcessCELS(const char * /*assetName*/, char **parseAssetPtr, char * /*mainAssetPtr*/, char *endOfAssetBlock, int32 **dataOffset, int32 **palDataOffset, RGB8 *myPalette); @@ -38,80 +63,80 @@ bool InitWSAssets() { int32 i; // Make sure this is only called once. - if (_G(wsloaderInitialized)) { + if (_GWS(wsloaderInitialized)) { error_show(FL, 'WSSN'); } // Allocate space for the tables used by the loader and the resource io MACHine tables - if ((_G(globalMACHnames) = (char **)mem_alloc(sizeof(char *) * 256, "MACH resource table")) == nullptr) { + if ((_GWS(globalMACHnames) = (char **)mem_alloc(sizeof(char *) * 256, "MACH resource table")) == nullptr) { return false; } - if ((_G(globalMACHHandles) = (Handle *)mem_alloc(sizeof(Handle) * 256, "CELS Handles table")) == nullptr) { + if ((_GWS(globalMACHHandles) = (MemHandle *)mem_alloc(sizeof(Handle) * 256, "CELS Handles table")) == nullptr) { return false; } - if ((_G(globalMACHoffsets) = (int32 *)mem_alloc(sizeof(int32 *) * 256, "MACH offsets table")) == nullptr) { + if ((_GWS(globalMACHoffsets) = (int32 *)mem_alloc(sizeof(int32 *) * 256, "MACH offsets table")) == nullptr) { return false; } for (i = 0; i < 256; i++) { - _G(globalMACHnames)[i] = nullptr; - _G(globalMACHHandles)[i] = nullptr; - _G(globalMACHoffsets)[i] = -1; + _GWS(globalMACHnames)[i] = nullptr; + _GWS(globalMACHHandles)[i] = nullptr; + _GWS(globalMACHoffsets)[i] = -1; } // SEQUence tables - if ((_G(globalSEQUnames) = (char **)mem_alloc(sizeof(char *) * 256, "SEQU resource table")) == nullptr) { + if ((_GWS(globalSEQUnames) = (char **)mem_alloc(sizeof(char *) * 256, "SEQU resource table")) == nullptr) { return false; } - if ((_G(globalSEQUHandles) = (Handle *)mem_alloc(sizeof(Handle) * 256, "CELS Handles table")) == nullptr) { + if ((_GWS(globalSEQUHandles) = (MemHandle *)mem_alloc(sizeof(Handle) * 256, "CELS Handles table")) == nullptr) { return false; } - if ((_G(globalSEQUoffsets) = (int32 *)mem_alloc(sizeof(int32 *) * 256, "SEQU offsets table")) == nullptr) { + if ((_GWS(globalSEQUoffsets) = (int32 *)mem_alloc(sizeof(int32 *) * 256, "SEQU offsets table")) == nullptr) { return false; } for (i = 0; i < 256; i++) { - _G(globalSEQUnames)[i] = nullptr; - _G(globalSEQUHandles)[i] = nullptr; - _G(globalSEQUoffsets)[i] = -1; + _GWS(globalSEQUnames)[i] = nullptr; + _GWS(globalSEQUHandles)[i] = nullptr; + _GWS(globalSEQUoffsets)[i] = -1; } // DATA tables - if ((_G(globalDATAnames) = (char **)mem_alloc(sizeof(char *) * 256, "DATA resource table")) == nullptr) { + if ((_GWS(globalDATAnames) = (char **)mem_alloc(sizeof(char *) * 256, "DATA resource table")) == nullptr) { return false; } - if ((_G(globalDATAHandles) = (Handle *)mem_alloc(sizeof(Handle) * 256, "CELS Handles table")) == nullptr) { + if ((_GWS(globalDATAHandles) = (MemHandle *)mem_alloc(sizeof(Handle) * 256, "CELS Handles table")) == nullptr) { return false; } - if ((_G(globalDATAoffsets) = (int32 *)mem_alloc(sizeof(int32 *) * 256, "DATA offsets table")) == nullptr) { + if ((_GWS(globalDATAoffsets) = (int32 *)mem_alloc(sizeof(int32 *) * 256, "DATA offsets table")) == nullptr) { return false; } for (i = 0; i < 256; i++) { - _G(globalDATAnames)[i] = nullptr; - _G(globalDATAHandles)[i] = nullptr; - _G(globalDATAoffsets)[i] = -1; + _GWS(globalDATAnames)[i] = nullptr; + _GWS(globalDATAHandles)[i] = nullptr; + _GWS(globalDATAoffsets)[i] = -1; } // CELS tables - if ((_G(globalCELSnames) = (char **)mem_alloc(sizeof(char *) * 256, "CELS resource table")) == nullptr) { + if ((_GWS(globalCELSnames) = (char **)mem_alloc(sizeof(char *) * 256, "CELS resource table")) == nullptr) { return false; } - if ((_G(globalCELSHandles) = (Handle *)mem_alloc(sizeof(Handle) * 256, "CELS Handles table")) == nullptr) { + if ((_GWS(globalCELSHandles) = (MemHandle *)mem_alloc(sizeof(Handle) * 256, "CELS Handles table")) == nullptr) { return false; } - if ((_G(globalCELSoffsets) = (int32 *)mem_alloc(sizeof(int32 *) * 256, "CELS offsets table")) == nullptr) { + if ((_GWS(globalCELSoffsets) = (int32 *)mem_alloc(sizeof(int32 *) * 256, "CELS offsets table")) == nullptr) { return false; } - if ((_G(globalCELSPaloffsets) = (int32 *)mem_alloc(sizeof(int32 *) * 256, "CELS pal offsets table")) == nullptr) { + if ((_GWS(globalCELSPaloffsets) = (int32 *)mem_alloc(sizeof(int32 *) * 256, "CELS pal offsets table")) == nullptr) { return false; } for (i = 0; i < 256; i++) { - _G(globalCELSnames)[i] = nullptr; - _G(globalCELSHandles)[i] = nullptr; - _G(globalCELSoffsets)[i] = -1; - _G(globalCELSPaloffsets)[i] = -1; + _GWS(globalCELSnames)[i] = nullptr; + _GWS(globalCELSHandles)[i] = nullptr; + _GWS(globalCELSoffsets)[i] = -1; + _GWS(globalCELSPaloffsets)[i] = -1; } - //set the global to indicate the loader is active - _G(wsloaderInitialized) = true; + // Set the global to indicate the loader is active + _GWS(wsloaderInitialized) = true; return true; } @@ -119,7 +144,7 @@ bool InitWSAssets() { bool ClearWSAssets(uint32 assetType, int32 minHash, int32 maxHash) { int32 i; - if (!_G(wsloaderInitialized)) { + if (!_GWS(wsloaderInitialized)) { return false; } @@ -131,15 +156,15 @@ bool ClearWSAssets(uint32 assetType, int32 minHash, int32 maxHash) { switch (assetType) { case _WS_ASSET_MACH: - //clear the machines table for entries [minHash, maxHash] + // Clear the machines table for entries [minHash, maxHash] for (i = minHash; i <= maxHash; i++) { TerminateMachinesByHash(i); - if (_G(globalMACHnames)[i]) { - rtoss(_G(globalMACHnames)[i]); - mem_free(_G(globalMACHnames)[i]); - _G(globalMACHnames)[i] = nullptr; - _G(globalMACHHandles)[i] = nullptr; - _G(globalMACHoffsets)[i] = -1; + if (_GWS(globalMACHnames)[i]) { + rtoss(_GWS(globalMACHnames)[i]); + mem_free(_GWS(globalMACHnames)[i]); + _GWS(globalMACHnames)[i] = nullptr; + _GWS(globalMACHHandles)[i] = nullptr; + _GWS(globalMACHoffsets)[i] = -1; } } break; @@ -147,39 +172,39 @@ bool ClearWSAssets(uint32 assetType, int32 minHash, int32 maxHash) { case _WS_ASSET_SEQU: // Clear the sequences table for entries [minHash, maxHash] for (i = minHash; i <= maxHash; i++) { - if (_G(globalSEQUnames)[i]) { - rtoss(_G(globalSEQUnames)[i]); - mem_free(_G(globalSEQUnames)[i]); - _G(globalSEQUnames)[i] = nullptr; - _G(globalSEQUHandles)[i] = nullptr; - _G(globalSEQUoffsets)[i] = -1; + if (_GWS(globalSEQUnames)[i]) { + rtoss(_GWS(globalSEQUnames)[i]); + mem_free(_GWS(globalSEQUnames)[i]); + _GWS(globalSEQUnames)[i] = nullptr; + _GWS(globalSEQUHandles)[i] = nullptr; + _GWS(globalSEQUoffsets)[i] = -1; } } break; case _WS_ASSET_DATA: - //clear the data table for entries [minHash, maxHash] + // Clear the data table for entries [minHash, maxHash] for (i = minHash; i <= maxHash; i++) { - if (_G(globalDATAnames)[i]) { - rtoss(_G(globalDATAnames)[i]); - mem_free(_G(globalDATAnames)[i]); - _G(globalDATAnames)[i] = nullptr; - _G(globalDATAHandles)[i] = nullptr; - _G(globalDATAoffsets)[i] = -1; + if (_GWS(globalDATAnames)[i]) { + rtoss(_GWS(globalDATAnames)[i]); + mem_free(_GWS(globalDATAnames)[i]); + _GWS(globalDATAnames)[i] = nullptr; + _GWS(globalDATAHandles)[i] = nullptr; + _GWS(globalDATAoffsets)[i] = -1; } } break; case _WS_ASSET_CELS: - //clear the cels tables for entries [minHash, maxHash] + // Clear the cels tables for entries [minHash, maxHash] for (i = minHash; i <= maxHash; i++) { - if (_G(globalCELSnames)[i]) { - rtoss(_G(globalCELSnames)[i]); - mem_free(_G(globalCELSnames)[i]); - _G(globalCELSnames)[i] = nullptr; - _G(globalCELSHandles)[i] = nullptr; - _G(globalCELSoffsets)[i] = -1; - _G(globalCELSPaloffsets)[i] = -1; + if (_GWS(globalCELSnames)[i]) { + rtoss(_GWS(globalCELSnames)[i]); + mem_free(_GWS(globalCELSnames)[i]); + _GWS(globalCELSnames)[i] = nullptr; + _GWS(globalCELSHandles)[i] = nullptr; + _GWS(globalCELSoffsets)[i] = -1; + _GWS(globalCELSPaloffsets)[i] = -1; } } break; @@ -191,7 +216,7 @@ bool ClearWSAssets(uint32 assetType, int32 minHash, int32 maxHash) { } void ShutdownWSAssets(void) { - if (!_G(wsloaderInitialized)) + if (!_GWS(wsloaderInitialized)) return; // For each asset type, clear the entire table @@ -201,22 +226,22 @@ void ShutdownWSAssets(void) { ClearWSAssets(_WS_ASSET_DATA, 0, MAX_ASSET_HASH); // Deallocate all tables - if (_G(globalMACHnames)) mem_free(_G(globalMACHnames)); - if (_G(globalSEQUnames)) mem_free(_G(globalSEQUnames)); - if (_G(globalDATAnames)) mem_free(_G(globalDATAnames)); - if (_G(globalCELSnames)) mem_free(_G(globalCELSnames)); + if (_GWS(globalMACHnames)) mem_free(_GWS(globalMACHnames)); + if (_GWS(globalSEQUnames)) mem_free(_GWS(globalSEQUnames)); + if (_GWS(globalDATAnames)) mem_free(_GWS(globalDATAnames)); + if (_GWS(globalCELSnames)) mem_free(_GWS(globalCELSnames)); - if (_G(globalMACHHandles)) mem_free(_G(globalMACHHandles)); - if (_G(globalMACHoffsets)) mem_free(_G(globalMACHoffsets)); - if (_G(globalSEQUHandles)) mem_free(_G(globalSEQUHandles)); - if (_G(globalSEQUoffsets)) mem_free(_G(globalSEQUoffsets)); - if (_G(globalDATAHandles)) mem_free(_G(globalDATAHandles)); - if (_G(globalDATAoffsets)) mem_free(_G(globalDATAoffsets)); - if (_G(globalCELSHandles)) mem_free(_G(globalCELSHandles)); - if (_G(globalCELSoffsets)) mem_free(_G(globalCELSoffsets)); - if (_G(globalCELSPaloffsets)) mem_free(_G(globalCELSPaloffsets)); + if (_GWS(globalMACHHandles)) mem_free(_GWS(globalMACHHandles)); + if (_GWS(globalMACHoffsets)) mem_free(_GWS(globalMACHoffsets)); + if (_GWS(globalSEQUHandles)) mem_free(_GWS(globalSEQUHandles)); + if (_GWS(globalSEQUoffsets)) mem_free(_GWS(globalSEQUoffsets)); + if (_GWS(globalDATAHandles)) mem_free(_GWS(globalDATAHandles)); + if (_GWS(globalDATAoffsets)) mem_free(_GWS(globalDATAoffsets)); + if (_GWS(globalCELSHandles)) mem_free(_GWS(globalCELSHandles)); + if (_GWS(globalCELSoffsets)) mem_free(_GWS(globalCELSoffsets)); + if (_GWS(globalCELSPaloffsets)) mem_free(_GWS(globalCELSPaloffsets)); - _G(wsloaderInitialized) = false; + _GWS(wsloaderInitialized) = false; } bool LoadWSAssets(const char *wsAssetName, RGB8 *myPalette) { @@ -230,7 +255,7 @@ bool LoadWSAssets(const char *wsAssetName, RGB8 *myPalette) { int32 assetSize; // Check that the loader has been initialized - if (!_G(wsloaderInitialized)) { + if (!_GWS(wsloaderInitialized)) { error_show(FL, 'WSLI'); } @@ -285,9 +310,9 @@ bool LoadWSAssets(const char *wsAssetName, RGB8 *myPalette) { ClearWSAssets(_WS_ASSET_MACH, *chunkHash, *chunkHash); // Store the resource name, and the offset into the resource block - _G(globalMACHnames)[*chunkHash] = mem_strdup(wsAssetName); - _G(globalMACHHandles)[*chunkHash] = workHandle; - _G(globalMACHoffsets)[*chunkHash] = parseAssetPtr - mainAssetPtr; + _GWS(globalMACHnames)[*chunkHash] = mem_strdup(wsAssetName); + _GWS(globalMACHHandles)[*chunkHash] = workHandle; + _GWS(globalMACHoffsets)[*chunkHash] = parseAssetPtr - mainAssetPtr; // Check that the assetblocksize is big enough that the chunk body was read in... if ((endOfAssetBlock - parseAssetPtr) < (int)(*chunkSize - 12)) { @@ -324,9 +349,9 @@ bool LoadWSAssets(const char *wsAssetName, RGB8 *myPalette) { ClearWSAssets(_WS_ASSET_SEQU, *chunkHash, *chunkHash); // Store the resource name, and the offset into the resource block - _G(globalSEQUnames)[*chunkHash] = mem_strdup(wsAssetName); - _G(globalSEQUHandles)[*chunkHash] = workHandle; - _G(globalSEQUoffsets)[*chunkHash] = (int32)parseAssetPtr - (int32)mainAssetPtr; + _GWS(globalSEQUnames)[*chunkHash] = mem_strdup(wsAssetName); + _GWS(globalSEQUHandles)[*chunkHash] = workHandle; + _GWS(globalSEQUoffsets)[*chunkHash] = (int32)parseAssetPtr - (int32)mainAssetPtr; // Check that the assetblocksize is big enough that the chunk body was read in... if ((endOfAssetBlock - parseAssetPtr) < (int)(*chunkSize - 12)) { @@ -363,9 +388,9 @@ bool LoadWSAssets(const char *wsAssetName, RGB8 *myPalette) { ClearWSAssets(_WS_ASSET_DATA, *chunkHash, *chunkHash); // Store the resource name, and the offset into the resource block - _G(globalDATAnames)[*chunkHash] = mem_strdup(wsAssetName); - _G(globalDATAHandles)[*chunkHash] = workHandle; - _G(globalDATAoffsets)[*chunkHash] = (int32)parseAssetPtr - (int32)mainAssetPtr; + _GWS(globalDATAnames)[*chunkHash] = mem_strdup(wsAssetName); + _GWS(globalDATAHandles)[*chunkHash] = workHandle; + _GWS(globalDATAoffsets)[*chunkHash] = (int32)parseAssetPtr - (int32)mainAssetPtr; // Check that the assetblocksize is big enough that the chunk body was read in... if ((endOfAssetBlock - parseAssetPtr) < (int)(*chunkSize - 12)) { @@ -401,7 +426,7 @@ bool LoadWSAssets(const char *wsAssetName, RGB8 *myPalette) { ClearWSAssets(_WS_ASSET_CELS, *chunkHash, *chunkHash); // Store the resource name - _G(globalCELSnames)[*chunkHash] = mem_strdup(wsAssetName); + _GWS(globalCELSnames)[*chunkHash] = mem_strdup(wsAssetName); // Process the SS from the stream file if (ProcessCELS(wsAssetName, &parseAssetPtr, mainAssetPtr, endOfAssetBlock, &celsPtr, &palPtr, myPalette) < 0) { @@ -410,16 +435,16 @@ bool LoadWSAssets(const char *wsAssetName, RGB8 *myPalette) { // At this point, celsPtr points to the beginning of the cels data, palPtr to the pal data // Store the Handle, and calculate the offsets - _G(globalCELSHandles)[*chunkHash] = workHandle; + _GWS(globalCELSHandles)[*chunkHash] = workHandle; if (celsPtr) { - _G(globalCELSoffsets)[*chunkHash] = (int32)celsPtr - (int32)mainAssetPtr; + _GWS(globalCELSoffsets)[*chunkHash] = (int32)celsPtr - (int32)mainAssetPtr; } else { - _G(globalCELSoffsets)[*chunkHash] = -1; + _GWS(globalCELSoffsets)[*chunkHash] = -1; } if (palPtr) { - _G(globalCELSPaloffsets)[*chunkHash] = (int32)palPtr - (int32)mainAssetPtr; + _GWS(globalCELSPaloffsets)[*chunkHash] = (int32)palPtr - (int32)mainAssetPtr; } else { - _G(globalCELSPaloffsets)[*chunkHash] = -1; + _GWS(globalCELSPaloffsets)[*chunkHash] = -1; } break; @@ -624,7 +649,7 @@ int32 AddWSAssetCELS(const char *wsAssetName, int32 hash, RGB8 *myPalette) { int32 emptySlot, i, assetSize, *celsPtr, *palPtr; // Check that the loader has been initialized - if (!_G(wsloaderInitialized)) { + if (!_GWS(wsloaderInitialized)) { error_show(FL, 'WSLI', "Asset Name: %s", wsAssetName); } @@ -635,8 +660,8 @@ int32 AddWSAssetCELS(const char *wsAssetName, int32 hash, RGB8 *myPalette) { // Search through the SS names table for (i = 0; i <= MAX_ASSET_HASH; i++) { // See if there is something loaded in this slot - if (_G(globalCELSnames)[i]) { - if (!strcmp(_G(globalCELSnames)[i], wsAssetName)) { + if (_GWS(globalCELSnames)[i]) { + if (!strcmp(_GWS(globalCELSnames)[i], wsAssetName)) { break; } } else if (emptySlot < 0) { @@ -652,12 +677,12 @@ int32 AddWSAssetCELS(const char *wsAssetName, int32 hash, RGB8 *myPalette) { } // Check to see if the SS is already loaded in the given hash slot - if (_G(globalCELSnames)[hash] && (!strcmp(_G(globalCELSnames)[hash], wsAssetName))) { - if (_G(globalCELSPaloffsets)[hash] >= 0) { + if (_GWS(globalCELSnames)[hash] && (!strcmp(_GWS(globalCELSnames)[hash], wsAssetName))) { + if (_GWS(globalCELSPaloffsets)[hash] >= 0) { // Get the pointer to the pal data #ifdef TODO - workHandle = _G(globalCELSHandles)[hash]; - palPtr = (int32 *)((int32)*workHandle + _G(globalCELSPaloffsets)[hash]); + workHandle = _GWS(globalCELSHandles)[hash]; + palPtr = (int32 *)((int32)*workHandle + _GWS(globalCELSPaloffsets)[hash]); #else error("TODO: Figure out dereferencing"); #endif @@ -680,7 +705,7 @@ int32 AddWSAssetCELS(const char *wsAssetName, int32 hash, RGB8 *myPalette) { // we found an empty slot to load the SS into if ((i > MAX_ASSET_HASH) && (emptySlot >= 0)) { - if ((workHandle = rget(wsAssetName, &assetSize)) == NULL) { + if ((workHandle = rget(wsAssetName, &assetSize)) == nullptr) { error_show(FL, 'FNF!', (char *)wsAssetName); } @@ -694,7 +719,7 @@ int32 AddWSAssetCELS(const char *wsAssetName, int32 hash, RGB8 *myPalette) { ClearWSAssets(_WS_ASSET_CELS, emptySlot, emptySlot); // Store the resource name - _G(globalCELSnames)[emptySlot] = mem_strdup(wsAssetName); + _GWS(globalCELSnames)[emptySlot] = mem_strdup(wsAssetName); // Process the SS from the stream file if (ProcessCELS(wsAssetName, &parseAssetPtr, mainAssetPtr, endOfAssetBlock, &celsPtr, &palPtr, myPalette) < 0) { @@ -703,16 +728,16 @@ int32 AddWSAssetCELS(const char *wsAssetName, int32 hash, RGB8 *myPalette) { // At this point, celsPtr points to the beginning of the cels data, palPtr to the pal data // Store the Handle, and calculate the offsets - _G(globalCELSHandles)[emptySlot] = workHandle; + _GWS(globalCELSHandles)[emptySlot] = workHandle; if (celsPtr) { - _G(globalCELSoffsets)[emptySlot] = (int32)celsPtr - (int32)mainAssetPtr; + _GWS(globalCELSoffsets)[emptySlot] = (int32)celsPtr - (int32)mainAssetPtr; } else { - _G(globalCELSoffsets)[emptySlot] = -1; + _GWS(globalCELSoffsets)[emptySlot] = -1; } if (palPtr) { - _G(globalCELSPaloffsets)[emptySlot] = (int32)palPtr - (int32)mainAssetPtr; + _GWS(globalCELSPaloffsets)[emptySlot] = (int32)palPtr - (int32)mainAssetPtr; } else { - _G(globalCELSPaloffsets)[emptySlot] = -1; + _GWS(globalCELSPaloffsets)[emptySlot] = -1; } // Unlock the handle @@ -721,12 +746,12 @@ int32 AddWSAssetCELS(const char *wsAssetName, int32 hash, RGB8 *myPalette) { return emptySlot; } else if (i < MAX_ASSET_HASH) { // Else if we found the SS already loaded - if (_G(globalCELSPaloffsets)[i] >= 0) { + if (_GWS(globalCELSPaloffsets)[i] >= 0) { // Get the pointer to the pal data #ifdef TODO - workHandle = _G(globalCELSHandles)[i]; + workHandle = _GWS(globalCELSHandles)[i]; HLock(workHandle); - palPtr = (int32 *)((int32)*workHandle + _G(globalCELSPaloffsets)[i]); + palPtr = (int32 *)((int32)*workHandle + _GWS(globalCELSPaloffsets)[i]); #else error("TODO: Figure out dereferencing"); #endif @@ -764,7 +789,7 @@ static int32 ProcessCELS(const char * /*assetName*/, char **parseAssetPtr, char uint32 *tempPtr, *celsSize, *data, *dataPtr, *offsetPtr, i, j, *header, *format; bool byteSwap; - if (!_G(wsloaderInitialized)) + if (!_GWS(wsloaderInitialized)) return -1; *dataOffset = nullptr; @@ -970,4 +995,875 @@ static void RestoreSSPaletteInfo(RGB8 *myPalette, int32 *palPtr) { } } +M4sprite *GetWSAssetSprite(char *spriteName, uint32 hash, uint32 index, M4sprite *mySprite, bool *streamSeries) { + MemHandle workHandle; + int32 i; + + // Ensure wsloader has been initialized + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return nullptr; + } + + // If spriteName is specified, we search by name, otherwise hash is assumed to be correct + if (spriteName) { + if (!_GWS(globalCELSnames)) return nullptr; + for (i = 0; i <= MAX_ASSET_HASH; i++) { + if (!strcmp(spriteName, _GWS(globalCELSnames)[i])) { + break; + } + } + hash = i; + } + + // Check for valid index, and sprite loaded at that index + if (hash > MAX_ASSET_HASH) { + if (spriteName) { + ws_LogErrorMsg(FL, "Sprite series is not in memory: %s.", spriteName); + } else { + ws_LogErrorMsg(FL, "Series number out of range: requested num: %ld.", hash); + } + } + + // Get the resource handle + workHandle = _GWS(globalCELSHandles)[hash]; + + // Create the sprite + mySprite = CreateSprite(workHandle, _GWS(globalCELSoffsets)[hash], index, mySprite, streamSeries); + + // Check the sprite + if (!mySprite) { + ws_LogErrorMsg(FL, "Series: %s, Hash: %ld, index: %ld", _GWS(globalCELSnames)[hash], hash, index); + } + + return mySprite; +} + + +int32 LoadSpriteSeries(const char *assetName, Handle *seriesHandle, int32 *celsOffset, int32 *palOffset, RGB8 *myPalette) { + MemHandle workHandle; + int32 celsSize, *celsPtr, *palPtr; + char *mainAssetPtr, *endOfAssetBlock, *parseAssetPtr; + int32 assetSize; + + //This loads a sprite series into the provided vars, rather than the WS tables. + //The WS loader is not involved with this procedure. + + // Load in the sprite series + if ((workHandle = rget(assetName, &assetSize)) == nullptr) + error_show(FL, 'FNF!', "Sprite series: %s", assetName); + + HLock(workHandle); + + mainAssetPtr = (char *)*workHandle; + endOfAssetBlock = (char *)((uint32)mainAssetPtr + (uint32)assetSize); + parseAssetPtr = mainAssetPtr; + + // Process the SS from the stream file + if ((celsSize = ProcessCELS(assetName, &parseAssetPtr, mainAssetPtr, endOfAssetBlock, &celsPtr, &palPtr, myPalette)) < 0) { + error_show(FL, 'WSLP', "series: %s", assetName); + } + + // Store the handle and offsets + *seriesHandle = workHandle; + *celsOffset = (int32)celsPtr - (int32)mainAssetPtr; + *palOffset = (int32)palPtr - (int32)mainAssetPtr; + + HUnLock(workHandle); + + return celsSize; +} + +int32 LoadSpriteSeriesDirect(const char *assetName, Handle *seriesHandle, int32 *celsOffset, int32 *palOffset, RGB8 *myPalette) { + MemHandle workHandle; + Common::File f; + int32 celsSize, *celsPtr, *palPtr; + char *mainAssetPtr, *endOfAssetBlock, *parseAssetPtr; + uint32 assetSize; + + // This loads a sprite series into the provided vars, rather than the WS tables. + // The WS loader is not involved with this procedure. + + // First open the file + if (!f.open(assetName)) + return -1; + + // Get the size + assetSize = f.size(); + + // Create a handle big enough to hold the contents of the file + if ((workHandle = NewHandle(assetSize, "ss file")) == nullptr) { + f.close(); + return -1; + } + + // Lock the handle and read the contents of the file intoit + HLock(workHandle); + mainAssetPtr = (char *)*workHandle; + if (f.read(mainAssetPtr, assetSize) < assetSize) { + f.close(); + return -1; + } + + // Close the file + f.close(); + + // Set up some pointers + endOfAssetBlock = (char *)((uint32)mainAssetPtr + (uint32)assetSize); + parseAssetPtr = mainAssetPtr; + + // Process the SS from the stream file + if ((celsSize = ProcessCELS(assetName, &parseAssetPtr, mainAssetPtr, endOfAssetBlock, &celsPtr, &palPtr, myPalette)) < 0) { + error_show(FL, 'WSLP', "series: %s", assetName); + } + + // Store the handle and offsets + *seriesHandle = workHandle; + *celsOffset = (int32)celsPtr - (int32)mainAssetPtr; + *palOffset = (int32)palPtr - (int32)mainAssetPtr; + HUnLock(workHandle); + + return celsSize; +} + +CCB *GetWSAssetCEL(uint32 hash, uint32 index, CCB *myCCB) { + bool streamSeries; + M4sprite *mySprite; + + // Ensure the WS loader has been initialized. + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return false; + } + + //If a memory location to store the CCB has not been provided... + if (!myCCB) { + // Create the CCB struct + if ((myCCB = (CCB *)mem_alloc(sizeof(CCB), "CCB")) == nullptr) { + ws_LogErrorMsg(FL, "Out of memory - mem requested: %ld bytes", sizeof(CCB)); + return nullptr; + } + + // Create the CCB current location and new location rectangles + if ((myCCB->currLocation = (M4Rect *)mem_alloc(sizeof(M4Rect), "M4Rect")) == nullptr) { + ws_LogErrorMsg(FL, "Out of memory - mem requested: %ld bytes", sizeof(M4Rect)); + return nullptr; + } + myCCB->currLocation->x1 = 0; + myCCB->currLocation->y1 = 0; + myCCB->currLocation->x2 = 0; + myCCB->currLocation->y2 = 0; + if ((myCCB->newLocation = (M4Rect *)mem_alloc(sizeof(M4Rect), "M4Rect")) == nullptr) { + ws_LogErrorMsg(FL, "Out of memory - mem requested: %ld bytes", sizeof(M4Rect)); + return nullptr; + } + myCCB->maxArea = nullptr; + myCCB->source = nullptr; + } + + //The source for a CCB is a sprite. create the sprite from the WS tables hash, index + mySprite = myCCB->source; + if ((mySprite = GetWSAssetSprite(nullptr, hash, index, mySprite, &streamSeries)) == nullptr) { + // Term messages for whatever went wrong are printed from within GetWSAssetSprite() + return nullptr; + } + myCCB->source = mySprite; + if (streamSeries) { + myCCB->flags |= CCB_STREAM; + } + + //Initialize the CCB and return + myCCB->newLocation->x1 = 0; + myCCB->newLocation->y1 = 0; + myCCB->newLocation->x2 = 0; + myCCB->newLocation->y2 = 0; + myCCB->scaleX = 0; + myCCB->scaleY = 0; + myCCB->seriesName = _GWS(globalCELSnames)[hash]; + return myCCB; +} + +int32 GetWSAssetCELCount(uint32 hash) { + uint32 *celsPtr; + + // Ensure the WS loader has been initialized. + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return -1; + } + + // Verify the hash is valid, and a SS for that hash has been loaded + if (hash > MAX_ASSET_HASH) { + ws_LogErrorMsg(FL, "Series number out of range: requested num: %ld", hash); + return -1; + } + + // Make sure the series is still in memory + if ((!_GWS(globalCELSHandles)[hash]) || (!*_GWS(globalCELSHandles)[hash])) { + ws_LogErrorMsg(FL, "Series not in memory series num: %ld", hash); + return -1; + } + + // Find and return the number of sprites in the SS + celsPtr = (uint32 *)((uint32) * (_GWS(globalCELSHandles)[hash]) + (uint32)(_GWS(globalCELSoffsets)[hash])); + return celsPtr[CELS_COUNT]; +} + + +int32 GetWSAssetCELFrameRate(uint32 hash) { + uint32 *celsPtr; // *mainAssetPtr, +// int32 assetSize; + + // Ensure the WS loader has been initialized. + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return -1; + } + + // Verify the hash is valid, and a SS for that hash has been loaded + if (hash > MAX_ASSET_HASH) { + ws_LogErrorMsg(FL, "Series number out of range: requested num: %ld", hash); + return -1; + } + + // Make sure the series is still in memory + if ((!_GWS(globalCELSHandles)[hash]) || (!*_GWS(globalCELSHandles)[hash])) { + ws_LogErrorMsg(FL, "Series not in memory series num: %ld", hash); + return -1; + } + + // Find and return the frame rate for the SS + celsPtr = (uint32 *)((uint32) * (_GWS(globalCELSHandles)[hash]) + (uint32)(_GWS(globalCELSoffsets)[hash])); + return celsPtr[CELS_FRAME_RATE]; +} + + +int32 GetWSAssetCELPixSpeed(uint32 hash) { + uint32 *celsPtr; // *mainAssetPtr, +// int32 assetSize; + + // Ensure the WS loader has been initialized. + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return -1; + } + + // Verify the hash is valid, and a SS for that hash has been loaded + if (hash > MAX_ASSET_HASH) { + ws_LogErrorMsg(FL, "Series number out of range: requested num: %ld", hash); + return -1; + } + + // Make sure the series is still in memory + if ((!_GWS(globalCELSHandles)[hash]) || (!*_GWS(globalCELSHandles)[hash])) { + ws_LogErrorMsg(FL, "Series not in memory series num: %ld", hash); + return -1; + } + + // Find and return the pix speed for the SS + celsPtr = (uint32 *)((uint32) * (_GWS(globalCELSHandles)[hash]) + (uint32)(_GWS(globalCELSoffsets)[hash])); + return celsPtr[CELS_PIX_SPEED]; +} + + +int32 ws_get_sprite_width(uint32 hash, int32 index) { + uint32 *celsPtr, *offsets, *data, *myCelSource; // *mainAssetPtr, + int32 numCels; // AssetSize, + + // Ensure the WS loader has been initialized. + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return -1; + } + + // Verify the hash is valid, and a SS for that hash has been loaded + if (hash > MAX_ASSET_HASH) { + ws_LogErrorMsg(FL, "Series number out of range: requested num: %ld", hash); + return -1; + } + + // Make sure the series is still in memory + if ((!_GWS(globalCELSHandles)[hash]) || (!*_GWS(globalCELSHandles)[hash])) { + ws_LogErrorMsg(FL, "Series not in memory series num: %ld", hash); + return -1; + } + + // Find the source for the SS + celsPtr = (uint32 *)((uint32) * (_GWS(globalCELSHandles)[hash]) + (uint32)(_GWS(globalCELSoffsets)[hash])); + + // Check that the index into the series requested is within a valid range + numCels = celsPtr[CELS_COUNT]; + if (index >= numCels) { + ws_LogErrorMsg(FL, "Sprite index out of range - max index: %ld, requested index: %ld", numCels - 1, index); + return -1; + } + + // Find the offset table in the SS header + offsets = &celsPtr[CELS_OFFSETS]; + + // Find the beginning of the data for all sprites in the SS + data = &celsPtr[CELS_OFFSETS + numCels]; + + // Find the sprite data for the specific sprite in the series + myCelSource = (uint32 *)((uint32)data + offsets[index]); + + return (int32)myCelSource[CELS_W]; +} + + +int32 ws_get_sprite_height(uint32 hash, int32 index) { + uint32 *celsPtr, *offsets, *data, *myCelSource; // *mainAssetPtr, + int32 numCels; // assetSize, + + // Ensure the WS loader has been initialized. + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return -1; + } + + // Verify the hash is valid, and a SS for that hash has been loaded + if (hash > MAX_ASSET_HASH) { + ws_LogErrorMsg(FL, "Series number out of range: requested num: %ld", hash); + return -1; + } + + // Make sure the series is still in memory + if ((!_GWS(globalCELSHandles)[hash]) || (!*_GWS(globalCELSHandles)[hash])) { + ws_LogErrorMsg(FL, "Series not in memory series num: %ld", hash); + return -1; + } + + // Find the source for the SS + celsPtr = (uint32 *)((uint32) * (_GWS(globalCELSHandles)[hash]) + (uint32)(_GWS(globalCELSoffsets)[hash])); + + // Check that the index into the series requested is within a valid range + numCels = celsPtr[CELS_COUNT]; + if (index >= numCels) { + ws_LogErrorMsg(FL, "Sprite index out of range - max index: %ld, requested index: %ld", numCels - 1, index); + return -1; + } + + // Find the offset table in the SS header + offsets = &celsPtr[CELS_OFFSETS]; + + // Find the beginning of the data for all sprites in the SS + data = &celsPtr[CELS_OFFSETS + numCels]; + + // Find the sprite data for the specific sprite in the series + myCelSource = (uint32 *)((uint32)data + offsets[index]); + + return (int32)myCelSource[CELS_H]; +} + +MemHandle ws_GetSEQU(uint32 hash, int32 *numLocalVars, int32 *offset) { + uint32 *sequPtr; + + // Ensure the WS loader has been initialized. + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return nullptr; + } + + // Verify the hash is valid, and a SEQU for that hash has been loaded + if (hash > MAX_ASSET_HASH) { + ws_LogErrorMsg(FL, "SEQU number out of range: requested num: %ld", hash); + return nullptr; + } + + // Make sure the SEQU is still in memory + if ((!_GWS(globalSEQUHandles)[hash]) || (!*_GWS(globalSEQUHandles)[hash])) { + ws_LogErrorMsg(FL, "SEQU not in memory: sequence num: %ld", hash); + return nullptr; + } + + // Find the sequence chunk + sequPtr = (uint32 *)((uint32)*(_GWS(globalSEQUHandles)[hash]) + (uint32)(_GWS(globalSEQUoffsets)[hash])); + + // Return the offset into the resource chunk, and the number of local vars used by the sequence + *offset = (int32)(&sequPtr[SEQU_SEQU_START]) - (int32) * (_GWS(globalSEQUHandles)[hash]); + *numLocalVars = sequPtr[SEQU_NUM_VARS]; + + // Return the resource handle + return _GWS(globalSEQUHandles)[hash]; +} + + +MemHandle ws_GetMACH(uint32 hash, int32 *numStates, int32 *stateTableOffset, int32 *machInstrOffset) { + uint32 *machPtr; // *mainAssetPtr, + + // Ensure the WS loader has been initialized. + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return nullptr; + } + + // Verify the hash is valid, and a MACH for that hash has been loaded + if (hash > MAX_ASSET_HASH) { + ws_LogErrorMsg(FL, "MACH number out of range: requested num: %ld", hash); + return nullptr; + } + + // Make sure the MACH is still in memory + if ((!_GWS(globalMACHHandles)[hash]) || (!*_GWS(globalMACHHandles)[hash])) { + ws_LogErrorMsg(FL, "MACH not in memory: machine num: %ld", hash); + return nullptr; + } + + // Lock the handle + HLock(_GWS(globalMACHHandles)[hash]); + + // Find the machine chunk + machPtr = (uint32 *)((uint32) * (_GWS(globalMACHHandles)[hash]) + (uint32)(_GWS(globalMACHoffsets)[hash])); + + // Set the number of states, the state offset table, the start of the mach instructions + *numStates = (int32)machPtr[MACH_NUM_STATES]; + *stateTableOffset = (int32)(&machPtr[MACH_OFFSETS]) - (int32)(*_GWS(globalMACHHandles)[hash]); + *machInstrOffset = (int32)((int32)machPtr + ((*numStates + 1) << 2)) - (int32)(*_GWS(globalMACHHandles)[hash]); + + //unlock and return the handle + HUnLock(_GWS(globalMACHHandles)[hash]); + return _GWS(globalMACHHandles)[hash]; +} + +MemHandle ws_GetDATA(uint32 hash, uint32 index, int32 *rowOffset) { + uint32 *dataPtr; + + // Ensure the WS loader has been initialized. + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return nullptr; + } + + // Verify the hash is valid, and a SS for that hash has been loaded + if (hash > MAX_ASSET_HASH) { + ws_LogErrorMsg(FL, "DATA number out of range: requested num: %ld", hash); + return nullptr; + } + + // Make sure the DATA is still in memory + if ((!_GWS(globalDATAHandles)[hash]) || (!*_GWS(globalDATAHandles)[hash])) { + ws_LogErrorMsg(FL, "DATA not in memory: data num: %ld", hash); + return nullptr; + } + + // Find the data block chunk + dataPtr = (uint32 *)((uint32) * (_GWS(globalDATAHandles)[hash]) + (uint32)(_GWS(globalDATAoffsets)[hash])); + + // Verify the row index of the data block is valid + if (index > dataPtr[DATA_REC_COUNT]) { + term_message("File: %s, line: %ld, ws_GetDATA() failed:", FL); + term_message("Data block num: %ld", hash); + term_message("Data row out of range - max row index: %ld, requested row index: %ld", dataPtr[DATA_REC_COUNT], index); + return nullptr; + } + + *rowOffset = (int32)((uint32)(&dataPtr[DATA_REC_START]) + ((index * dataPtr[DATA_REC_SIZE]) << 2) - + (int32)(*_GWS(globalDATAHandles)[hash])); + // Return the data handle + return _GWS(globalDATAHandles)[hash]; +} + + +int32 ws_GetDATACount(uint32 hash) { + uint32 *dataPtr; // *mainAssetPtr, +// int32 assetSize; + + // Ensure the WS loader has been initialized. + if (!_GWS(wsloaderInitialized)) { + ws_LogErrorMsg(FL, "WS loader has not been initialized."); + return -1; + } + + // Verify the hash is valid, and a SS for that hash has been loaded + if (hash > MAX_ASSET_HASH) { + ws_LogErrorMsg(FL, "DATA number out of range: requested num: %ld", hash); + return -1; + } + + // Make sure the DATA is still in memory + if ((!_GWS(globalDATAHandles)[hash]) || (!*_GWS(globalDATAHandles)[hash])) { + ws_LogErrorMsg(FL, "DATA not in memory: data num: %ld", hash); + return -1; + } + + // Find the data block chunk + dataPtr = (uint32 *)((uint32) * (_GWS(globalDATAHandles)[hash]) + (uint32)(_GWS(globalDATAoffsets)[hash])); + + // Return the number of rows in the data block + return dataPtr[DATA_REC_COUNT]; +} + + +static int32 GetSSHeaderInfo(StreamFile *stream, uint32 **data, RGB8 *myPalette) { + uint32 celsType, celsSize, numColors, *myColors; + uint32 *tempPtr, i, j, header, format; + int32 numCels, dataOffset; + bool byteSwap; + void *handlebuffer; + + if (!stream) { + ws_LogErrorMsg(FL, "nullptr FILE POINTER given."); + return -1; + } + + // Read in the series header and the format number + handlebuffer = &header; + if (!(*stream).read((Handle)&handlebuffer, 4)) { + ws_LogErrorMsg(FL, "Unable to read series header."); + return -1; + } + handlebuffer = &format; + if (!(*stream).read((Handle)&handlebuffer, 4)) { + ws_LogErrorMsg(FL, "Unable to read series format."); + return -1; + } + + // Make sure the header is "M4SS", and that the format is not antique + if (header == HEAD_SS4M) { + format = SWAP_INT32(format); + } else if (header != HEAD_M4SS) { + ws_LogErrorMsg(FL, "Series is not a valid M4SS series."); + return -1; + } + if (format < SS_FORMAT) { + ws_LogErrorMsg(FL, "Format is antique and cannot be read - rebuild series."); + return -1; + } + + // Read in the SS chunk type - either PAL or SS info + handlebuffer = &celsType; + if (!(*stream).read((Handle)&handlebuffer, 4)) { + ws_LogErrorMsg(FL, "Unable to read series chunk type."); + return -1; + } + + if ((celsType == CELS__PAL) || (celsType == CELS_LAP_)) { + + //PAL info, read in the size of the PAL chunk + handlebuffer = &celsSize; + if (!(*stream).read((Handle)&handlebuffer, 4)) { + ws_LogErrorMsg(FL, "Unable to read series chunk size."); + return -1; + } + + // Now read in the number of colors to be inserted into the PAL + handlebuffer = &numColors; + if (!(*stream).read((Handle)&handlebuffer, 4)) { + ws_LogErrorMsg(FL, "Unable to read number of colors in PAL chunk."); + return -1; + } + + // Make sure the info is in the correct format (swap between Motorola and Intel formats) + if (celsType == CELS_LAP_) { + celsSize = SWAP_INT32(celsSize); + numColors = SWAP_INT32(numColors); + byteSwap = true; + } else { + byteSwap = false; + } + + // If there is at least one color specified in this block + if (numColors > 0) { + if ((myColors = (uint32 *)mem_alloc(celsSize - 12, "ss pal info")) == nullptr) { + ws_LogErrorMsg(FL, "Failed to mem_alloc() %ld bytes.", celsSize - 12); + return -1; + } + + // Read in the color info into a temp buffer + handlebuffer = myColors; + if (!(*stream).read((Handle)&handlebuffer, numColors << 2)) { + ws_LogErrorMsg(FL, "Failed to read in the PAL color info."); + return -1; + } + + // If the chunk is in the wrong format, byte-swap the entire chunk + // note: we do this because we want the data stored in nrgb format + // The data is always read in low byte first, but we need it high byte first + // regardless of the endianness of the machine. + if (byteSwap) { + tempPtr = (uint32 *)&myColors[0]; + for (i = 0; i < numColors; i++) { + *tempPtr++ = SWAP_INT32(*tempPtr); + } + } + + //If we have a place to store the color info + if (myPalette) { + tempPtr = (uint32 *)(&myColors[0]); + for (i = 0; i < numColors; i++) { + j = (*tempPtr & 0xff000000) >> 24; + myPalette[j].r = (*tempPtr & 0x00ff0000) >> 14; + myPalette[j].g = (*tempPtr & 0x0000ff00) >> 6; + myPalette[j].b = (*tempPtr & 0x000000ff) << 2; + tempPtr++; + } + } + + // Turf the temp buffer + mem_free((void *)myColors); + } + + // Read in the next chunk type + handlebuffer = &celsType; + if (!(*stream).read((Handle)&handlebuffer, 4)) { + ws_LogErrorMsg(FL, "Failed to read in series chunk type."); + return -1; + } + } + + // Make sure the chunk type is Sprite Series info + if ((celsType != CELS___SS) && (celsType != CELS_SS__)) { + ws_LogErrorMsg(FL, "Series chunk type is not labelled as SS info."); + return -1; + } + + // Read in the size of the entire chunk + handlebuffer = &celsSize; + if (!(*stream).read((Handle)&handlebuffer, 4)) { + ws_LogErrorMsg(FL, "Failed to read in series chunk size."); + return -1; + } + + // If the chunk is the wrong format, byte-swap (between motorola and intel formats) + if (celsType == CELS_SS__) { + celsSize = SWAP_INT32(celsSize); + } + + // *data contains header + offsets, therefore, we must scan ahead + // and find out how many cels are here... + if (!(*stream).seek_ahead((CELS_COUNT - CELS_SRC_SIZE - 1) << 2)) { + ws_LogErrorMsg(FL, "Failed to seek ahead in the stream."); + return -1; + } + + // Read how many sprites are in the series + handlebuffer = &numCels; + if (!(*stream).read((Handle)&handlebuffer, 4)) { + ws_LogErrorMsg(FL, "Failed to read the number of sprites in the series."); + return -1; + } + + // Again, byte-swap if the chunk is in the wrong format + if (celsType == CELS_SS__) { + numCels = SWAP_INT32(numCels); + } + + // Now, seek backwards to where we left off + if (!(*stream).seek_ahead((CELS_SRC_SIZE - CELS_COUNT) * 4)) { + ws_LogErrorMsg(FL, "Failed to seek backwards in the stream."); + return -1; + } + + // Allocate a block to hold both the series header, and the sprite offset table + if ((*data = (uint32 *)mem_alloc((SS_HEAD_SIZE + numCels) * 4, "ss header")) == nullptr) { + ws_LogErrorMsg(FL, "Failed to mem_alloc() %ld bytes.", (SS_HEAD_SIZE + numCels) << 2); + return -1; + } + + // Read in the series header and the sprite offset table + // Since we already read in celsType and celsSize, SS_HEAD_SIZE-2 + handlebuffer = &((*data)[2]); + if (!(*stream).read((Handle)&handlebuffer, (SS_HEAD_SIZE + numCels - 2) << 2)) { + ws_LogErrorMsg(FL, "Failed to read the series header and the sprite offset table."); + return -1; + } + + // Set the celsType and the celsSize + (*data)[0] = celsType; + (*data)[1] = celsSize; + + // If the chunk is in the wrong format, byte-swap the series header + if (celsType == CELS_SS__) { + tempPtr = &((*data)[2]); + for (i = 0; i < (uint)(SS_HEAD_SIZE + numCels - 2); i++) { + *tempPtr++ = SWAP_INT32(*tempPtr); + } + } + + // Find out how far into the stream we are, and return that value + dataOffset = (*stream).get_pos(); + return dataOffset; +} + +bool ws_OpenSSstream(StreamFile *streamFile, Anim8 *anim8) { + CCB *myCCB; + frac16 *myRegs; + uint32 *celsPtr, *offsets; + int32 ssDataOffset, i, numSprites; + int32 obesest_frame = 0; + uint32 maxFrameSize; + + // Verify the parameters + if ((!streamFile) || (!anim8) || (!anim8->myCCB)) { + ws_LogErrorMsg(FL, "SysFile* streamFile invalid."); + return false; + } + + myCCB = anim8->myCCB; + myRegs = anim8->myRegs; + ssDataOffset = 0; + + // Read in the SS stream header + if ((ssDataOffset = GetSSHeaderInfo(streamFile, &(myCCB->streamSSHeader), &_G(master_palette)[0])) <= 0) { + return false; + } + + // Automatically set some of the sequence registers + celsPtr = myCCB->streamSSHeader; + numSprites = celsPtr[CELS_COUNT]; + myRegs[IDX_CELS_INDEX] = (uint)-0x10000; + myRegs[IDX_CELS_COUNT] = numSprites << 16; + myRegs[IDX_CELS_FRAME_RATE] = celsPtr[CELS_FRAME_RATE] << 16; + + // Here we convert the offset table to become the actual size of the data for each sprite + // This is so the stream can be optimized to always read in on sprite boundaries + // Get the beginning of the offset table + offsets = &celsPtr[CELS_OFFSETS]; + + maxFrameSize = 0; + // For all but the last frame, the frame size is the difference in offset values + for (i = 0; i < numSprites - 1; i++) { + offsets[i] = offsets[i + 1] - offsets[i]; + if (offsets[i] > maxFrameSize) { + maxFrameSize = offsets[i]; + obesest_frame = i; + } + } + + // For the last sprite we take the entire chunk size - the chunk header - the offset for that sprite + offsets[numSprites - 1] = celsPtr[CELS_SRC_SIZE] - ((SS_HEAD_SIZE + celsPtr[CELS_COUNT]) << 2) - offsets[numSprites - 1]; + if (offsets[numSprites - 1] > maxFrameSize) { + maxFrameSize = offsets[numSprites - 1]; + obesest_frame = numSprites - 1; + } + + // Calculate the maximum size a sprite could be + maxFrameSize += SS_INDV_HEAD << 2; + + if (!myCCB->source) { + myCCB->source = (M4sprite *)mem_alloc(sizeof(M4sprite), "Sprite"); + if (!myCCB->source) { + ws_LogErrorMsg(FL, "Failed to mem_alloc() %ld bytes.", sizeof(M4sprite)); + return false; + } + } + + term_message("Biggest frame was: %ld, size: %ld bytes (compressed)", obesest_frame, maxFrameSize); + + // Access the streamer to recognize the new client + if ((myCCB->myStream = (void *)f_stream_Open(streamFile, ssDataOffset, maxFrameSize, maxFrameSize << 4, numSprites, (int32 *)offsets, 4, false)) == nullptr) { + ws_LogErrorMsg(FL, "Failed to open a stream."); + return false; + } + + // Tag the CCB as being streamed + myCCB->flags |= CCB_DISC_STREAM; + myCCB->seriesName = nullptr; + + // Get the first frame + if (!ws_GetNextSSstreamCel(anim8)) { + ws_LogErrorMsg(FL, "Failed to get the first stream frame."); + return false; + } + + return true; +} + +bool ws_GetNextSSstreamCel(Anim8 *anim8) { + CCB *myCCB; + M4sprite *mySprite; + uint32 *celsPtr, *offsets, *myCelSource; + uint32 frameNum; + + // Verify the parameters + if (!anim8) { + ws_LogErrorMsg(FL, "nullptr Anim8* given"); + return false; + } + + myCCB = anim8->myCCB; + if ((!anim8->myCCB) || (!myCCB->streamSSHeader) || (!myCCB->myStream)) { + ws_LogErrorMsg(FL, "Invalid Anim8* given."); + return false; + } + if (!(myCCB->flags & CCB_DISC_STREAM)) { + ws_LogErrorMsg(FL, "Anim8* given has not previously opened a stream"); + return false; + } + + // Find the SS source and the offset table into the source + celsPtr = myCCB->streamSSHeader; + offsets = &celsPtr[CELS_OFFSETS]; + + // Automatically increment the sequence register + anim8->myRegs[IDX_CELS_INDEX] += 0x10000; + + // Check whether the end of the SS has been streamed + frameNum = anim8->myRegs[IDX_CELS_INDEX] >> 16; + if (frameNum >= celsPtr[CELS_COUNT]) { + ws_LogErrorMsg(FL, "No more frames available to stream"); + return false; + } + + // Read the next sprite from the stream. Note the offset table was converted to absolute size when the stream was opened. + if (f_stream_Read((strmRequest *)myCCB->myStream, (uint8 **)(&myCCB->streamSpriteSource), offsets[frameNum]) < (int)offsets[frameNum]) { + ws_LogErrorMsg(FL, "Unable to read the next stream frame"); + return false; + } + + // Flag the CCB if the sprite series is a delta-streaming sprite series + if (myCCB->streamSpriteSource[CELS_STREAM]) { + myCCB->flags |= CCB_STREAM; + } + + // Initialize the sprite structure + myCelSource = myCCB->streamSpriteSource; + mySprite = myCCB->source; + + mySprite->xOffset = (int32)convert_intel32((uint32)myCelSource[CELS_X]); + mySprite->yOffset = (int32)convert_intel32((uint32)myCelSource[CELS_Y]); + mySprite->w = (int32)convert_intel32((uint32)myCelSource[CELS_W]); + mySprite->h = (int32)convert_intel32((uint32)myCelSource[CELS_H]); + + { + uint32 temp = (uint8)myCelSource[CELS_COMP]; + mySprite->encoding = (uint8)convert_intel32(temp); + } + + mySprite->data = (uint8 *)&myCelSource[CELS_DATA]; + + // Initialize the CCB structure + myCCB->newLocation->x1 = 0; + myCCB->newLocation->y1 = 0; + myCCB->newLocation->x2 = 0; + myCCB->newLocation->y2 = 0; + myCCB->scaleX = 0; + myCCB->scaleY = 0; + + return true; +} + +void ws_CloseSSstream(CCB *myCCB) { + + // Verify the parameters + if ((!myCCB) || (!(myCCB->flags & CCB_DISC_STREAM))) { + ws_LogErrorMsg(FL, "Invalid CCB* given."); + return; + } + + // Remove the CCB_DISC_STREAM flag + myCCB->flags &= ~CCB_DISC_STREAM; + + // Free up the CCB pointers which store streaming information + if (myCCB->streamSSHeader) { + mem_free((char *)myCCB->streamSSHeader); + } + + // Close the stream + if (myCCB->myStream) { + f_stream_Close((strmRequest *)myCCB->myStream); + myCCB->myStream = nullptr; + } +} + } // End of namespace M4 diff --git a/engines/m4/wscript/ws_load.h b/engines/m4/wscript/ws_load.h index 6e85a479763..6bafea73e71 100644 --- a/engines/m4/wscript/ws_load.h +++ b/engines/m4/wscript/ws_load.h @@ -66,13 +66,13 @@ struct WSLoad_Globals { char **_globalDATAnames = nullptr; char **_globalCELSnames = nullptr; - Handle *_globalMACHHandles = nullptr; + MemHandle *_globalMACHHandles = nullptr; int32 *_globalMACHoffsets = nullptr; - Handle *_globalSEQUHandles = nullptr; + MemHandle *_globalSEQUHandles = nullptr; int32 *_globalSEQUoffsets = nullptr; - Handle *_globalDATAHandles = nullptr; + MemHandle *_globalDATAHandles = nullptr; int32 *_globalDATAoffsets = nullptr; - Handle *_globalCELSHandles = nullptr; + MemHandle *_globalCELSHandles = nullptr; int32 *_globalCELSoffsets = nullptr; int32 *_globalCELSPaloffsets = nullptr; }; @@ -81,20 +81,19 @@ extern bool InitWSAssets(); extern bool ClearWSAssets(uint32 assetType, int32 minHash, int32 maxHash); extern void ShutdownWSAssets(); -extern bool ws_CELSIntegrity(int32 minHash, int32 maxHash); extern bool LoadWSAssets(const char *wsAssetName, RGB8 *myPalette); extern int32 AddWSAssetCELS(const char *wsAssetName, int32 hash, RGB8 *myPalette); -extern uint32 *FindSpriteSource(uint32 *celsPtr, int32 index); extern M4sprite *GetWSAssetSprite(char *spriteName, uint32 hash, uint32 index, M4sprite *mySprite, bool *streamSeries); + extern CCB *GetWSAssetCEL(uint32 hash, uint32 index, CCB *myCCB); extern int32 GetWSAssetCELCount(uint32 hash); extern int32 GetWSAssetCELFrameRate(uint32 hash); extern int32 GetWSAssetCELPixSpeed(uint32 hash); extern int32 ws_get_sprite_width(uint32 hash, int32 index); extern int32 ws_get_sprite_height(uint32 hash, int32 index); -extern Handle ws_GetSEQU(uint32 hash, int32 *numLocalVars, int32 *offset); -extern Handle ws_GetMACH(uint32 hash, int32 *numStates, int32 *stateTableOffset, int32 *machInstrOffset); -extern Handle ws_GetDATA(uint32 hash, uint32 index, int32 *rowOffset); +extern MemHandle ws_GetSEQU(uint32 hash, int32 *numLocalVars, int32 *offset); +extern MemHandle ws_GetMACH(uint32 hash, int32 *numStates, int32 *stateTableOffset, int32 *machInstrOffset); +extern MemHandle ws_GetDATA(uint32 hash, uint32 index, int32 *rowOffset); extern int32 ws_GetDATACount(uint32 hash); extern int32 GetSSHeaderInfo(Common::SeekableReadStream *stream, uint32 **data, RGB8 *myPalette); extern bool ws_GetSSMaxWH(MemHandle ssHandle, int32 ssOffset, int32 *maxW, int32 *maxH); diff --git a/engines/m4/wscript/ws_machine.cpp b/engines/m4/wscript/ws_machine.cpp index e8a2b70e122..1b4272aaf63 100644 --- a/engines/m4/wscript/ws_machine.cpp +++ b/engines/m4/wscript/ws_machine.cpp @@ -15,44 +15,52 @@ * 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, see . + * along with this program. If not, see . * */ #include "m4/wscript/ws_machine.h" #include "m4/wscript/ws_cruncher.h" +#include "m4/wscript/ws_hal.h" +#include "m4/wscript/wst_regs.h" #include "m4/wscript/wscript.h" #include "m4/core/errors.h" +#include "m4/core/imath.h" +#include "m4/dbg/debug.h" +#include "m4/mem/mem.h" #include "m4/platform/timer.h" #include "m4/vars.h" namespace M4 { -static int32 ws_KillMachines(); +#define COND_FLAG 0x80000000 +#define OP_COUNT 0x00007fff +#define OP_JUMP 3 +#define OP_KILL 4 + static void clear_msg_list(machine *m); static void clear_persistent_msg_list(machine *m); bool ws_Initialize(frac16 *theGlobals) { int32 i; - _G(machineIDCount) = 0; - - _G(dataFormats) = ws_GetDataFormats(); + _GWS(machineIDCount) = 0; + _GWS(dataFormats) = ws_GetDataFormats(); if (!theGlobals) { ws_LogErrorMsg(FL, "ws_Initialize() called without a valid global register array."); return false; } - _G(ws_globals) = theGlobals; + _GWS(ws_globals) = theGlobals; for (i = 0; i < GLB_SHARED_VARS; i++) { - _G(ws_globals)[i] = 0; + _GWS(ws_globals)[i] = 0; } - _G(firstMachine) = NULL; - _G(nextXM) = NULL; - _G(myGlobalMessages) = NULL; + _GWS(firstMachine) = nullptr; + _GWS(nextXM) = nullptr; + _GWS(myGlobalMessages) = nullptr; if (!ws_InitWSTimer()) { return false; @@ -65,9 +73,9 @@ bool ws_Initialize(frac16 *theGlobals) { return false; } - _G(oldTime) = timer_read_60(); - _G(pauseTime) = 0; - _G(enginesPaused) = false; + _GWS(oldTime) = timer_read_60(); + _GWS(pauseTime) = 0; + _GWS(enginesPaused) = false; return true; } @@ -79,43 +87,9 @@ void ws_Shutdown() { ws_KillHAL(); } -void TerminateMachinesByHash(int32 machHash) { - warning("TODO: TerminateMachinesByHash"); -} - -static int32 ws_KillMachines() { - machine *myMachine; - globalMsgReq *tempGlobalMsg; - int32 myBytes = 0; - - // Deallocate all machines - myMachine = _G(firstMachine); - while (myMachine) { - _G(firstMachine) = _G(firstMachine)->next; - - clear_msg_list(myMachine); - clear_persistent_msg_list(myMachine); - - mem_free(myMachine); - myBytes += sizeof(machine); - myMachine = _G(firstMachine); - } - - // Deallocate global messages - tempGlobalMsg = _G(myGlobalMessages); - while (tempGlobalMsg) { - _G(myGlobalMessages) = _G(myGlobalMessages)->next; - mem_free((void *)tempGlobalMsg); - tempGlobalMsg = _G(myGlobalMessages); - } - - return myBytes; -} - static void dispose_msgRequest(msgRequest *msg) { - if (msg) { + if (msg) mem_free(msg); - } } static void clear_msg_list(machine *m) { @@ -141,7 +115,7 @@ static void clear_persistent_msg_list(machine *m) { nextMsg = nextMsg->nextMsg; dispose_msgRequest(freeMsg); } - m->myPersistentMsgs = NULL; + m->myPersistentMsgs = nullptr; // Clear the used persistent msgs nextMsg = m->usedPersistentMsgs; @@ -151,12 +125,1100 @@ static void clear_persistent_msg_list(machine *m) { dispose_msgRequest(freeMsg); } - m->usedPersistentMsgs = NULL; + m->usedPersistentMsgs = nullptr; } -void SendWSMessage(uint32 msgHash, frac16 msgValue, machine *recvM, - uint32 machHash, machine *sendM, int32 msgCount) { - error("TODO: SendWSMessage"); +static msgRequest *new_msgRequest() { + msgRequest *newMsg; + if ((newMsg = (msgRequest *)mem_alloc(sizeof(msgRequest), "msgRequest")) == nullptr) { + ws_LogErrorMsg(FL, "Failed to mem_alloc() %ld bytes.", sizeof(msgRequest)); + } + return newMsg; +} + +static void restore_persistent_msgs(machine *m) { + msgRequest *lastMsg; + + // Check params... + if ((!m) || (!m->usedPersistentMsgs)) { + return; + } + + // Loop to find the last used persistent msg + lastMsg = m->usedPersistentMsgs; + while (lastMsg->nextMsg) { + lastMsg = lastMsg->nextMsg; + } + + // Place the entire usedPersistentMsgs linked list at the front of the persistentMsgs list + lastMsg->nextMsg = m->myPersistentMsgs; + m->myPersistentMsgs = m->usedPersistentMsgs; + m->usedPersistentMsgs = nullptr; +} + +// CONDITIONAL OPs + +static void op_AFTER(machine *m, int32 *pcOffset) { + int32 myElapsedTime; + + if (!_GWS(myArg2)) { + ws_Error(m, ERR_MACH, 0x0261, "functionality: after arg1 {...}"); + } + + if (_GWS(myArg3)) { + myElapsedTime = (int32)imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)) >> 16; + } else { + myElapsedTime = (int32)(*_GWS(myArg2)) >> 16; + } + + ws_MakeOnTimeReq(_GWS(ws_globals)[GLB_TIME] + myElapsedTime, m, *pcOffset, (int32)*_GWS(myArg1) >> 14); + *pcOffset += (int32)*_GWS(myArg1) >> 14; +} + +static void op_ON_END_SEQ(machine *m, int32 *pcOffset) { + if (!_GWS(myArg1)) { + ws_Error(m, ERR_MACH, 0x0260, "on_seq_end() failed."); + } + + ws_OnEndSeqRequest(m->myAnim8, *pcOffset, *_GWS(myArg1) >> 14); + *pcOffset += (int32)*_GWS(myArg1) >> 14; +} + +/** + * Message Requests are stored in a linked list, directly in the machine + * A message is never actually received, but when another machine wishes to + * send a message to this one, it checks this list to see if the message is + * expected, and if this machine knows what to do. + */ +static void op_ON_MSG(machine *m, int32 *pcOffset) { + msgRequest *myMsg; + if (!_GWS(myArg1)) { + ws_Error(m, ERR_MACH, 0x0260, "on_msg() failed."); + } + + if ((myMsg = new_msgRequest()) == nullptr) { + ws_Error(m, ERR_MACH, 0x02fe, "on_msg() failed."); + return; + } + + if (_GWS(myArg2)) { + myMsg->msgHash = *_GWS(myArg2); + } else { + myMsg->msgHash = 0; + } + if (_GWS(myArg3)) { + myMsg->msgValue = *_GWS(myArg3); + } else { + myMsg->msgValue = 0; + } + + myMsg->pcOffset = *pcOffset; + myMsg->pcCount = (int32)*_GWS(myArg1) >> 14; + myMsg->nextMsg = m->myMsgs; + m->myMsgs = myMsg; + *pcOffset += (int32)*_GWS(myArg1) >> 14; +} + +/** + * Same as op_ON_MSG() except these messages do not get cleared between states + */ +static void op_ON_P_MSG(machine *m, int32 *pcOffset) { + msgRequest *myMsg, *prevMsg; + uint32 msgHash; + frac16 msgValue; + + if (!_GWS(myArg2)) { + ws_Error(m, ERR_MACH, 0x0261, "functionality: on_p_msg arg1 {...}"); + } + + // Get the values for msgHash and msgValue from the args... + msgHash = *_GWS(myArg2); + if (_GWS(myArg3)) { + msgValue = *_GWS(myArg3); + } else { + msgValue = 0; + } + + // Since the message is persistent, it may have been satisfied earlier, check the used list + prevMsg = nullptr; + myMsg = m->usedPersistentMsgs; + + // Loop through all the used msgs, see if there is already a struct in place + while (myMsg && ((myMsg->msgHash != msgHash) || (myMsg->msgValue != msgValue))) { + prevMsg = myMsg; + myMsg = myMsg->nextMsg; + } + + // If a previous identical msg has already been requested, restore it + if (myMsg) { + // Remove it from the used msgs linked list + // if myMsg is first in the list + if (!prevMsg) { + m->usedPersistentMsgs = myMsg->nextMsg; + } else { + // Else myMsg is in the middle of the list (after prevMsg) + prevMsg->nextMsg = myMsg->nextMsg; + } + } else { + // Else a new msg has to be created + if ((myMsg = new_msgRequest()) == nullptr) { + ws_Error(m, ERR_MACH, 0x02fe, "on_p_msg() failed."); + return; + } + + // Set the msg request values + myMsg->msgHash = msgHash; + myMsg->msgValue = msgValue; + } + + // Since it may be a replacement msg, a new pcOffset may be set + myMsg->pcOffset = *pcOffset; + myMsg->pcCount = (int32)*_GWS(myArg1) >> 14; + + // Link it into the list + myMsg->nextMsg = m->myPersistentMsgs; + m->myPersistentMsgs = myMsg; + + // Update the pcOffset + *pcOffset += (int32)*_GWS(myArg1) >> 14; +} + +static void op_SWITCH_LT(machine *m, int32 *pcOffset) { + if (!_GWS(myArg3)) { + ws_Error(m, ERR_MACH, 0x0262, "functionality: switch (arg1 < arg2) {...}"); + } + if (*_GWS(myArg2) >= *_GWS(myArg3)) { + *pcOffset += (int32)*_GWS(myArg1) >> 14; + } +} + +static void op_SWITCH_LE(machine *m, int32 *pcOffset) { + if (!_GWS(myArg3)) { + ws_Error(m, ERR_MACH, 0x0262, "functionality: switch (arg1 <= arg2) {...}"); + } + if (*_GWS(myArg2) > *_GWS(myArg3)) { + *pcOffset += (int32)*_GWS(myArg1) >> 14; + } +} + +static void op_SWITCH_EQ(machine *m, int32 *pcOffset) { + if (!_GWS(myArg3)) { + ws_Error(m, ERR_MACH, 0x0262, "functionality: switch (arg1 == arg2) {...}"); + } + if (*_GWS(myArg2) != *_GWS(myArg3)) { + *pcOffset += (int32)*_GWS(myArg1) >> 14; + } +} + +static void op_SWITCH_NE(machine *m, int32 *pcOffset) { + if (!_GWS(myArg3)) { + ws_Error(m, ERR_MACH, 0x0262, "functionality: switch (arg1 != arg2) {...}"); + } + if (*_GWS(myArg2) == *_GWS(myArg3)) { + *pcOffset += (int32)*_GWS(myArg1) >> 14; + } +} + +static void op_SWITCH_GE(machine *m, int32 *pcOffset) { + if (!_GWS(myArg3)) { + ws_Error(m, ERR_MACH, 0x0262, "functionality: switch (arg1 >= arg2) {...}"); + } + if (*_GWS(myArg2) < *_GWS(myArg3)) { + *pcOffset += (int32)*_GWS(myArg1) >> 14; + } +} + +static void op_SWITCH_GT(machine *m, int32 *pcOffset) { + if (!_GWS(myArg3)) { + ws_Error(m, ERR_MACH, 0x0262, "functionality: switch (arg1 > arg2) {...}"); + } + if (*_GWS(myArg2) <= *_GWS(myArg3)) { + *pcOffset += (int32)*_GWS(myArg1) >> 14; + } +} + + +// IMMEDIATE OPs + +static bool op_DO_NOTHING(machine *m, int32 *pcOffset) { + return true; +} + +static bool op_GOTO(machine *m, int32 *pcOffset) { + if (!_GWS(myArg1)) { + ws_Error(m, ERR_MACH, 0x0263, "functionality: goto arg1"); + } + m->curState = (*_GWS(myArg1)) >> 16; + m->recurseLevel = 0; + return false; +} + +static bool op_JUMP(machine *m, int32 *pcOffset) { + if (!_GWS(myArg1)) { + ws_Error(m, ERR_MACH, 0x0263, "functionality: jump arg1"); + } + + *pcOffset += (int32)*_GWS(myArg1) >> 16; + return true; +} + +static bool op_TERMINATE(machine *m, int32 *pcOffset) { + m->curState = -1; + m->recurseLevel = 0; + return false; +} + +static bool op_START_SEQ(machine *m, int32 *pcOffset) { + if (!_GWS(myArg1)) { + ws_Error(m, ERR_MACH, 0x0263, "functionality: start_seq arg1"); + } + + // Here we check whether a program was previously running + if (!m->myAnim8) { + if ((m->myAnim8 = ws_AddAnim8ToCruncher(m, *_GWS(myArg1) >> 16)) == nullptr) { + ws_Error(m, ERR_MACH, 0x02ff, "start_seq() failed."); + } + } else { + if (!ws_ChangeAnim8Program(m, *_GWS(myArg1) >> 16)) { + ws_Error(m, ERR_MACH, 0x02ff, "start_seq() failed."); + } + } + + // Inform the ws debugger of the new sequence + dbg_LaunchSequence(m->myAnim8); + + return true; +} + +static bool op_PAUSE_SEQ(machine *m, int32 *pcOffset) { + ws_PauseAnim8(m->myAnim8); + return true; +} + +static bool op_STORE_VAL(machine *m, int32 *pcOffset) { + if (!_GWS(myArg2)) { + ws_Error(m, ERR_MACH, 0x0264, "functionality: arg1 = arg2 or arg1 = rand(arg2, arg3)"); + } + if (_GWS(myArg3)) { + *_GWS(myArg1) = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)); + } else { + *_GWS(myArg1) = *_GWS(myArg2); + } + return true; +} + +static bool op_SEND_MSG(machine *m, int32 *pcOffset) { + frac16 msgValue; + + if (!_GWS(myArg2)) { + ws_Error(m, ERR_MACH, 0x0264, "functionality: send to machine arg1, message arg2"); + } + if (_GWS(myArg3)) { + msgValue = *_GWS(myArg3); + } else { + msgValue = 0; + } + SendWSMessage(*_GWS(myArg2), msgValue, nullptr, *_GWS(myArg1) >> 16, m, 1); + return true; +} + +static bool op_SEND_GMSG(machine *m, int32 *pcOffset) { + frac16 msgValue; + + if (!_GWS(myArg2)) { + ws_Error(m, ERR_MACH, 0x0264, "functionality: send to to all machines of type arg1, message arg2"); + } + if (_GWS(myArg3)) { + msgValue = *_GWS(myArg3); + } else { + msgValue = 0; + } + + SendWSMessage(*_GWS(myArg2), msgValue, nullptr, *_GWS(myArg1) >> 16, m, 0); + return true; +} + +static bool op_REPLY_MSG(machine *m, int32 *pcOffset) { + frac16 msgValue; + + if (!_GWS(myArg1)) { + ws_Error(m, ERR_MACH, 0x0263, "functionality: reply to sender with msg arg1"); + } + if (_GWS(myArg2)) { + msgValue = *_GWS(myArg2); + } else { + msgValue = 0; + } + + SendWSMessage(*_GWS(myArg1), msgValue, m->msgReplyXM, 0, m, 1); + return true; +} + +static bool op_SYSTEM_MSG(machine *m, int32 *pcOffset) { + + if (!_GWS(myArg1)) { + ws_Error(m, ERR_MACH, 0x0263, "functionality: send to 'C' callback function with msg arg1"); + } + + if (m->CintrMsg) { + (m->CintrMsg)(*_GWS(myArg1), m); + } + + return true; +} + +static bool op_TRIG(machine *m, int32 *pcOffset) { + int32 myCount, i; + char tempStr[80]; + + if (!_GWS(myArg1)) { + ws_Error(m, ERR_MACH, 0x0263, "functionality: trigger mach arg1, arg2 instances"); + } + + if (_GWS(myArg2)) { + if (_GWS(myArg3)) { + myCount = imath_ranged_rand16(*_GWS(myArg2), *_GWS(myArg3)) >> 16; + } else { + myCount = (*_GWS(myArg2)) >> 16; + } + } else { + myCount = 1; + } + + Common::sprintf_s(tempStr, "*** TRIGGERED BY MACHINE: %ld", m->myHash); + for (i = 0; i < myCount; i++) { + if (!TriggerMachineByHash(*_GWS(myArg1) >> 16, m->myAnim8, -1, -1, m->CintrMsg, false, tempStr)) { + ws_Error(m, ERR_MACH, 0x0267, "trig() failed"); + } + } + + return true; +} + +static bool op_TRIG_W(machine *m, int32 *pcOffset) { + int32 myHash, myCount = 0, minCount = 0, maxCount = 0, i, myInstruction; + int32 myIndex, minIndex, maxIndex, myDataCount; + int32 myDataHash; + bool randFlag = false; + char tempStr[80]; + uint32 *myPC, *oldPC, *machInstr; + + if (!_GWS(myArg1)) { + ws_Error(m, ERR_MACH, 0x0263, "functionality: trigger mach arg1, arg2 instances"); + } + + myHash = (*_GWS(myArg1)) >> 16; + if (_GWS(myArg2)) { + if (_GWS(myArg3)) { + randFlag = true; + minCount = (*_GWS(myArg2)) >> 16; + maxCount = (*_GWS(myArg3)) >> 16; + } else { + myCount = (*_GWS(myArg2)) >> 16; + } + } else { + myCount = 1; + } + + // This is a double length instruction - up to 128 bits, we must read in the next pCode + machInstr = (uint32 *)((uint32)(*(m->machHandle)) + (uint32)m->machInstrOffset); + myPC = (uint32 *)((uint32)(machInstr)+*pcOffset); + oldPC = myPC; + if ((myInstruction = ws_PreProcessPcode(&myPC, m->myAnim8)) < 0) { + ws_Error(m, ERR_MACH, 0x0266, "trig_w() failed."); + } + + // Now find the new pcOffset + *pcOffset += (int32)myPC - (int32)oldPC; + + if (!_GWS(myArg1)) { + ws_Error(m, ERR_MACH, 0x0263, "trig_w instruction requires a data hash specified by a second pCode."); + } + + myDataHash = (int32)(*_GWS(myArg1)) >> 16; + myDataCount = ws_GetDATACount(myDataHash); + if (_GWS(myArg2)) { + if (_GWS(myArg3)) { + minIndex = (*_GWS(myArg2)) >> 16; + maxIndex = (*_GWS(myArg3)) >> 16; + } else { + minIndex = (*_GWS(myArg2)) >> 16; + maxIndex = (*_GWS(myArg2)) >> 16; + } + } else { + minIndex = 0; + maxIndex = myDataCount; + } + + if (myInstruction) { + for (myIndex = minIndex; myIndex <= maxIndex; i++) { + if (randFlag) { + myCount = imath_ranged_rand(minCount, maxCount); + } + for (i = 0; i < myCount; i++) { + Common::sprintf_s(tempStr, "*** TRIGGERED BY MACHINE: %ld", m->myHash); + if (!TriggerMachineByHash(myHash, m->myAnim8, myDataHash, myIndex, m->CintrMsg, false, tempStr)) { + ws_Error(m, ERR_MACH, 0x0267, "trig_w() failed"); + } + } + } + } else { + myIndex = imath_ranged_rand(minIndex, maxIndex); + if (randFlag) { + myCount = imath_ranged_rand(minCount, maxCount); + } + for (i = 0; i < myCount; i++) { + Common::sprintf_s(tempStr, "*** TRIGGERED BY MACHINE: %ld", m->myHash); + if (!TriggerMachineByHash(myHash, m->myAnim8, myDataHash, myIndex, m->CintrMsg, false, tempStr)) { + ws_Error(m, ERR_MACH, 0x0267, "trig_w() failed"); + } + } + } + return true; +} + +static bool op_CLEAR_REGS(machine *m, int32 *pcOffset) { + Anim8 *myAnim8; + int32 i; + + if (!m->myAnim8) { + ws_Error(m, ERR_INTERNAL, 0x02f3, "clear_regs() failed."); + } + + myAnim8 = m->myAnim8; + for (i = 0; i < IDX_COUNT + myAnim8->numLocalVars; i++) { + myAnim8->myRegs[i] = 0; + } + + myAnim8->myRegs[IDX_S] = 0x10000; + + return true; +} + +static bool op_RESUME_SEQ(machine *m, int32 *pcOffset) { + if (!m->myAnim8) { + ws_Error(m, ERR_INTERNAL, 0x02f3, "resume_seq() failed."); + } + + ws_ResumeAnim8(m->myAnim8); + return true; +} + + +bool (*immOpTable[])(machine *m, int32 *pcOffset) = { + nullptr, //0 ***END + &op_DO_NOTHING, //1 + &op_GOTO, //2 + &op_JUMP, //3 don't forget the op_jump #define + &op_TERMINATE, //4 + &op_START_SEQ, //5 + &op_PAUSE_SEQ, //6 + &op_RESUME_SEQ, //7 + &op_STORE_VAL, //8 + &op_SEND_MSG, //9 + &op_SEND_GMSG, //10 + &op_REPLY_MSG, //11 + &op_SYSTEM_MSG, //12 + &op_TRIG, //13 + &op_TRIG_W, //14 + &op_CLEAR_REGS //15 +}; + +void (*condOpTable[])(machine *m, int32 *pcOffset) = { + &op_AFTER, //0 + &op_ON_END_SEQ, //1 + &op_ON_MSG, //2 + &op_ON_P_MSG, //3 + &op_SWITCH_LT, //4 + &op_SWITCH_LE, //5 + &op_SWITCH_EQ, //6 + &op_SWITCH_NE, //7 + &op_SWITCH_GE, //8 + &op_SWITCH_GT //9 +}; + +void PauseEngines(void) { + _GWS(enginesPaused) = true; +} + +void UnpauseEngines(void) { + _GWS(enginesPaused) = false; +} + +void AddPauseTime(int32 myTime) { + _GWS(pauseTime) += myTime; +} + +void CycleEngines(Buffer * cleanBackground, int16 * depth_table, GrBuff * screenCodes, uint8 * myPalette, uint8 * ICT, bool updateVideo) { + int32 clockTime; + + dbg_DebugNextCycle(); + clockTime = timer_read_60(); + + if (_GWS(enginesPaused)) { + _GWS(pauseTime) += clockTime - _GWS(oldTime); + _GWS(oldTime) = clockTime; + + } else { + _GWS(ws_globals)[GLB_WATCH_DOG] = clockTime - _GWS(pauseTime) - _GWS(ws_globals)[GLB_TIME]; + _GWS(ws_globals)[GLB_TIME] += _GWS(ws_globals)[GLB_WATCH_DOG]; + ws_CrunchAnim8s(depth_table); + + if (cleanBackground) { + ws_DoDisplay(cleanBackground, depth_table, screenCodes, myPalette, ICT, updateVideo); + } + ws_CrunchEOSreqs(); + + ws_CheckTimeReqs(_GWS(ws_globals)[GLB_TIME]); + } +} + +void ws_RefreshWoodscriptBuffer(Buffer *cleanBackground, int16 *depth_table, GrBuff *screenCodes, uint8 *myPalette, uint8 *ICT) { + ws_hal_RefreshWoodscriptBuffer(_GWS(myCruncher), cleanBackground, depth_table, + screenCodes, myPalette, ICT); +} + +static void CancelAllEngineReqs(machine * m) { + globalMsgReq *myGMsg, *tempGMsg; + + //---- CANCEL CRUNCHER REQS + if (m->myAnim8) { + ws_CancelOnEndSeq(m->myAnim8); + } + + //---- Free all pending state message requests in this machine + clear_msg_list(m); + + //---- Restore all persistent message requests in this machine + restore_persistent_msgs(m); + + //---- Free all pending global messages requests in this machine + if (_GWS(myGlobalMessages)) { + myGMsg = _GWS(myGlobalMessages); + while (myGMsg->next) { + if (myGMsg->next->sendM == m) { + tempGMsg = myGMsg->next; + myGMsg->next = myGMsg->next->next; + mem_free((void *)tempGMsg); + } else myGMsg = myGMsg->next; + } + } + + //---- CANCEL TIMER REQS + ws_CancelOnTimeReqs(m); +} + + +static void ShutdownMachine(machine * m) { + dbg_RemoveWSMach(m); + + if (m->myAnim8) { + ws_RemoveAnim8FromCruncher(m->myAnim8); + } + + //---- Free all pending message requests in this machine + clear_msg_list(m); + clear_persistent_msg_list(m); + + // Fix nextXM so SendWSMessage doesn't break + if (m == _GWS(nextXM)) { + _GWS(nextXM) = _GWS(nextXM)->next; + } + + // Clear any existing walk path + DisposePath(m->walkPath); + + // If there is no previous machine, the next machine becomes the first one + if (m->prev) { + m->prev->next = m->next; + } else { + _GWS(firstMachine) = m->next; + } + + if (m->next) { + m->next->prev = m->prev; + } + + m->machID = 0xdeaddead; + + if (m->machName) { + mem_free((void *)m->machName); + } + + mem_free((void *)m); +} + + +void TerminateMachinesByHash(uint32 machHash) { + machine *curr, *next; + + curr = _GWS(firstMachine); // Start at beginning of machine chain + while (curr) { + next = curr->next; // Preserve next pointer against curr's dealloc + if (curr->myHash == machHash) { // is this one to delete? + if (curr == _GWS(firstMachine)) { // maintain the beginning of machine chain + _GWS(firstMachine) = next; + } + CancelAllEngineReqs(curr); // cancel its requests + ShutdownMachine(curr); // deallocate the whole ball'o'wax + } + curr = next; // and pop aint32 the chain + } +} + +void TerminateMachine(machine * myMachine) { + if ((!myMachine) || (!VerifyMachineExists(myMachine))) { + return; + } + + CancelAllEngineReqs(myMachine); + ShutdownMachine(myMachine); +} + + +bool VerifyMachineExists(machine * m) { + machine *tempM; + + // Parameter verification + if (!m) { + return false; + } + + // Loop through the active machine list, looking for m + tempM = _GWS(firstMachine); + while (tempM && (tempM != m)) { + tempM = tempM->next; + } + + // If the end of the list was reached, and m was not found, false + if (!tempM) { + return false; + } + + // Otherwise m was found, therefore machine exists + return true; +} + +int32 ws_KillMachines() { + machine *myMachine; + globalMsgReq *tempGlobalMsg; + int32 myBytes = 0; + + // Deallocate all machines + myMachine = _GWS(firstMachine); + while (myMachine) { + _GWS(firstMachine) = _GWS(firstMachine)->next; + + clear_msg_list(myMachine); + clear_persistent_msg_list(myMachine); + + mem_free((void *)myMachine); + myBytes += sizeof(machine); + myMachine = _GWS(firstMachine); + } + + // Deallocate global messages + tempGlobalMsg = _GWS(myGlobalMessages); + while (tempGlobalMsg) { + _GWS(myGlobalMessages) = _GWS(myGlobalMessages)->next; + mem_free((void *)tempGlobalMsg); + tempGlobalMsg = _GWS(myGlobalMessages); + } + + return myBytes; +} + + +// This is the proc designed to evaluate the instructions of the state machine + +static int32 StepAt(int32 * pcOffset, machine * m) { + bool keepProcessing; + int32 myInstruction; + Anim8 *myAnim8; + uint32 machID, *myPC, *oldPC, *machInstr; + + machID = m->machID; + myAnim8 = m->myAnim8; + + dbg_SetCurrMachInstr(m, *pcOffset); + + // Find the current PC and process it to get the current instruction + machInstr = (uint32 *)((uint32)(*(m->machHandle)) + (uint32)m->machInstrOffset); + myPC = (uint32 *)((uint32)(machInstr)+*pcOffset); + oldPC = myPC; + _GWS(pcOffsetOld) = *pcOffset; + if ((myInstruction = ws_PreProcessPcode(&myPC, myAnim8)) < 0) { + ws_Error(m, ERR_MACH, 0x0266, nullptr); + } + + // Now find the new pcOffset + *pcOffset += (int32)myPC - (int32)oldPC; + + if (myInstruction >= 64) { + condOpTable[myInstruction - 64](m, pcOffset); + } else if (myInstruction > 0) { + keepProcessing = immOpTable[myInstruction](m, pcOffset); + if (!keepProcessing) { + + // Does the machine still exist + if (m->machID == machID) { + + CancelAllEngineReqs(m); + if (m->curState == -1) { + ShutdownMachine(m); + } else { // If machine hasn't terminated + IntoTheState(m); // recurse to kickstart next state + } + } + } + } + return myInstruction; +} + + +void ws_StepWhile(machine * m, int32 pcOffset, int32 pcCount) { + int32 myInstruction, oldPC; + uint32 machID, recurseLevel; + + // We are executing machine instructions after a conditional has been satisfied. + // Mark where we started + oldPC = pcOffset; + + // Increment and remember the recurseLevel and the machine ID + m->recurseLevel++; + recurseLevel = m->recurseLevel; + machID = m->machID; + + // Execute instructions until the conditional count has been reached. + myInstruction = -1; + while (myInstruction && (myInstruction != OP_KILL) && + (pcOffset >= oldPC) && (pcOffset - oldPC < pcCount) && + (m->machID == machID) && (m->recurseLevel == recurseLevel)) { + myInstruction = StepAt(&pcOffset, m); + } + + // The last instruction might have been a JUMP instruction. This should be + // a JUMP to reissue the conditional, Therefore, reexecute the conditional. + if (myInstruction == OP_JUMP) { + StepAt(&pcOffset, m); + } + + // If the above loop executed without being modified (ie terminated) by a call to StepAt() + if (myInstruction != OP_KILL) { + if ((m->machID == machID) && (m->recurseLevel == recurseLevel)) { + m->recurseLevel--; + } + } +} + + +// When a state machine enters a new state, every request and command is +// evaluated immediately. + +void IntoTheState(machine * m) { + int32 myInstruction; + uint32 *stateTable, machID, recurseLevel; + int32 pcOffset; + + if ((m->curState >= m->numOfStates) || (m->curState < 0)) { + ws_Error(m, ERR_INTERNAL, 0x2f2, "IntoTheState() failed."); + } + + stateTable = (uint32 *)((uint32)(*(m->machHandle)) + (uint32)m->stateTableOffset); + pcOffset = stateTable[m->curState]; + + // Increment and remember the recurseLevel and the machine ID + m->recurseLevel++; + recurseLevel = m->recurseLevel; + machID = m->machID; + + // Execute all instruction until an instruction (ie. OP_END) signals execution to stop + // by returning 0, or something has reset the recurseLevel (ie. op_GOTO) + myInstruction = -1; + while (myInstruction && (myInstruction != OP_KILL) && + ((m->machID == machID) && (m->recurseLevel == recurseLevel))) { + myInstruction = StepAt(&pcOffset, m); + } + + if (myInstruction != OP_KILL) { + // If the above loop executed without being modified (ie terminated) by a call to StepAt() + if ((m->machID == machID) && (m->recurseLevel == recurseLevel)) { + // Decriment the recurse counter + m->recurseLevel--; + } + } +} + +// This proc creates an instance of a machine based on the machine chunk + +machine *TriggerMachineByHash(int32 myHash, Anim8 * parentAnim8, int32 dataHash, int32 dataRow, MessageCB CintrMsg, bool debug, const char *machName) { + machine *m; + + if ((m = (machine *)mem_alloc(sizeof(machine), "machine")) == nullptr) { + ws_LogErrorMsg(FL, "Out of memory - mem requested: %ld.", sizeof(machine)); + ws_LogErrorMsg(FL, "Trying to trigger hash: %ld, name: %s", myHash, machName); + ws_Error(m, ERR_INTERNAL, 0x2fe, "TriggerMachineByHash() failed."); + return nullptr; + } + + // Initialize the identification fields + _GWS(machineIDCount)++; + if (_GWS(machineIDCount) == 0xdeaddead) { + _GWS(machineIDCount)++; + } + + m->myHash = myHash; + m->machID = _GWS(machineIDCount); + m->machName = mem_strdup(machName); + + if ((m->machHandle = ws_GetMACH(myHash, &m->numOfStates, &m->stateTableOffset, &m->machInstrOffset)) == nullptr) { + ws_LogErrorMsg(FL, "Trying to trigger hash: %ld, name: %s", myHash, machName); + return nullptr; + } + + // Get the data handle and offset if requested + if (dataHash >= 0) { + m->dataHash = dataHash; + if ((m->dataHandle = ws_GetDATA(dataHash, (uint32)dataRow, &m->dataOffset)) == nullptr) { + ws_LogErrorMsg(FL, "Trying to trigger hash: %ld, name: %s", myHash, machName); + return nullptr; + } + } else { + m->dataHash = -1; + m->dataHandle = nullptr; + } + + // Insert m into the list... + m->next = _GWS(firstMachine); + m->prev = nullptr; + if (_GWS(firstMachine)) { + _GWS(firstMachine)->prev = m; + } + _GWS(firstMachine) = m; + + m->recurseLevel = 0; + m->curState = 0; + m->myAnim8 = nullptr; + m->parentAnim8 = parentAnim8; + m->targetCount = 0; + m->msgReplyXM = nullptr; + m->CintrMsg = CintrMsg; + m->myMsgs = nullptr; + m->myPersistentMsgs = nullptr; + m->usedPersistentMsgs = nullptr; + m->walkPath = nullptr; + + dbg_DebugWSMach(m, debug); + + IntoTheState(m); + return m; +} + +enum { + REGULAR_MSG = 0, + PERSISTENT_MSG +}; + + +static bool SearchMsgList(uint32 msgHash, uint32 msgValue, machine * recvM, int32 whichList, machine * sendM) { + bool found; + msgRequest *myMsg = nullptr, *prevMsg; + int32 pcOffset, pcCount; + + // Initialize search vars + found = false; + prevMsg = nullptr; + + // Find the first msg, based on which list is to be searched + switch (whichList) { + // Regular messages + case REGULAR_MSG: + myMsg = recvM->myMsgs; + break; + + // Persistent msgs + case PERSISTENT_MSG: + myMsg = recvM->myPersistentMsgs; + break; + } + + // Search through the message list + while (myMsg && (!found)) { + // Check if we've found the msg we're looking for + if ((myMsg->msgHash == msgHash) && (myMsg->msgValue == msgValue)) { + + // Set found bool + found = true; + + // Find out where to begin executing from + pcOffset = myMsg->pcOffset; + pcCount = myMsg->pcCount; + + // Remove the msg from the list, based on which list + switch (whichList) { + // Regular messages + case REGULAR_MSG: + // If myMsg was first in the list + if (!prevMsg) { + recvM->myMsgs = myMsg->nextMsg; + } else { + // Else it was in the middle + prevMsg->nextMsg = myMsg->nextMsg; + } + + // Dispose of the message + dispose_msgRequest(myMsg); + break; + + // Persistent messages + case PERSISTENT_MSG: + // If myMsg was first in the list + if (!prevMsg) { + recvM->myPersistentMsgs = myMsg->nextMsg; + } else { + // Else it was in the middle + prevMsg->nextMsg = myMsg->nextMsg; + } + + // Move the message to the inactive list + myMsg->nextMsg = recvM->usedPersistentMsgs; + recvM->usedPersistentMsgs = myMsg; + break; + } + + // Set up so the recv machine can reply to this message + recvM->msgReplyXM = sendM; + + // Service the request + ws_StepWhile(recvM, pcOffset, pcCount); + } else { + // Else check the next message + prevMsg = myMsg; + myMsg = myMsg->nextMsg; + } + } + + // Return whether a message was found or not + return found; +} + +// This proc is what allows a machine to send a message to another machine(s) + +void SendWSMessage(uint32 msgHash, frac16 msgValue, machine * recvM, + uint32 machHash, machine * sendM, int32 msgCount) { + + bool found, more_to_send; + machine *currMachine; + int32 myCount; // ,i; + bool sendToAll; + globalMsgReq *myGlobalMsgs, *tempGlobalMsg; + + // In this case we are sending to a specific machine: recvM + if (recvM) { + // Search first the regular message list, and if it was not found + if (!SearchMsgList(msgHash, msgValue, recvM, REGULAR_MSG, sendM)) { + // Search the persistent message list + SearchMsgList(msgHash, msgValue, recvM, PERSISTENT_MSG, sendM); + } + + // and return + return; + } + + // Otherwise... + // Not sending to a specific machine, so send to machines with the given hash + + // Prepare a global message structure + if ((tempGlobalMsg = (globalMsgReq *)mem_alloc(sizeof(globalMsgReq), "globalMsgReq")) == nullptr) { + ws_LogErrorMsg(FL, "Out of memory - mem requested: %ld.", sizeof(machine)); + ws_Error(nullptr, ERR_INTERNAL, 0x2fe, "SendWSMessage() failed."); + } + tempGlobalMsg->msgHash = msgHash; + tempGlobalMsg->msgValue = msgValue; + tempGlobalMsg->machHash = machHash; + tempGlobalMsg->sendM = sendM; + tempGlobalMsg->msgCount = msgCount; + tempGlobalMsg->next = nullptr; + + // If we are in the middle of a "global send message", queue the request and exit + // Question: is this a re-entrancy check? + // Answer: not really. If a machine sends out a "global send message", then we + // to completely process the "global send message" before any other + // "global send message" statements are executed. Suppose machine:A + // accepted two different messages. Machine:B sends out message:1 + // Machine:C also receives message:1 sent by machine:B, and sends out + // message:2. Since machine:C received message:1 before machine:A, now + // both message:1 and message:2 have been sent. Which does machine:A + // respond to? Queueing message:2 until message:1 has been completely + // processed ensures predictability. ie. In this case, machine:A + // will respond to message:1 before any machine responds to message:2. + // + + // Check to see if we are already in the middle of processing global messages + if (_GWS(myGlobalMessages)) { + + // Find the end of the global list + myGlobalMsgs = _GWS(myGlobalMessages); + while (myGlobalMsgs->next) { + myGlobalMsgs = myGlobalMsgs->next; + } + + //myGlobalMsgs is the last element, now tempGlobalMsg is. + myGlobalMsgs->next = tempGlobalMsg; + + // Since we are already processing a global message, this one is now queued, and we return + return; + } + + // We are not currently processing another global message, therefore put this on the queue + // To prevent future global requests from processing until this request is serviced + _GWS(myGlobalMessages) = tempGlobalMsg; + + // Loop through and service all global requests. + while (_GWS(myGlobalMessages)) { + // Sending to all machines, or just a bunch of them? + myCount = _GWS(myGlobalMessages)->msgCount; + if (myCount <= 0) { + sendToAll = true; + } else { + sendToAll = false; + } + + // Search machine list + more_to_send = true; + currMachine = _GWS(firstMachine); + while (currMachine && more_to_send) { + // Set nextXM up in case this machine is deleted during the ws_StepWhile + // nextXM will be maintained by ShutDownMachine() + _GWS(nextXM) = currMachine->next; + + // Have we got a machine of the specified hash + if (currMachine->myHash == _GWS(myGlobalMessages)->machHash) { + // Search the machines regular list. + found = SearchMsgList(msgHash, msgValue, currMachine, REGULAR_MSG, sendM); + + // If the message wasn't found in the regular list, search the persistent list + if (!found) { + found = SearchMsgList(msgHash, msgValue, currMachine, PERSISTENT_MSG, sendM); + } + + // Check to see if found + if (found) { + myCount--; + if ((!sendToAll) && (myCount <= 0)) { + more_to_send = false; + } + } + } + currMachine = _GWS(nextXM); + } + + // Note: ws_StepWhile could have added more messages to the proceedings + // Discard a global message and queue up the next one: + tempGlobalMsg = _GWS(myGlobalMessages); + _GWS(myGlobalMessages) = _GWS(myGlobalMessages)->next; + mem_free((void *)tempGlobalMsg); + } } } // End of namespace M4 diff --git a/engines/m4/wscript/ws_machine.h b/engines/m4/wscript/ws_machine.h index d4d2b8bc83c..c3668729071 100644 --- a/engines/m4/wscript/ws_machine.h +++ b/engines/m4/wscript/ws_machine.h @@ -97,11 +97,11 @@ struct Anim8 { Anim8 *behind = nullptr; Anim8 *myParent = nullptr; // The parent anim8 int32 sequHash = 0; // The current sequence Hash = 0; - Handle sequHandle = nullptr; // The sequence Handle + MemHandle sequHandle = nullptr; // The sequence Handle int32 pcOffset = 0; // The offset into the sequence of the current PC CCB *myCCB = nullptr; int32 dataHash = 0; // The array of data - Handle dataHandle = nullptr; + MemHandle dataHandle = nullptr; int32 dataOffset = 0; int32 startTime = 0; int32 switchTime = 0; @@ -125,7 +125,7 @@ struct machine { uint32 myHash = 0; uint32 machID = 0; char *machName = nullptr; - Handle machHandle = 0; + MemHandle machHandle = 0; int32 machInstrOffset = 0; int32 stateTableOffset = 0; int32 curState = 0; @@ -134,7 +134,7 @@ struct machine { Anim8 *myAnim8 = nullptr; Anim8 *parentAnim8 = nullptr; int32 dataHash = 0; - Handle dataHandle = 0; + MemHandle dataHandle = 0; int32 dataOffset = 0; int32 targetCount = 0; struct machine *msgReplyXM = nullptr; @@ -168,11 +168,26 @@ struct WSMachine_Globals { // Used for processing pCodes frac16 *_ws_globals = nullptr; + void *_addrExists = nullptr; }; extern bool ws_Initialize(frac16 *theGlobals); extern void ws_Shutdown(); -extern void TerminateMachinesByHash(int32 machHash); +extern void PauseEngines(); +extern void UnpauseEngines(); +extern void AddPauseTime(int32 myTime); + +void CycleEngines(Buffer *cleanBackground, int16 *depth_table, GrBuff *screenCodes, + uint8 *myPalette, uint8 *ICT, bool updateVideo); +void ws_RefreshWoodscriptBuffer(Buffer *cleanBackground, int16 *depth_table, GrBuff *screenCodes, uint8 *myPalette, uint8 *ICT); + +void TerminateMachine(machine *m); +void TerminateMachinesByHash(uint32 machHash); +bool VerifyMachineExists(machine *m); +int32 ws_KillMachines(); +void ws_StepWhile(machine *m, int32 pcOffset, int32 pcCount); +void IntoTheState(machine *m); +machine *TriggerMachineByHash(int32 myHash, Anim8 *parentAnim8, int32 dataHash, int32 dataRow, MessageCB CintrMsg, bool debug, const char *machName); /** * This proc is what allows a machine to send a message to another machine(s) @@ -180,6 +195,9 @@ extern void TerminateMachinesByHash(int32 machHash); extern void SendWSMessage(uint32 msgHash, frac16 msgValue, machine *recvM, uint32 machHash, machine *sendM, int32 msgCount); +#define kernel_spawn_machine(name,hash,callback) TriggerMachineByHash(hash, NULL, -1, -1, callback, FALSE, (char*)name) +#define kernel_terminate_machine(m) TerminateMachine(m) + } // End of namespace M4 #endif diff --git a/engines/m4/wscript/ws_timer.cpp b/engines/m4/wscript/ws_timer.cpp index 1618579a9e0..c0a2eb7dcba 100644 --- a/engines/m4/wscript/ws_timer.cpp +++ b/engines/m4/wscript/ws_timer.cpp @@ -27,18 +27,18 @@ namespace M4 { static void dispose_timeRequest(onTimeReq *timeReq); bool ws_InitWSTimer(void) { - _G(firstTimeReq) = nullptr; + _GWS(firstTimeReq) = nullptr; return true; } void ws_KillTime() { onTimeReq *tempTime; - tempTime = _G(firstTimeReq); + tempTime = _GWS(firstTimeReq); while (tempTime) { - _G(firstTimeReq) = _G(firstTimeReq)->next; + _GWS(firstTimeReq) = _GWS(firstTimeReq)->next; dispose_timeRequest(tempTime); - tempTime = _G(firstTimeReq); + tempTime = _GWS(firstTimeReq); } } @@ -48,4 +48,17 @@ static void dispose_timeRequest(onTimeReq *timeReq) { } } +void ws_MakeOnTimeReq(int32 wakeUpTime, machine *myXM, int32 pcOffset, int32 pcCount) { + error("TODO: ws_MakeOnTimeReq"); +} + +void ws_CancelOnTimeReqs(machine *m) { + error("TODO: ws_CancelOnTimeReqs"); +} + +void ws_CheckTimeReqs(int32 curTime) { + error("TODO: ws_CheckTimeReqs"); +} + + } // End of namespace M4 diff --git a/engines/m4/wscript/wst_regs.cpp b/engines/m4/wscript/wst_regs.cpp new file mode 100644 index 00000000000..4a0b734e624 --- /dev/null +++ b/engines/m4/wscript/wst_regs.cpp @@ -0,0 +1,122 @@ +/* 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 3 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, see . + * + */ + +#include "m4/wscript/wst_regs.h" + +namespace M4 { + +const char *myRegLabels[IDX_COUNT] = { + "timer", //0 + "tag", //1 + "layer", //2 + + "w", //3 + "h", //4 + "x", //5 + "y", //6 + "s", //7 + "r", //8 + + "cels_hash", //9 + "cels_index", //10 + "cels_count", //11 + "cels_frame_rate", //12 + "cels_pix_speed", //13 + + "targ_s", //14 + "targ_r", //15 + "targ_x", //16 + "targ_y", //17 + + "delta_s", //18 + "delta_r", //19 + "delta_x", //20 + "delta_y", //21 + + "velocity", //22 + "theta", //23 + + "ztemp1", //24 + "ztemp2", //25 + "ztemp3", //26 + "ztemp4", //27 + "ztemp5", //28 + "ztemp6", //29 + "ztemp7", //30 + "ztemp8", //31 + + "mach_id" //32 +}; + +const char *myGlobLabels[GLOB_COUNT] = { + "***SYSTEM - TIME", + "***SYSTEM - WATCHDOG", + "min_y", + "max_y", + "min_scale", + "max_scale", + "scaler", + "g_temp1", + "g_temp2", + "g_temp3", + "g_temp4", + "g_temp5", + "g_temp6", + "g_temp7", + "g_temp8", + "g_temp9", + "g_temp10", + "g_temp11", + "g_temp12", + "g_temp13", + "g_temp14", + "g_temp15", + "g_temp16", + "g_temp17", + "g_temp18", + "g_temp19", + "g_temp20", + "g_temp21", + "g_temp22", + "g_temp23", + "g_temp24", + "g_temp25", + "g_temp26", + "g_temp27", + "g_temp28", + "g_temp29", + "g_temp30", + "g_temp31", + "g_temp32", +}; + +const char *tagLabels[TAG_COUNT] = { + "tag_none", + "tag_targs", + "tag_bez", + "tag_deltas", + "tag_vectors", + "tag_text", + "tag_move_cel", + "tag_map_cel" +}; + +} // End of namespace M4 diff --git a/engines/m4/wscript/wst_regs.h b/engines/m4/wscript/wst_regs.h index cd563389fd4..9bb7a61ec8b 100644 --- a/engines/m4/wscript/wst_regs.h +++ b/engines/m4/wscript/wst_regs.h @@ -85,9 +85,11 @@ enum { #define TAG_MOVE_CEL 0x00004000 #define TAG_MAP_CEL 0x00002000 -extern char *myRegLabels[]; -extern char *myGlobLabels[]; -extern char *tagLabels[]; +#define GLOB_COUNT 39 + +extern const char *myRegLabels[]; +extern const char *myGlobLabels[]; +extern const char *tagLabels[]; } // End of namespace M4