Files
CydandClaude Fable 5 2f17631081 FS507D asset recovery, ConLobby V5.0.7Df promotion, mechlab New-Mech crash fix
FS507D_20161015 release analysis wrap-up (drop itself is gitignored; art-review
folder kept local for review):
- Recovered the only assets our tree lacked: 13 hsh HUD/radar/mech bmps and the
  two 5.07D lobby decals (decal_46/47.tga, extracted from release props.mw4 via
  a ported gos_LZDecompress).
- ResourceImagePool.cpp: missing-texture placeholder is now LAB_ONLY (editor
  keeps degraded mode); Release restores the original fatal STOP. Release +
  Profile rebuilt, 0 errors.

Console script reconciled:
- ConLobby.script.new ("BattleTech Console V5.0.7Df", newest revision anywhere)
  promoted to Content\ShellScripts\ConLobby.script; .new removed; stale loose
  deploy copy removed. Verified in-game (console title shows V5.0.7Df).
- Corrected CLAUDE.md: the release never "renamed" the console to
  ComputerPlayer.script -- the resource packer stores script contents under
  alphabetically skewed entry names (runtime resolves the same pairing).
  Packer quirks documented (stale entry carry-forward on incremental builds,
  name/content skew in directory sweeps).

Mechlab bug fix (first FireStorm bug hunt):
- New-Mech dialog showed blank rows for Wolfhound/Zeus and crashed (KERNELBASE
  read AV) when creating a Zeus. Root cause: newmech in chassis.script created
  its Type droplist without setting $$m_listBoxSize$$, so capacity defaulted to
  60 while 65 chassis were written in -- OOB script-array writes. Fixed by
  sizing the list from $$m_chassisCount$$. Latent stock-MW4 bug armed when the
  FireStorm roster passed 60 chassis. Verified: Zeus variant creates cleanly.

props.mw4 fully repacked (decals + promoted console + mechlab fix, junk
.new/.org entries gone); game deploy refreshed at MW4\. Gitignore: FS507D drop
and player-created MW4\Resource\Variants.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 13:04:45 -05:00

473 lines
13 KiB
C++

#include "AdeptHeaders.hpp"
#include "ResourceImagePool.hpp"
#include "Application.hpp"
#include "Tool.hpp"
#include "VideoRenderer.hpp"
#include <Compost\Compost.hpp>
#include <MLR\GOSImage.hpp>
using MidLevelRenderer::GOSImage;
#define SUPPORT_TGA
#define SUPPORT_PNG
#if 1
#define TEXTURE_LOAD(string) LOG_BLOCK("Texture Load::" string)
#else
#define TEXTURE_LOAD(string)
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Compost::FetchTextureData(Compost::Feature_Texture *tex, int index, int type)
{
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Compost::FetchTextureData(Compost::Feature_Texture *tex, const char *name, int type)
{
Verify(gos_GetCurrentHeap() == Heap);
MString fname(name);
fname += ".bid";
fname.ToLower();
FileStream hFile(fname);
int size = tex->RecalculateSize();
tex->data = new BYTE [size];
tex->dataSize = size;
if(size != hFile.GetBytesRemaining())
{
STOP(("Length of BID-file %s incorrect", name));
}
hFile.ReadBytes(tex->data, size);
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Compost::DestroyTextureData(Compost::Feature_Texture *tex)
{
delete [] tex->data;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
DWORD ResourceImagePool::FindTexture(
const char* tex_name,
int hint
)
{
Check_Pointer(tex_name);
TEXTURE_LOAD("Find Texture");
//
//----------------------------------------------
// Put the texture directory on the texture name
//----------------------------------------------
//
MString file_name = "content\\textures\\";
file_name += tex_name;
file_name += ".tga";
//
//---------------------------------------------
// Loop through testing for each extension type
//---------------------------------------------
//
#if defined(SUPPORT_PNG)
char* ext_pointer = static_cast<char*>(file_name) + file_name.GetLength()-3;
do
{
#endif
//
//-------------------------------------------------------------------
// If we can find a texture matching the name, check to see if it has
// explicit mipmaps. If so, we need to secretly reroute to a texture
// given by the bias
//-------------------------------------------------------------------
//
bool exists;
{
Resource texture_stream(file_name);
exists = texture_stream.DoesResourceExist();
}
if (exists)
{
int hint_flags;
Resource hint_stream(MString(file_name)+"{hint}");
Verify(hint_stream.DoesResourceExist());
hint_stream.LoadData();
hint_stream >> hint_flags;
if (Environment.bitDepth == 32)
hint_flags |= gosHint_Try32bpp;
hint_stream.UnloadData();
if (hint_flags & gosHint_UserMipMaps)
{
int bias = hint_flags >> 24;
bias = VideoRenderer::s_MipBias + bias;
Min_Clamp(bias, 0);
int len = strlen(file_name)-5;
file_name[len] = static_cast<char>(file_name[len] + bias);
Max_Clamp(file_name[len], '5');
}
char format = static_cast<char>(hint_flags>>16);
hint_flags &= 0xffff;
DWORD texture_handle =
gos_NewTextureFromFile(
static_cast<gos_TextureFormat>(format),
file_name,
hint_flags|hint
);
//
//--------------------------------------------
// If we want this in video memory, preload it
//--------------------------------------------
//
if ((hint_flags & (gosHint_AGPMemory|gosHint_VideoMemory)) == gosHint_VideoMemory)
gos_PreloadTexture(texture_handle);
return texture_handle;
}
//
//--------------------------------------------------------------------
// We couldn't find the resource, so try the actual file. If the hint
// says to use explicit mipmaps, bump the number by the bias
//--------------------------------------------------------------------
//
else
{
if (hint & gosHint_UserMipMaps)
{
int len = strlen(file_name)-5;
Verify(file_name[len] == '0');
file_name[len] =
static_cast<char>(file_name[len] + VideoRenderer::s_MipBias);
}
//
//-----------------------------
// See if the file can be found
//-----------------------------
//
if (gos_DoesFileExist(file_name))
{
#ifdef LAB_ONLY
PAUSE(("[%s] is NOT in resources but found on disc.", tex_name));
#endif
if (FileStream::IsRedirected)
{
FileStream::IsRedirected = false;
return
gos_NewTextureFromFile(
gos_Texture_Detect,
FileStream::RedirectedName,
hint
);
}
else
{
return
gos_NewTextureFromFile(
gos_Texture_Detect,
file_name,
hint
);
}
}
#ifdef LAB_ONLY
else
{
PAUSE(("[%s] is NOT in resources and NOT on disc.", tex_name));
}
#endif
Verify(!FileStream::IsRedirected);
}
//
//----------------------------------------------------------------
// Stop the loop if the png doesn't exist or if we found our image
//----------------------------------------------------------------
//
#if defined(SUPPORT_PNG)
if (ext_pointer[0]=='p')
break;
//
//---------------------------
// roll to the next extension
//---------------------------
//
ext_pointer[0]='p';
ext_pointer[1]='n';
ext_pointer[2]='g';
Verify(!ext_pointer[3]);
} while (true);
#endif
return 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
ResourceImagePool::LoadImageGOS(GOSImage *image, int hint)
{
//
//----------------------------------------
// If the image is already loaded, we done
//----------------------------------------
//
TEXTURE_LOAD("Load Image");
if ( (image->flags & GOSImage::Loaded) != 0)
return true;
//
//-----------------------------------------------------------------
// if the image name starts with an underscore, it requires special
// treatment. Two underscores mean no skin, so just remove the
// first underscore
//-----------------------------------------------------------------
//
const char* tex_name = image->imageName;
if (tex_name[1] == g_SkinSignal)
{
if (*tex_name == g_SkinSignal)
++tex_name;
//
//-------------------------------------------------------------------
// If we get here, then we must synthesize this new texture rather
// than using something directly from the resources. We will have to
// open up both the specified file and a skin file and mix them
//-------------------------------------------------------------------
//
else
{
TEXTURE_LOAD("Load Image::Composite Skin");
static char skin[7] = "skin 0";
skin[4] = *tex_name;
//
//---------------------------------------------------------------
// We've got the names of both textures now, next we need to have
// gos open them up for us
//---------------------------------------------------------------
//
DWORD skin_handle = FindTexture(skin, gosHint_DisableMipmap|gosHint_DontShrink);
if(skin_handle==0)
{
STOP((0, "Skin: %s not found !", skin));
}
DWORD detail_handle = FindTexture(tex_name+1, gosHint_DisableMipmap|gosHint_DontShrink);
if(detail_handle==0)
{
STOP((0, "Skindetail: %s not found !", tex_name+1));
}
image->imageHandle = SkinThis(tex_name, gos_Texture_Solid, skin_handle, detail_handle);
gos_DestroyTexture(detail_handle);
gos_DestroyTexture(skin_handle);
image->flags |= (image->imageHandle ? GOSImage::Loaded : 0);
return ( (image->flags & GOSImage::Loaded) != 0);
}
}
//
//--------------------------
// Just open the texture now
//--------------------------
//
image->imageHandle = FindTexture(tex_name, hint|gosHint_ReloadFromDisk);
//
//----------------------------------
// See if the image loaded correctly
//----------------------------------
//
image->flags |= (image->imageHandle ? GOSImage::Loaded : 0);
return ( (image->flags & GOSImage::Loaded) != 0);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ResourceImagePool::BuildTexturePool(NotationFile *hint_file)
{
//
//-------------------------------
// Open up the texture build file
//-------------------------------
//
Check_Object(Tool::Instance);
NotationFile::PageIterator *pages = hint_file->MakePageIterator();
Register_Object(pages);
//
//---------------------------------------------------------
// Go through the textures file and add each listed texture
//---------------------------------------------------------
//
Check_Object(Tool::Instance);
Page *page;
Verify(!Resource::ParentFileDependencies);
while ((page = pages->ReadAndNext()) != NULL)
{
Check_Object(page);
const char* texture = page->GetName();
if (!texture)
continue;
Check_Pointer(texture);
//
//------------------------------------------------------------
// See if the resource file is aliased, if so replace the name
//------------------------------------------------------------
//
page->GetEntry("alias", &texture);
//
//--------------------------------------------
// If we don't resourcify the texture, move on
//--------------------------------------------
//
bool resourcify = true;
page->GetEntry("resourcify", &resourcify);
if (!resourcify)
continue;
//
//------------------------------------------------
// See if we can open up a resource with this name
//------------------------------------------------
//
MString file_name = "content\\textures\\";
file_name += texture;
file_name += ".tga";
//
//--------------------------
// Find the file on the path
//--------------------------
//
Verify(!FileStream::IsRedirected);
#ifdef LAB_ONLY
strcpy(MWGameInfo::g_LastFileOpenRequest, "BuildTexturePool: ");
strcpy(MWGameInfo::g_LastFileOpenRequest + 20, file_name);
#endif
if (!gos_DoesFileExist(file_name))
{
#if defined(SUPPORT_PNG)
Verify(!FileStream::IsRedirected);
char *extension = (char*)file_name + file_name.GetLength()-3;
extension[0] = 'p';
extension[1] = 'n';
extension[2] = 'g';
Verify(!extension[3]);
if (!gos_DoesFileExist(file_name))
#endif
{
#ifdef LAB_ONLY
file_name = "content\\textures\\01AACA1.tga"; /* [editor degraded-mode] LAB builds (editor/Profile) substitute a placeholder for missing WIP textures instead of fatal STOP, so downstream gets a valid handle */
#else
STOP(("Texture %s could not be found!", (const char *)file_name)); /* Release: fail loud, ship no placeholder */
#endif
}
}
//
//--------------------------------------------------------
// If not, or it's not up to date, put it in the resources
//--------------------------------------------------------
//
Resource resource((FileStream::IsRedirected)?FileStream::RedirectedName:file_name);
FileStream::IsRedirected = false;
if (!resource.DoesResourceExist() || !resource.IsResourceUpToDate())
{
//
//--------------------
// Read the hint flags
//--------------------
//
int hints = ReadHint(page) & ~gosHint_ReloadFromDisk;
//
//-------------------
// Read in the format
//-------------------
//
const char* format;
if (page->GetEntry("format", &format))
{
if (!strcmp(format, "solid"))
hints |= static_cast<BYTE>(gos_Texture_Solid) << 16;
else if (!strcmp(format, "keyed"))
hints |= static_cast<BYTE>(gos_Texture_Keyed) << 16;
else if (!strcmp(format, "alpha"))
hints |= static_cast<BYTE>(gos_Texture_Alpha) << 16;
else if (!strcmp(format, "bump"))
hints |= static_cast<BYTE>(gos_Texture_Bump) << 16;
else if (!strcmp(format, "normal"))
hints |= static_cast<BYTE>(gos_Texture_Normal) << 16;
else
hints |= static_cast<BYTE>(gos_Texture_Detect) << 16;
}
else
hints |= static_cast<BYTE>(gos_Texture_Detect) << 16;
//
//--------------------------------------------------
// Look to see if we want a one level mip-bias delay
//--------------------------------------------------
//
int bias=0;
page->GetEntry("bias", &bias);
Clamp(bias, -5, 5);
hints |= bias << 24;
//
//---------------------------------
// Open the file as a memory stream
//---------------------------------
//
FileStream texture_file(file_name);
FileDependencies texture_dependencies;
texture_dependencies.AddDependency(&texture_file);
texture_dependencies.AddDependencies(hint_file->GetFileDependencies());
resource.Save(&texture_file, &texture_dependencies);
//
//---------------------------------
// Open the file as a memory stream
//---------------------------------
//
MString hint_name = resource.GetName();
hint_name += "{hint}";
Resource hint(hint_name);
MemoryStream hint_stream(&hints, sizeof(hints));
hint.Save(&hint_stream, hint_file->GetFileDependencies());
}
}
Unregister_Object(pages);
delete pages;
}