Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
912 lines
22 KiB
C++
912 lines
22 KiB
C++
#include "munga.h"
|
|
#include "windows.h"
|
|
#pragma hdrstop
|
|
|
|
#include "resource.h"
|
|
#include "resver.h"
|
|
|
|
ResourceDescription::ResourceDescription(
|
|
ResourceFile *file,
|
|
const char* name,
|
|
ResourceDescription::ResourceType type,
|
|
int priority,
|
|
LWord flags,
|
|
const void* address,
|
|
size_t size,
|
|
ResourceDescription::ResourceID index
|
|
)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(name);
|
|
|
|
resourceFile = file;
|
|
resourceID = index;
|
|
resourceType = type;
|
|
Str_Copy(resourceName, name, sizeof(resourceName));
|
|
for (int i = strlen(resourceName) + 1; i<sizeof(resourceName); ++i)
|
|
{
|
|
resourceName[i] = '\0';
|
|
}
|
|
|
|
downloadPriority = priority;
|
|
resourceFlags = flags;
|
|
offsetInFile = 0;
|
|
resourceSize = size;
|
|
resourceAddress = NULL;
|
|
lockCount = 0;
|
|
|
|
if (address)
|
|
{
|
|
Check_Pointer(address);
|
|
resourceAddress = new char[resourceSize];
|
|
Register_Pointer(resourceAddress);
|
|
Mem_Copy(resourceAddress, address, resourceSize, resourceSize);
|
|
SetLoaded();
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription::~ResourceDescription()
|
|
{
|
|
Check(this);
|
|
// Verify(!IsLocked());
|
|
if (resourceAddress)
|
|
{
|
|
Unregister_Pointer(resourceAddress);
|
|
delete[] resourceAddress;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ResourceDescription::ReleaseUnlockedResource()
|
|
{
|
|
Check(this);
|
|
if (!IsLocked())
|
|
{
|
|
if (IsLoaded())
|
|
{
|
|
SetNotLoaded();
|
|
Verify(resourceAddress);
|
|
Unregister_Pointer(resourceAddress);
|
|
delete[] resourceAddress;
|
|
resourceAddress = NULL;
|
|
}
|
|
Verify(!resourceAddress);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ResourceDescription::LoadResource()
|
|
{
|
|
Check(this);
|
|
if (!IsLoaded())
|
|
{
|
|
Verify(!resourceAddress);
|
|
Check(resourceFile);
|
|
resourceFile->LoadResource(this);
|
|
Verify(resourceAddress);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
ResourceDescription::TestInstance() const
|
|
{
|
|
return True;
|
|
}
|
|
|
|
//#############################################################################
|
|
//########################## ResourceFile ###############################
|
|
//#############################################################################
|
|
|
|
//#############################################################################
|
|
// ResourceFile CONSTRUCTORS & DESTRUCTORS
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceFile::ResourceFile():
|
|
labOnly(0),
|
|
resourceArray(NULL),
|
|
resourceArraySize(0),
|
|
maxResourceID(ResourceDescription::NullResourceID),
|
|
lastPreallocatedResourceID(ResourceDescription::NullResourceID)
|
|
{
|
|
versionArray[0] = RESOURCE_FORMAT_VERSION;
|
|
versionArray[1] = 0;
|
|
versionArray[2] = 0;
|
|
versionArray[3] = 0;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceFile::~ResourceFile()
|
|
{
|
|
if (resourceArray)
|
|
{
|
|
for (int i=maxResourceID; i>=0; i--)
|
|
{
|
|
if (resourceArray[i])
|
|
{
|
|
Unregister_Object(resourceArray[i]);
|
|
delete resourceArray[i];
|
|
}
|
|
}
|
|
Unregister_Pointer(resourceArray);
|
|
delete[] resourceArray;
|
|
}
|
|
|
|
versionArray[0] = RESOURCE_FORMAT_VERSION;
|
|
versionArray[1] = 0;
|
|
versionArray[2] = 0;
|
|
versionArray[3] = 0;
|
|
labOnly = 0;
|
|
resourceArraySize = 0;
|
|
maxResourceID = ResourceDescription::NullResourceID;
|
|
resourceArray = NULL;
|
|
}
|
|
|
|
//##############################################################################
|
|
// ResourceFile METHODS
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription*
|
|
ResourceFile::AddResource(
|
|
const char* name,
|
|
ResourceDescription::ResourceType type,
|
|
int priority,
|
|
LWord load_flag,
|
|
const void* address,
|
|
size_t size,
|
|
ResourceDescription::ResourceID index
|
|
)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(name);
|
|
|
|
int i;
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// If we asked for the next available ID number, find out what it should be
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
if (index == ResourceDescription::NullResourceID)
|
|
{
|
|
index = lastPreallocatedResourceID + 1;
|
|
if (resourceArray)
|
|
{
|
|
Check_Pointer(resourceArray);
|
|
for (; index<=resourceArraySize; ++index)
|
|
{
|
|
if (!resourceArray[index])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// Check to see if the resource index table needs to be created
|
|
//-------------------------------------------------------------
|
|
//
|
|
if (!resourceArray)
|
|
{
|
|
resourceArraySize = index + IndexBlockSize;
|
|
resourceArray = new ResourceDescription* [resourceArraySize];
|
|
Register_Pointer(resourceArray);
|
|
for (i=0; i<resourceArraySize; ++i)
|
|
{
|
|
resourceArray[i] = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------
|
|
// Otherwise, check to see if we have to grow the index table
|
|
//-----------------------------------------------------------
|
|
//
|
|
if (index >= resourceArraySize)
|
|
{
|
|
//
|
|
//----------------------------------------
|
|
// Allocate a new table for the main index
|
|
//----------------------------------------
|
|
//
|
|
ResourceDescription **new_index =
|
|
new ResourceDescription* [index + IndexBlockSize];
|
|
Register_Pointer(new_index);
|
|
|
|
//
|
|
//------------------------------------
|
|
// Copy the old table into the new one
|
|
//------------------------------------
|
|
//
|
|
Check_Pointer(resourceArray);
|
|
for (i=0; i<resourceArraySize; ++i)
|
|
{
|
|
new_index[i] = resourceArray[i];
|
|
}
|
|
|
|
//
|
|
//---------------------
|
|
// Delete the old table
|
|
//---------------------
|
|
//
|
|
Unregister_Pointer(resourceArray);
|
|
delete[] resourceArray;
|
|
resourceArray = new_index;
|
|
resourceArraySize = index + IndexBlockSize;
|
|
|
|
//
|
|
//----------------------------------
|
|
// NULL the new entries in the table
|
|
//----------------------------------
|
|
//
|
|
while (i<resourceArraySize)
|
|
{
|
|
resourceArray[i++] = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Add the new resource in at the specified index
|
|
//-----------------------------------------------
|
|
//
|
|
|
|
Check_Pointer(resourceArray);
|
|
Verify(!resourceArray[index]);
|
|
if (index > maxResourceID)
|
|
{
|
|
maxResourceID = index;
|
|
}
|
|
resourceArray[index] =
|
|
new ResourceDescription(
|
|
this,
|
|
name,
|
|
type,
|
|
priority,
|
|
load_flag,
|
|
address,
|
|
size,
|
|
index
|
|
);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// The caller of this function is responsible for registering the memory
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Register_Object(resourceArray[index]);
|
|
return resourceArray[index];
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription*
|
|
ResourceFile::AddResourceList(
|
|
const char* name,
|
|
ResourceDescription::ResourceType type,
|
|
int priority,
|
|
LWord load_flag,
|
|
const ResourceDescription::ResourceID* address,
|
|
int count,
|
|
ResourceDescription::ResourceID index
|
|
)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(name);
|
|
Check_Pointer(address);
|
|
|
|
//
|
|
//--------------------------------------------------------
|
|
// prefix the directory table with the count of references
|
|
//--------------------------------------------------------
|
|
//
|
|
ResourceDescription::ResourceID *table =
|
|
new ResourceDescription::ResourceID[count+1];
|
|
Register_Pointer(table);
|
|
for (int i=0; i<count; ++i)
|
|
{
|
|
table[i+1] = address[i];
|
|
}
|
|
*table = count;
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Go ahead and add the directory to the main index
|
|
//-------------------------------------------------
|
|
//
|
|
ResourceDescription *directory =
|
|
AddResource(
|
|
name,
|
|
type,
|
|
priority,
|
|
load_flag,
|
|
table,
|
|
(count+1) * sizeof(ResourceDescription::ResourceID),
|
|
index
|
|
);
|
|
Unregister_Pointer(table);
|
|
delete[] table;
|
|
|
|
return directory;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription*
|
|
ResourceFile::AddResourceMemoryStream(
|
|
const char* name,
|
|
ResourceDescription::ResourceType type,
|
|
int priority,
|
|
LWord load_flag,
|
|
MemoryStream* memory_stream,
|
|
ResourceDescription::ResourceID index
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(memory_stream);
|
|
|
|
size_t size = memory_stream->GetBytesUsed();
|
|
memory_stream->Rewind();
|
|
|
|
return
|
|
AddResource(
|
|
name,
|
|
type,
|
|
priority,
|
|
load_flag,
|
|
memory_stream->GetPointer(),
|
|
size,
|
|
index
|
|
);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ResourceFile::DeleteResource(ResourceDescription *resource)
|
|
{
|
|
Check(this);
|
|
Check(resource);
|
|
Check_Pointer(resourceArray);
|
|
Verify(!resource->IsLocked());
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Delete pointer from resoureArray if it was there
|
|
//------------------------------------------------------------------------
|
|
//
|
|
if (resource->resourceID != ResourceDescription::NullResourceID )
|
|
{
|
|
resourceArray[resource->resourceID] = NULL;
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Find new maxResourceID
|
|
//------------------------------------------------------------------------
|
|
//
|
|
if (resource->resourceID == maxResourceID )
|
|
{
|
|
ResourceDescription::ResourceID i;
|
|
for (i = maxResourceID; i>=0; i--)
|
|
{
|
|
if(resourceArray[i] != NULL)
|
|
{
|
|
Check(resourceArray[i]);
|
|
break;
|
|
}
|
|
}
|
|
maxResourceID = i;
|
|
}
|
|
}
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// The caller of this function is responsible for unregistering the memory
|
|
//------------------------------------------------------------------------
|
|
//
|
|
|
|
delete resource;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription*
|
|
ResourceFile::FindResourceDescription(ResourceDescription::ResourceID index)
|
|
{
|
|
Check(this);
|
|
|
|
if (index<0 || index>maxResourceID || !resourceArray)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Check_Pointer(resourceArray);
|
|
ResourceDescription *res = resourceArray[index];
|
|
return res;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription*
|
|
ResourceFile::FindResourceDescription(
|
|
const char* name,
|
|
ResourceDescription::ResourceType type,
|
|
ResourceDescription::ResourceID after
|
|
)
|
|
{
|
|
Check(this);
|
|
|
|
ResourceDescription *desc;
|
|
ResourceDescription::ResourceID i;
|
|
for (i=after+1; i<=maxResourceID; ++i)
|
|
{
|
|
desc = FindResourceDescription(i);
|
|
if (desc)
|
|
{
|
|
Check(desc);
|
|
if (
|
|
type == desc->resourceType &&
|
|
(!name || !strcmp(name, desc->resourceName))
|
|
)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (i > maxResourceID)
|
|
{
|
|
desc = NULL;
|
|
}
|
|
return desc;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription*
|
|
ResourceFile::SearchList(
|
|
ResourceDescription::ResourceID list_id,
|
|
ResourceDescription::ResourceType type
|
|
)
|
|
{
|
|
ResourceDescription *list_res = FindResourceDescription(list_id);
|
|
if (list_res)
|
|
{
|
|
Check(list_res);
|
|
list_res->Lock();
|
|
Verify(
|
|
list_res->resourceType <= ResourceDescription::MaxListResourceType
|
|
);
|
|
ResourceDescription::ResourceID *list =
|
|
(ResourceDescription::ResourceID*)list_res->resourceAddress;
|
|
Check_Pointer(list);
|
|
for (int i=1; i<=*list; ++i)
|
|
{
|
|
ResourceDescription *res = FindResourceDescription(list[i]);
|
|
Check(res);
|
|
if (res->resourceType == type)
|
|
{
|
|
list_res->Unlock();
|
|
return res;
|
|
}
|
|
}
|
|
list_res->Unlock();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription*
|
|
ResourceFile::SearchList(
|
|
ResourceDescription::ResourceID list_id,
|
|
const char* name
|
|
)
|
|
{
|
|
Check_Pointer(name);
|
|
ResourceDescription *list_res = FindResourceDescription(list_id);
|
|
if (list_res)
|
|
{
|
|
Check(list_res);
|
|
list_res->Lock();
|
|
Verify(
|
|
list_res->resourceType <= ResourceDescription::MaxListResourceType
|
|
);
|
|
ResourceDescription::ResourceID *list =
|
|
(ResourceDescription::ResourceID*)list_res->resourceAddress;
|
|
Check_Pointer(list);
|
|
for (int i=1; i<=*list; ++i)
|
|
{
|
|
ResourceDescription *res = FindResourceDescription(list[i]);
|
|
Check(res);
|
|
if (!strcmp(name, res->resourceName))
|
|
{
|
|
list_res->Unlock();
|
|
return res;
|
|
}
|
|
}
|
|
list_res->Unlock();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ResourceFile::ReleaseUnlockedResources()
|
|
{
|
|
for (ResourceDescription::ResourceID i = maxResourceID; i>=0; i--)
|
|
{
|
|
if(resourceArray[i] != NULL)
|
|
{
|
|
ResourceDescription *res = resourceArray[i];
|
|
Check(res);
|
|
res->ReleaseUnlockedResource();
|
|
}
|
|
}
|
|
}
|
|
|
|
//#############################################################################
|
|
// ResourceFile Test Support
|
|
//
|
|
|
|
Logical
|
|
ResourceFile::TestInstance() const
|
|
{
|
|
return True;
|
|
}
|
|
|
|
//##############################################################################
|
|
//########################## StreamableResourceFile ######################
|
|
//##############################################################################
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
StreamableResourceFile::StreamableResourceFile()
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
StreamableResourceFile::StreamableResourceFile(const char* file_name)
|
|
{
|
|
Open(file_name);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
StreamableResourceFile::StreamableResourceFile(const char* file_name, Byte version_array[3], int match_level)
|
|
{
|
|
Open(file_name);
|
|
int i;
|
|
for (i=0; i<match_level; ++i)
|
|
if (version_array[i] != versionArray[i+1])
|
|
break;
|
|
|
|
if (i < match_level)
|
|
{
|
|
DEBUG_STREAM << "\n\nError - Resource file " << file_name << " v" << (int)versionArray[0] << '.' << (int)versionArray[1] << '.' << (int)versionArray[2] << '.' << (int)versionArray[3] << " is obsolete!\n\n" << std::flush;
|
|
PostQuitMessage(AbortExitCodeID);
|
|
}
|
|
|
|
#if 0
|
|
if (release_only && labOnly)
|
|
{
|
|
DEBUG_STREAM << "\n\n\nERROR!\n\n"
|
|
"This resource file was not properly released!\n\n";
|
|
PostQuitMessage(AbortExitCodeID);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
StreamableResourceFile::~StreamableResourceFile()
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
int
|
|
StreamableResourceFile::Open(const char *file_name)
|
|
{
|
|
Check(this);
|
|
Str_Copy(resourceFilename, file_name, sizeof(resourceFilename));
|
|
|
|
diskFile.Open(resourceFilename);
|
|
if (!diskFile.IsFileOpened())
|
|
{
|
|
return False;
|
|
}
|
|
|
|
//
|
|
//-----------------------
|
|
// Read in resource count
|
|
//-----------------------
|
|
//
|
|
maxResourceID = 0;
|
|
|
|
diskFile.ReadBytes(&versionArray, sizeof(versionArray));
|
|
|
|
if (versionArray[0] != RESOURCE_FORMAT_VERSION)
|
|
{
|
|
DEBUG_STREAM << "\n\nError - Resource file " << file_name << " v"
|
|
<< (int)versionArray[0] << '.' << (int)versionArray[1] << '.'
|
|
<< (int)versionArray[2] << '.' << (int)versionArray[3]
|
|
<< " is obsolete!\n\n" << std::flush;
|
|
PostQuitMessage(AbortExitCodeID);
|
|
}
|
|
|
|
diskFile >> labOnly >> maxResourceID;
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// Read in main index table first, and make room for expansion later
|
|
//------------------------------------------------------------------
|
|
//
|
|
size_t *offset_table = new size_t[maxResourceID];
|
|
Register_Pointer(offset_table);
|
|
|
|
diskFile.ReadBytes(offset_table, maxResourceID*sizeof(size_t));
|
|
resourceArraySize = maxResourceID + IndexBlockSize;
|
|
|
|
resourceArray = new ResourceDescription*[resourceArraySize];
|
|
Register_Pointer(resourceArray);
|
|
int i;
|
|
for (i=0; i<resourceArraySize; ++i)
|
|
{
|
|
resourceArray[i] = NULL;
|
|
}
|
|
--maxResourceID;
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Step through the index table, reading each description as we find it
|
|
//---------------------------------------------------------------------
|
|
//
|
|
for (i=0; i<=maxResourceID; ++i)
|
|
{
|
|
//
|
|
//--------------------------------------------
|
|
// Make sure we skip empty resources correctly
|
|
//--------------------------------------------
|
|
//
|
|
if (!offset_table[i])
|
|
{
|
|
resourceArray[i] = NULL;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
//------------------------
|
|
// Read in the description
|
|
//------------------------
|
|
//
|
|
ResourcePhysicalFormat buffer;
|
|
diskFile.SetPointer(offset_table[i]);
|
|
diskFile >> buffer;
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// If the preload flag is set, read in the resource
|
|
//-------------------------------------------------
|
|
//
|
|
|
|
char *data = NULL;
|
|
if (buffer.resourceFlags == ResourceDescription::Preload && buffer.resourceLength > 0)
|
|
{
|
|
data = new char[buffer.resourceLength];
|
|
Register_Pointer(data);
|
|
diskFile.SetPointer(buffer.resourceOffset);
|
|
diskFile.ReadBytes(data, buffer.resourceLength);
|
|
}
|
|
if (buffer.resourceType > ResourceDescription::MaxListResourceType || !data)
|
|
{
|
|
resourceArray[i] = AddResource(
|
|
buffer.resourceName,
|
|
buffer.resourceType,
|
|
buffer.downloadPriority,
|
|
buffer.resourceFlags,
|
|
data,
|
|
buffer.resourceLength,
|
|
buffer.resourceID
|
|
);
|
|
}
|
|
else
|
|
{
|
|
resourceArray[i] = AddResourceList(
|
|
buffer.resourceName,
|
|
buffer.resourceType,
|
|
buffer.downloadPriority,
|
|
buffer.resourceFlags,
|
|
(int*)data + 1,
|
|
*(int*)data,
|
|
buffer.resourceID
|
|
);
|
|
}
|
|
resourceArray[i]->offsetInFile = buffer.resourceOffset;
|
|
if (data)
|
|
{
|
|
resourceArray[i]->Lock();
|
|
Unregister_Pointer(data);
|
|
delete[] data;
|
|
}
|
|
}
|
|
|
|
Unregister_Pointer(offset_table);
|
|
delete[] offset_table;
|
|
return True;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
int
|
|
StreamableResourceFile::Save()
|
|
{
|
|
Check(this);
|
|
if (!resourceArray)
|
|
{
|
|
return False;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Make sure to load any unloaded resources before closing the input file
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
ResourceDescription *desc;
|
|
ResourceDescription::ResourceID i;
|
|
for (i=0; i<=maxResourceID; ++i)
|
|
{
|
|
desc = resourceArray[i];
|
|
if (desc)
|
|
{
|
|
Check(desc);
|
|
desc->LoadResource();
|
|
}
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------
|
|
// Close the current file, and open a new one
|
|
//-------------------------------------------
|
|
//
|
|
if (diskFile.IsFileOpened())
|
|
{
|
|
diskFile.Close();
|
|
}
|
|
diskFile.Open(resourceFilename, True);
|
|
Verify(diskFile.IsFileOpened());
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Write out resource count and version
|
|
//-------------------------------------
|
|
//
|
|
++maxResourceID;
|
|
|
|
diskFile.WriteBytes(&versionArray, sizeof(versionArray));
|
|
diskFile << labOnly << maxResourceID;
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Write out main index table first. This is just filling up space, and the
|
|
// offsets will have to be written back into this section later
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
size_t *offset_table = new size_t[maxResourceID];
|
|
Register_Pointer(offset_table);
|
|
diskFile.WriteBytes(offset_table, maxResourceID*sizeof(size_t));
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// Step through the index table, writing out each description as we find it
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
for (i=0; i<maxResourceID; ++i)
|
|
{
|
|
//
|
|
//--------------------------------------------
|
|
// Make sure we skip empty resources correctly
|
|
//--------------------------------------------
|
|
//
|
|
desc = resourceArray[i];
|
|
if (!desc)
|
|
{
|
|
offset_table[i] = 0;
|
|
continue;
|
|
}
|
|
|
|
Check(desc);
|
|
|
|
//
|
|
//-------------------
|
|
// Write out the data
|
|
//-------------------
|
|
//
|
|
ResourcePhysicalFormat buffer;
|
|
buffer.resourceOffset = diskFile.GetIndex();
|
|
diskFile.WriteBytes(desc->resourceAddress, desc->resourceSize);
|
|
|
|
//
|
|
//--------------------------
|
|
// Write out the description
|
|
//--------------------------
|
|
//
|
|
offset_table[i] = diskFile.GetIndex();
|
|
buffer.resourceID = desc->resourceID;
|
|
buffer.resourceType = desc->resourceType;
|
|
|
|
//#if sizeof(buffer.resourceName) < sizeof(desc->resourceName)
|
|
//# error ResourceDescription::resourceName is too big!
|
|
//#endif
|
|
|
|
memcpy(
|
|
buffer.resourceName,
|
|
desc->resourceName,
|
|
sizeof(desc->resourceName)
|
|
);
|
|
|
|
buffer.resourceLength = desc->resourceSize;
|
|
buffer.downloadPriority = desc->downloadPriority;
|
|
buffer.resourceFlags =
|
|
desc->resourceFlags & ~ResourceDescription::LoadedFlag;
|
|
diskFile << buffer;
|
|
}
|
|
diskFile.SetPointer(
|
|
sizeof(maxResourceID) + sizeof(versionArray) + sizeof(labOnly)
|
|
);
|
|
diskFile.WriteBytes(offset_table, maxResourceID*sizeof(long));
|
|
Unregister_Pointer(offset_table);
|
|
delete(offset_table);
|
|
--maxResourceID;
|
|
diskFile.Close();
|
|
return True;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
int
|
|
StreamableResourceFile::SaveAs(const char* file_name)
|
|
{
|
|
Str_Copy(resourceFilename, file_name, sizeof(resourceFilename));
|
|
return Save();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
StreamableResourceFile::LoadResource(ResourceDescription *res)
|
|
{
|
|
ResourceFile::LoadResource(res);
|
|
Verify(!res->resourceAddress);
|
|
res->resourceAddress = new char[res->resourceSize];
|
|
Register_Pointer(res->resourceAddress);
|
|
diskFile.SetPointer(res->offsetInFile);
|
|
diskFile.ReadBytes(res->resourceAddress, res->resourceSize);
|
|
}
|
|
|
|
//#############################################################################
|
|
// StreamableResourceFile Test Support
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
StreamableResourceFile::TestInstance() const
|
|
{
|
|
return True;
|
|
}
|
|
|
|
#if defined(TEST_CLASS)
|
|
#include "resource.tcp"
|
|
#endif
|