mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
791 lines
25 KiB
C++
791 lines
25 KiB
C++
/* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "m4/wscript/ws_load.h"
|
|
#include "m4/wscript/ws_machine.h"
|
|
#include "m4/core/errors.h"
|
|
#include "m4/core/imath.h"
|
|
#include "m4/graphics/graphics.h"
|
|
#include "m4/mem/mem.h"
|
|
#include "m4/globals.h"
|
|
|
|
namespace M4 {
|
|
|
|
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);
|
|
|
|
bool InitWSAssets() {
|
|
int32 i;
|
|
|
|
// Make sure this is only called once.
|
|
if (_G(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) {
|
|
return false;
|
|
}
|
|
if ((_G(globalMACHHandles) = (Handle *)mem_alloc(sizeof(Handle) * 256, "CELS Handles table")) == nullptr) {
|
|
return false;
|
|
}
|
|
if ((_G(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;
|
|
}
|
|
|
|
// SEQUence tables
|
|
if ((_G(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) {
|
|
return false;
|
|
}
|
|
if ((_G(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;
|
|
}
|
|
|
|
// DATA tables
|
|
if ((_G(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) {
|
|
return false;
|
|
}
|
|
if ((_G(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;
|
|
}
|
|
|
|
// CELS tables
|
|
if ((_G(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) {
|
|
return false;
|
|
}
|
|
if ((_G(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) {
|
|
return false;
|
|
}
|
|
for (i = 0; i < 256; i++) {
|
|
_G(globalCELSnames)[i] = nullptr;
|
|
_G(globalCELSHandles)[i] = nullptr;
|
|
_G(globalCELSoffsets)[i] = -1;
|
|
_G(globalCELSPaloffsets)[i] = -1;
|
|
}
|
|
|
|
//set the global to indicate the loader is active
|
|
_G(wsloaderInitialized) = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ClearWSAssets(uint32 assetType, int32 minHash, int32 maxHash) {
|
|
int32 i;
|
|
|
|
if (!_G(wsloaderInitialized)) {
|
|
return false;
|
|
}
|
|
|
|
// Bounds checking
|
|
if (minHash < 0)
|
|
minHash = 0;
|
|
if (maxHash > MAX_ASSET_HASH)
|
|
maxHash = MAX_ASSET_HASH;
|
|
|
|
switch (assetType) {
|
|
case _WS_ASSET_MACH:
|
|
//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;
|
|
}
|
|
}
|
|
break;
|
|
|
|
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;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _WS_ASSET_DATA:
|
|
//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;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case _WS_ASSET_CELS:
|
|
//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;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ShutdownWSAssets(void) {
|
|
if (!_G(wsloaderInitialized))
|
|
return;
|
|
|
|
// For each asset type, clear the entire table
|
|
ClearWSAssets(_WS_ASSET_MACH, 0, MAX_ASSET_HASH);
|
|
ClearWSAssets(_WS_ASSET_SEQU, 0, MAX_ASSET_HASH);
|
|
ClearWSAssets(_WS_ASSET_CELS, 0, MAX_ASSET_HASH);
|
|
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 (_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));
|
|
|
|
_G(wsloaderInitialized) = false;
|
|
}
|
|
|
|
bool LoadWSAssets(const char *wsAssetName, RGB8 *myPalette) {
|
|
MemHandle workHandle;
|
|
char *mainAssetPtr, *parseAssetPtr, *endOfAssetBlock;
|
|
uint32 *tempPtr;
|
|
uint32 *chunkType, *chunkSize, *chunkHash;
|
|
bool finished, chunkSwap;
|
|
int32 *celsPtr, *palPtr;
|
|
uint32 i;
|
|
int32 assetSize;
|
|
|
|
// Check that the loader has been initialized
|
|
if (!_G(wsloaderInitialized)) {
|
|
error_show(FL, 'WSLI');
|
|
}
|
|
|
|
// Use the resource io manager to read in the entire block
|
|
if ((workHandle = rget(wsAssetName, &assetSize)) == nullptr) {
|
|
error_show(FL, 'FNF!', "Asset Name: %s", wsAssetName);
|
|
}
|
|
|
|
// Lock the handle so we can step through the chunk
|
|
HLock(workHandle);
|
|
mainAssetPtr = (char *)(*workHandle);
|
|
|
|
endOfAssetBlock = (char *)((uint32)mainAssetPtr + (uint32)assetSize);
|
|
|
|
parseAssetPtr = mainAssetPtr;
|
|
|
|
// Set the finished flag
|
|
finished = false;
|
|
|
|
// Get the first chunkType
|
|
if (!GetNextint32(&parseAssetPtr, endOfAssetBlock, &chunkType)) {
|
|
finished = true;
|
|
}
|
|
|
|
// Process each chunk according to type
|
|
while (!finished) {
|
|
// Read in the chunk size and hash number
|
|
if (!GetNextint32(&parseAssetPtr, endOfAssetBlock, &chunkSize)) {
|
|
error_show(FL, 'WSLE', "Asset Name: %s", wsAssetName);
|
|
}
|
|
if (!GetNextint32(&parseAssetPtr, endOfAssetBlock, &chunkHash)) {
|
|
error_show(FL, 'WSLE', "Asset Name: %s", wsAssetName);
|
|
}
|
|
|
|
// Process the chunk according to type
|
|
chunkSwap = false;
|
|
switch (*chunkType) {
|
|
// Chunk is a machine chunk
|
|
case CHUNK_HCAM:
|
|
// Byte swap the type, size and hash and continue through case CHUNK_MACH.
|
|
*chunkType = SWAP_INT32(*chunkType);
|
|
*chunkSize = SWAP_INT32(*chunkSize);
|
|
*chunkHash = SWAP_INT32(*chunkHash);
|
|
chunkSwap = true;
|
|
|
|
case CHUNK_MACH:
|
|
// Check the validity of the machine hash number, and clear it
|
|
if (*chunkHash > MAX_ASSET_HASH) {
|
|
error_show(FL, 'WSLA', "Asset Name: %s, MACH hash was: %ld", wsAssetName, *chunkHash);
|
|
}
|
|
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] = (int32)parseAssetPtr - (int32)mainAssetPtr;
|
|
|
|
// Check that the assetblocksize is big enough that the chunk body was read in...
|
|
if ((endOfAssetBlock - parseAssetPtr) < (int)(*chunkSize - 12)) {
|
|
error_show(FL, 'WSLE', "Asset Name: %s, MACH hash was: %ld", wsAssetName, *chunkHash);
|
|
}
|
|
|
|
// Byteswap the entire machine if necessary
|
|
if (chunkSwap) {
|
|
tempPtr = (uint32 *)parseAssetPtr;
|
|
for (i = 0; i < (*chunkSize - 12) >> 2; i++) { //>>2 - chunkSize is bytes, not int32s
|
|
*tempPtr = SWAP_INT32(*tempPtr);
|
|
tempPtr++;
|
|
}
|
|
}
|
|
|
|
// Update the assetPtr to the beginning of the next chunk
|
|
parseAssetPtr += *chunkSize - 12;
|
|
|
|
break;
|
|
|
|
// Chunk is a machine chunk
|
|
case CHUNK_UQES:
|
|
// Byte swap the type, size and hash and continue through case CHUNK_SEQU.
|
|
*chunkType = SWAP_INT32(*chunkType);
|
|
*chunkSize = SWAP_INT32(*chunkSize);
|
|
*chunkHash = SWAP_INT32(*chunkHash);
|
|
chunkSwap = true;
|
|
|
|
case CHUNK_SEQU:
|
|
// Check the validity of the sequence hash number, and clear it
|
|
if (*chunkHash > MAX_ASSET_HASH) {
|
|
error_show(FL, 'WSLA', "Asset Name: %s, SEQU hash was: %ld", wsAssetName, *chunkHash);
|
|
}
|
|
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;
|
|
|
|
// Check that the assetblocksize is big enough that the chunk body was read in...
|
|
if ((endOfAssetBlock - parseAssetPtr) < (int)(*chunkSize - 12)) {
|
|
error_show(FL, 'WSLE', "Asset Name: %s, SEQU hash was: %ld", wsAssetName, *chunkHash);
|
|
}
|
|
|
|
// Byteswap the entire sequence if necessary
|
|
if (chunkSwap) {
|
|
tempPtr = (uint32 *)parseAssetPtr;
|
|
for (i = 0; i < (*chunkSize - 12) >> 2; i++) { //>>2 - chunkSize is bytes, not int32s
|
|
*tempPtr = SWAP_INT32(*tempPtr);
|
|
tempPtr++;
|
|
}
|
|
}
|
|
|
|
// Update the assetPtr to the beginning of the next chunk
|
|
parseAssetPtr += *chunkSize - 12;
|
|
break;
|
|
|
|
// Chunk is a data chunk
|
|
case CHUNK_ATAD:
|
|
// Byte swap the type, size and hash and continue through case CHUNK_DATA.
|
|
*chunkType = SWAP_INT32(*chunkType);
|
|
*chunkSize = SWAP_INT32(*chunkSize);
|
|
*chunkHash = SWAP_INT32(*chunkHash);
|
|
chunkSwap = true;
|
|
|
|
case CHUNK_DATA:
|
|
// Check the validity of the data block hash number, and clear it
|
|
if (*chunkHash > MAX_ASSET_HASH) {
|
|
error_show(FL, 'WSLA', "Asset Name: %s, DATA hash was: %ld", wsAssetName, *chunkHash);
|
|
}
|
|
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;
|
|
|
|
// Check that the assetblocksize is big enough that the chunk body was read in...
|
|
if ((endOfAssetBlock - parseAssetPtr) < (int)(*chunkSize - 12)) {
|
|
error_show(FL, 'WSLE', "Asset Name: %s, DATA hash was: %ld", wsAssetName, *chunkHash);
|
|
}
|
|
|
|
// Byteswap the entire data block if necessary
|
|
if (chunkSwap) {
|
|
tempPtr = (uint32 *)parseAssetPtr;
|
|
for (i = 0; i < (*chunkSize - 12) >> 2; i++) { //>>2 - chunkSize is bytes, not int32s
|
|
*tempPtr = SWAP_INT32(*tempPtr);
|
|
tempPtr++;
|
|
}
|
|
}
|
|
|
|
// Update the assetPtr to the beginning of the next chunk
|
|
parseAssetPtr += *chunkSize - 12;
|
|
break;
|
|
|
|
case CHUNK_SLEC:
|
|
// Byte swap the type, size and hash and continue through case CHUNK_CELS.
|
|
*chunkType = SWAP_INT32(*chunkType);
|
|
*chunkSize = SWAP_INT32(*chunkSize);
|
|
*chunkHash = SWAP_INT32(*chunkHash);
|
|
chunkSwap = true;
|
|
|
|
case CHUNK_CELS:
|
|
// Check the validity of the cels hash number, and clear it
|
|
if (*chunkHash > MAX_ASSET_HASH) {
|
|
error_show(FL, 'WSLA', "Asset Name: %s, CELS hash was: %ld", wsAssetName, *chunkHash);
|
|
}
|
|
ClearWSAssets(_WS_ASSET_CELS, *chunkHash, *chunkHash);
|
|
|
|
// Store the resource name
|
|
_G(globalCELSnames)[*chunkHash] = mem_strdup(wsAssetName);
|
|
|
|
// Process the SS from the stream file
|
|
if (ProcessCELS(wsAssetName, &parseAssetPtr, mainAssetPtr, endOfAssetBlock, &celsPtr, &palPtr, myPalette) < 0) {
|
|
error_show(FL, 'WSLP', "Asset Name: %s, CELS hash was: %ld", wsAssetName, *chunkHash);
|
|
}
|
|
|
|
// 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;
|
|
if (celsPtr) {
|
|
_G(globalCELSoffsets)[*chunkHash] = (int32)celsPtr - (int32)mainAssetPtr;
|
|
} else {
|
|
_G(globalCELSoffsets)[*chunkHash] = -1;
|
|
}
|
|
if (palPtr) {
|
|
_G(globalCELSPaloffsets)[*chunkHash] = (int32)palPtr - (int32)mainAssetPtr;
|
|
} else {
|
|
_G(globalCELSPaloffsets)[*chunkHash] = -1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error_show(FL, 'WSLT', "Asset Name: %s, %ld bytes into the file.", wsAssetName,
|
|
(int32)parseAssetPtr - 12 - (int32)mainAssetPtr);
|
|
break;
|
|
}
|
|
|
|
// Read the next chunkType, or signal we are finished
|
|
if (!GetNextint32(&parseAssetPtr, endOfAssetBlock, &chunkType)) {
|
|
finished = true;
|
|
}
|
|
}
|
|
|
|
// Unlock the handle now
|
|
HUnLock(workHandle);
|
|
return true;
|
|
}
|
|
|
|
M4sprite *CreateSprite(MemHandle resourceHandle, int32 handleOffset, int32 index, M4sprite *mySprite, bool *streamSeries) {
|
|
uint32 *myCelSource, *data, *offsets, numCels;
|
|
uint32 *celsPtr;
|
|
|
|
// Parameter verification
|
|
if ((!resourceHandle) || (!*resourceHandle)) {
|
|
ws_LogErrorMsg(FL, "No sprite source in memory.");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mySprite) {
|
|
mySprite = (M4sprite *)mem_alloc(sizeof(M4sprite), "Sprite");
|
|
if (!mySprite) {
|
|
ws_LogErrorMsg(FL, "Out of memory - mem requested: %ld.", sizeof(M4sprite));
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// Find the cels source from the asset block
|
|
HLock(resourceHandle);
|
|
celsPtr = (uint32 *)((int32)*resourceHandle + handleOffset);
|
|
|
|
// Check that the index into the series requested is within a valid range
|
|
numCels = celsPtr[CELS_COUNT];
|
|
if (index >= (int)numCels) {
|
|
ws_LogErrorMsg(FL, "Sprite index out of range - max index: %ld, requested index: %ld", numCels - 1, index);
|
|
return nullptr;
|
|
}
|
|
|
|
// Find the offset table, and the beginning of the data for all sprites
|
|
offsets = &celsPtr[CELS_OFFSETS];
|
|
data = &celsPtr[CELS_OFFSETS + numCels];
|
|
|
|
// Find the sprite data for the specific sprite in the series
|
|
myCelSource = (uint32 *)((uint32)data + offsets[index]);
|
|
|
|
// Set the stream boolean
|
|
if (streamSeries) {
|
|
if (myCelSource[CELS_STREAM])
|
|
*streamSeries = true;
|
|
else
|
|
*streamSeries = false;
|
|
}
|
|
|
|
// Initialize the sprite struct and return it
|
|
mySprite->sourceHandle = resourceHandle;
|
|
mySprite->xOffset = (int32)myCelSource[CELS_X];
|
|
mySprite->yOffset = (int32)myCelSource[CELS_Y];
|
|
mySprite->w = (int32)myCelSource[CELS_W];
|
|
mySprite->h = (int32)myCelSource[CELS_H];
|
|
mySprite->encoding = (uint8)myCelSource[CELS_COMP];
|
|
mySprite->data = (uint8 *)&myCelSource[CELS_DATA];
|
|
|
|
if ((mySprite->w > 0) && (mySprite->h > 0)) {
|
|
mySprite->sourceOffset = (int32)((int32)(mySprite->data) - (int32)*resourceHandle);
|
|
} else {
|
|
mySprite->sourceOffset = 0;
|
|
}
|
|
|
|
// This value MUST be set before sprite draws are called after the block has been locked
|
|
mySprite->data = nullptr;
|
|
|
|
// Unlock the handle
|
|
HUnLock(resourceHandle);
|
|
|
|
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)) == NULL)
|
|
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 file size
|
|
assetSize = f.size();
|
|
|
|
// Create a handle big enough to hold the contents of the file
|
|
if ((workHandle = NewHandle(assetSize, "ss file")) == nullptr)
|
|
return -1;
|
|
|
|
// Lock the handle and read the contents of the file intoit
|
|
HLock(workHandle);
|
|
mainAssetPtr = (char *)*workHandle;
|
|
if (f.read(mainAssetPtr, assetSize) < assetSize)
|
|
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;
|
|
}
|
|
|
|
static bool GetNextint32(char **assetPtr, char *endOfAssetBlock, uint32 **returnVal) {
|
|
// Check to see if we still have an int32 available
|
|
if ((endOfAssetBlock - *assetPtr) < 4) {
|
|
return false;
|
|
}
|
|
|
|
// Get the next int32
|
|
*returnVal = (uint32 *)*assetPtr;
|
|
*assetPtr += 4;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int32 ProcessCELS(const char * /*assetName*/, char **parseAssetPtr, char * /*mainAssetPtr*/, char *endOfAssetBlock,
|
|
int32 **dataOffset, int32 **palDataOffset, RGB8 *myPalette) {
|
|
uint32 *celsType, *numColors, *palData;
|
|
uint32 *tempPtr, *celsSize, *data, *dataPtr, *offsetPtr, i, j, *header, *format;
|
|
bool byteSwap;
|
|
|
|
if (!_G(wsloaderInitialized))
|
|
return -1;
|
|
|
|
*dataOffset = nullptr;
|
|
*palDataOffset = nullptr;
|
|
|
|
// Get the header and the format
|
|
if (!GetNextint32(parseAssetPtr, endOfAssetBlock, &header)) {
|
|
ws_LogErrorMsg(FL, "Unable to get the SS header");
|
|
return -1;
|
|
}
|
|
if (!GetNextint32(parseAssetPtr, endOfAssetBlock, &format)) {
|
|
ws_LogErrorMsg(FL, "Unable to get the SS format");
|
|
return -1;
|
|
}
|
|
|
|
// Make sure the file is tagged "M4SS" (or "SS4M")
|
|
if (*header == HEAD_SS4M) {
|
|
*header = SWAP_INT32(*header);
|
|
*format = SWAP_INT32(*format);
|
|
} else if (*header != HEAD_M4SS) {
|
|
ws_LogErrorMsg(FL, "SS chunk is not a valid M4SS chunk.");
|
|
return -1;
|
|
}
|
|
|
|
// Verify the format is recent. This is a version control
|
|
if (*format < SS_FORMAT) {
|
|
ws_LogErrorMsg(FL, "Format is antique and cannot be read - rebuild series.");
|
|
return -1;
|
|
}
|
|
|
|
// Get the CELS chunk type - either PAL information, or the actual SS info
|
|
if (!GetNextint32(parseAssetPtr, endOfAssetBlock, &celsType)) {
|
|
ws_LogErrorMsg(FL, "Unable to read the SS chunk type.");
|
|
return -1;
|
|
}
|
|
|
|
byteSwap = false;
|
|
// If the chunk is PAL info - celsType == CELS_LAP_ indicates the chunk needs to be byte-swapped.
|
|
if ((*celsType == CELS__PAL) || (*celsType == CELS_LAP_)) {
|
|
|
|
// Read the chunk size, and the number of palette colors, and byte-swap if necessary
|
|
if (!GetNextint32(parseAssetPtr, endOfAssetBlock, &celsSize)) {
|
|
ws_LogErrorMsg(FL, "Unable to read the SS PAL chunk size.");
|
|
return -1;
|
|
}
|
|
if (!GetNextint32(parseAssetPtr, endOfAssetBlock, &numColors)) {
|
|
ws_LogErrorMsg(FL, "Unable to read the SS PAL number of colors.");
|
|
return -1;
|
|
}
|
|
|
|
if (*celsType == CELS_LAP_) {
|
|
*celsType = SWAP_INT32(*celsType);
|
|
*celsSize = SWAP_INT32(*celsSize);
|
|
*numColors = SWAP_INT32(*numColors);
|
|
byteSwap = true;
|
|
}
|
|
|
|
// Verify that we actually got legitimate values
|
|
if (((int32)(*celsSize) <= 0) || ((int32)(*numColors) <= 0)) {
|
|
ws_LogErrorMsg(FL, "Pal info has been corrupted");
|
|
return -1;
|
|
}
|
|
|
|
// The asset block offset for palData should begin with the number of colors
|
|
*palDataOffset = (int32 *)numColors;
|
|
palData = (uint32 *)numColors;
|
|
|
|
if (((int32)endOfAssetBlock - (int32)(*parseAssetPtr)) < ((int32)(*celsSize) - 8)) {
|
|
ws_LogErrorMsg(FL, "Pal info is larger than asset block.");
|
|
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 *)&numColors[1];
|
|
for (i = 0; i < *numColors; i++) {
|
|
*tempPtr++ = SWAP_INT32(*tempPtr);
|
|
}
|
|
}
|
|
|
|
*parseAssetPtr += *numColors << 2;
|
|
|
|
// The palette info has been processed, now it can be stored
|
|
if (myPalette) {
|
|
tempPtr = (uint32 *)(&palData[1]);
|
|
for (i = 0; i < (uint32)*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++;
|
|
}
|
|
}
|
|
|
|
byteSwap = false;
|
|
// Pal chunk has been processed, get the next chunk type
|
|
if (!GetNextint32(parseAssetPtr, endOfAssetBlock, &celsType)) {
|
|
ws_LogErrorMsg(FL, "Unable to read the SS chunk type.");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// The chunk header must now be the SS information (possibly byte-swapped)
|
|
if ((*celsType != CELS___SS) && (*celsType != CELS_SS__)) {
|
|
ws_LogErrorMsg(FL, "SS chunk type is invalid.");
|
|
return -1;
|
|
}
|
|
|
|
// Read in the chunk size
|
|
if (!GetNextint32(parseAssetPtr, endOfAssetBlock, &celsSize)) {
|
|
ws_LogErrorMsg(FL, "Unable to read the SS chunk size.");
|
|
return -1;
|
|
}
|
|
|
|
// Byteswap if necessary
|
|
if (*celsType == CELS_SS__) {
|
|
*celsType = SWAP_INT32(*celsType);
|
|
*celsSize = SWAP_INT32(*celsSize);
|
|
byteSwap = true;
|
|
}
|
|
|
|
// The asset block offset for the cel should begin with the celsType
|
|
*dataOffset = (int32 *)celsType;
|
|
data = (uint32 *)celsType;
|
|
|
|
// Verify that we actually got legitimate values
|
|
if ((int32)(*celsSize) <= 0) {
|
|
ws_LogErrorMsg(FL, "SS info has been corrupted");
|
|
return -1;
|
|
}
|
|
|
|
if (((int32)endOfAssetBlock - (int32)data) < (int32)*celsSize) {
|
|
ws_LogErrorMsg(FL, "SS info is larger than asset block.");
|
|
return -1;
|
|
}
|
|
|
|
// Check to see if we need to byte-swap the header information.
|
|
if (byteSwap) {
|
|
|
|
// The chunk header begins at (*data)[2]
|
|
// byte-swap the entire chunk header
|
|
tempPtr = &(data[2]);
|
|
for (i = 0; i < SS_HEAD_SIZE - 2; i++) {
|
|
*tempPtr++ = SWAP_INT32(*tempPtr);
|
|
}
|
|
|
|
if ((int32)(data[CELS_COUNT]) <= 0) {
|
|
ws_LogErrorMsg(FL, "SS info has been corrupted");
|
|
return -1;
|
|
}
|
|
|
|
// The chunk header has been byteswapped, now we must byte-swap the table of
|
|
// offsets into the chunk, which indicate where each individual sprite can be found.
|
|
offsetPtr = &(data[CELS_OFFSETS]);
|
|
tempPtr = offsetPtr;
|
|
for (i = 0; i < data[CELS_COUNT]; i++) {
|
|
*tempPtr++ = SWAP_INT32(*tempPtr);
|
|
}
|
|
|
|
// dataPtr points to the beginning of the block which is a concatenation of
|
|
// all the sprites. Loop through and byteswap each individual sprite header.
|
|
dataPtr = tempPtr;
|
|
for (i = 0; i < data[CELS_COUNT]; i++) {
|
|
|
|
// The beginning of sprite i is the dataPtr + the number of bytes in the offset table
|
|
tempPtr = (uint32 *)((uint32)dataPtr + offsetPtr[i]);
|
|
|
|
// Byteswap the individual sprite's header
|
|
for (j = 0; j < SS_INDV_HEAD; j++) {
|
|
*tempPtr++ = SWAP_INT32(*tempPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the size of the chunk containing the sprite series info
|
|
return *celsSize;
|
|
}
|
|
|
|
} // End of namespace M4
|