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>
988 lines
22 KiB
C++
988 lines
22 KiB
C++
#include "munga.h"
|
|
#pragma hdrstop
|
|
|
|
#include "tool.h"
|
|
#include "fileutil.h"
|
|
#include "eyecandy.h"
|
|
#include "dropzone.h"
|
|
#include "terrain.h"
|
|
#include "cultural.h"
|
|
#include "doorfram.h"
|
|
#include "camship.h"
|
|
#include "boxsolid.h"
|
|
#include "team.h"
|
|
#include "namelist.h"
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
ApplicationTool::CreateMakeMessage(
|
|
const char* class_name,
|
|
Entity::MakeMessage *creation_message,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories *directories
|
|
)
|
|
{
|
|
if (!stricmp(class_name,"TerrainClassID"))
|
|
{
|
|
Terrain::CreateMakeMessage(
|
|
(Terrain::MakeMessage*)creation_message,
|
|
model_file,
|
|
directories
|
|
);
|
|
return True;
|
|
}
|
|
else if (!stricmp(class_name,"UnscalableTerrainClassID"))
|
|
{
|
|
UnscalableTerrain::CreateMakeMessage(
|
|
(UnscalableTerrain::MakeMessage*)creation_message,
|
|
model_file,
|
|
directories
|
|
);
|
|
return True;
|
|
}
|
|
else if (!stricmp(class_name,"CulturalIconClassID"))
|
|
{
|
|
CulturalIcon::CreateMakeMessage(
|
|
(CulturalIcon::MakeMessage*)creation_message,
|
|
model_file,
|
|
directories
|
|
);
|
|
return True;
|
|
}
|
|
else if (!stricmp(class_name,"LandmarkClassID"))
|
|
{
|
|
Landmark::CreateMakeMessage(
|
|
(Landmark::MakeMessage*)creation_message,
|
|
model_file,
|
|
directories
|
|
);
|
|
return True;
|
|
}
|
|
else if (!stricmp(class_name,"DoorFrameClassID"))
|
|
{
|
|
DoorFrame::CreateMakeMessage(
|
|
(DoorFrame::MakeMessage*)creation_message,
|
|
model_file,
|
|
directories
|
|
);
|
|
return True;
|
|
}
|
|
else if (!stricmp(class_name,"EyeCandyClassID"))
|
|
{
|
|
EyeCandy::CreateMakeMessage(
|
|
(EyeCandy::MakeMessage*)creation_message,
|
|
model_file,
|
|
directories
|
|
);
|
|
return True;
|
|
}
|
|
else if (!stricmp(class_name, "TeamClassID"))
|
|
{
|
|
Team::CreateMakeMessage(
|
|
(Team::MakeMessage*)creation_message,
|
|
model_file,
|
|
directories
|
|
);
|
|
return True;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Not a class MUNGA knows about, so return False
|
|
//-----------------------------------------------
|
|
//
|
|
return False;
|
|
}
|
|
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
ApplicationTool::WriteMaps(
|
|
char *map_name,
|
|
NotationFile *notation_file,
|
|
NotationFile *ops_file,
|
|
StreamableResourceFile *file,
|
|
const ResourceDirectories *resource_directories,
|
|
int resource_id
|
|
)
|
|
{
|
|
const char
|
|
*entry_data, *type, *ops_name;
|
|
char *p;
|
|
char
|
|
short_map_name[15],
|
|
ops_file_page[100],
|
|
ops_file_data[100],
|
|
temp[100];
|
|
|
|
|
|
Str_Copy(short_map_name, map_name, 15);
|
|
p = short_map_name;
|
|
p += strlen(short_map_name) - 4;
|
|
*p = '\0';
|
|
|
|
|
|
|
|
|
|
//----------------------
|
|
// Write ops_file stuff
|
|
//----------------------
|
|
|
|
ops_file->GetEntry("resource", "res name", &ops_name);
|
|
|
|
strcpy(ops_file_data, ops_name);
|
|
strcat(ops_file_data, "::Standard::Location::");
|
|
strcat(ops_file_data, short_map_name );
|
|
strcpy(ops_file_page, ops_name);
|
|
strcat(ops_file_page, "::Standard::LocationList");
|
|
|
|
ops_file->AppendEntry(ops_file_page, "location", ops_file_data);
|
|
ops_file->SetEntry(ops_file_data, "tag", short_map_name );
|
|
ops_file->AppendEntry(ops_file_data, NULL, (char *)NULL);
|
|
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Get exsitance box stream from notation file
|
|
// named after map then save it to the resource
|
|
// Not required yet!
|
|
//-----------------------------------------------
|
|
//
|
|
|
|
|
|
|
|
|
|
strcpy(temp, short_map_name);
|
|
strcat(temp, ".xst");
|
|
|
|
char *filename = MakePathedFilename(
|
|
resource_directories->collisionDirectory,
|
|
temp);
|
|
Register_Pointer(filename);
|
|
|
|
NotationFile *exist_notation_file = new NotationFile(filename);
|
|
Register_Object(exist_notation_file);
|
|
|
|
Unregister_Pointer(filename);
|
|
delete filename;
|
|
|
|
labOnly |= exist_notation_file->IsMarkedLabOnly();
|
|
|
|
if (exist_notation_file->PageCount())
|
|
{
|
|
|
|
|
|
|
|
int size;
|
|
ExtentBox *existance_box = MakeExistanceBoxStream(exist_notation_file, &size);
|
|
Register_Pointer(existance_box);
|
|
|
|
file->AddResource(
|
|
short_map_name,
|
|
ResourceDescription::ExistanceBoxStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
existance_box,
|
|
size * sizeof(ExtentBox)
|
|
);
|
|
|
|
Unregister_Pointer(existance_box);
|
|
delete[] existance_box;
|
|
}
|
|
|
|
Unregister_Object(exist_notation_file);
|
|
delete exist_notation_file;
|
|
|
|
|
|
|
|
//---------------------
|
|
// Make camera stream
|
|
//---------------------
|
|
|
|
CameraInstanceManager::CreateCameraInstancesStream(
|
|
file,
|
|
map_name,
|
|
resource_directories
|
|
);
|
|
|
|
//
|
|
//--------------------------------------------------------
|
|
// Get the name and types of all the pages in the map file
|
|
//--------------------------------------------------------
|
|
//
|
|
|
|
NameList *namelist = notation_file->MakePageList();
|
|
Register_Object(namelist);
|
|
NameList::Entry *entry = namelist->GetFirstEntry();
|
|
|
|
|
|
Logical model_failed = False;
|
|
|
|
char model_name[100];
|
|
while (entry)
|
|
{
|
|
entry_data = entry->GetName();
|
|
|
|
|
|
if (!stricmp(entry_data, "LAB_ONLY"))
|
|
{
|
|
labOnly = True;
|
|
entry = entry->GetNextEntry();
|
|
continue;
|
|
}
|
|
|
|
else if(!notation_file->GetEntry(entry_data, "type", &type))
|
|
{
|
|
std::cout << "No Type in " << entry_data << std::endl;
|
|
|
|
Unregister_Object(namelist);
|
|
delete namelist;
|
|
|
|
return False;
|
|
|
|
|
|
}
|
|
|
|
//
|
|
//----------------------------
|
|
// Read in a model description
|
|
//----------------------------
|
|
//
|
|
char scenario_name[100];
|
|
|
|
if (!stricmp(type, "model"))
|
|
{
|
|
strcpy(model_name, entry_data);
|
|
|
|
if(
|
|
!notation_file->GetEntry(
|
|
model_name,
|
|
"modelfile",
|
|
&entry_data
|
|
)
|
|
)
|
|
{
|
|
std::cout << "No modelfile in " << model_name << std::endl;
|
|
Unregister_Object(namelist);
|
|
delete namelist;
|
|
|
|
return False;
|
|
}
|
|
|
|
ResourceDescription *res = file->FindResourceDescription(
|
|
model_name,
|
|
ResourceDescription::ModelListResourceType
|
|
);
|
|
if (!res)
|
|
{
|
|
char *filename =
|
|
MakePathedFilename(
|
|
resource_directories->modelDirectory,
|
|
entry_data
|
|
);
|
|
Register_Pointer(filename);
|
|
|
|
NotationFile *new_notation_file = new NotationFile(filename);
|
|
Register_Object(new_notation_file);
|
|
|
|
labOnly |= new_notation_file->IsMarkedLabOnly();
|
|
|
|
if (new_notation_file->PageCount())
|
|
{
|
|
if (
|
|
!WriteModels(
|
|
new_notation_file,
|
|
ops_file,
|
|
file,
|
|
model_name,
|
|
resource_directories
|
|
)
|
|
)
|
|
{
|
|
std::cout << "Model Failed : " << filename << std::endl;
|
|
model_failed = True;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Model File : " << filename << " does not exist!\n";
|
|
model_failed = True;
|
|
}
|
|
|
|
Unregister_Pointer(filename);
|
|
delete filename;
|
|
Unregister_Object(new_notation_file);
|
|
delete new_notation_file;
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(type, "dropzone"))
|
|
{
|
|
#if 0
|
|
strcpy(model_name, entry_data);
|
|
ResourceDescription *res =
|
|
file->FindResourceDescription(
|
|
model_name,
|
|
ResourceDescription::ModelListResourceType
|
|
);
|
|
if (!res)
|
|
{
|
|
//
|
|
// make drop zone name be blah.name
|
|
//
|
|
char dropzone_name[32];
|
|
Str_Copy(dropzone_name, short_map_name, sizeof(dropzone_name));
|
|
Str_Cat(dropzone_name, ".", sizeof(dropzone_name));
|
|
Str_Cat(dropzone_name, model_name,sizeof(dropzone_name));
|
|
|
|
|
|
|
|
strcpy(ops_file_data, ops_name);
|
|
strcat(ops_file_data, "::");
|
|
strcat(ops_file_data, short_map_name);
|
|
strcat(ops_file_data, "::DropZone::" );
|
|
strcat(ops_file_data, dropzone_name);
|
|
|
|
strcpy(ops_file_page, ops_name);
|
|
strcat(ops_file_page, "::");
|
|
strcat(ops_file_page, short_map_name );
|
|
strcat(ops_file_page, "::DropZoneList");
|
|
|
|
ops_file->AppendEntry(ops_file_page, "DropZone", ops_file_data);
|
|
ops_file->SetEntry(ops_file_data, "tag", dropzone_name );
|
|
ops_file->SetEntry(ops_file_data, "classID", "DpZn");
|
|
ops_file->AppendEntry(ops_file_data, NULL, (char *)NULL);
|
|
|
|
|
|
ResourceDescription::ResourceID res_number =
|
|
DropZone::CreateModelResource(
|
|
file,
|
|
model_name,
|
|
notation_file,
|
|
dropzone_name,
|
|
resource_directories
|
|
);
|
|
|
|
if (res_number != ResourceDescription::NullResourceID)
|
|
{
|
|
file->AddResourceList(
|
|
dropzone_name,
|
|
ResourceDescription::ModelListResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
&res_number,
|
|
1
|
|
);
|
|
}
|
|
else
|
|
{
|
|
model_failed = True;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
else if (!stricmp(type, "include"))
|
|
{
|
|
strcpy(scenario_name, entry_data);
|
|
|
|
NameList *namelist2 =
|
|
notation_file->MakeEntryList(entry_data, "file");
|
|
Register_Object(namelist2);
|
|
NameList::Entry *entry2 = namelist2->GetFirstEntry();
|
|
|
|
while(entry2 != NULL)
|
|
{
|
|
|
|
const char *entry2_data = entry2->GetChar();
|
|
|
|
char *filename =
|
|
MakePathedFilename(
|
|
resource_directories->mapDirectory,
|
|
entry2_data
|
|
);
|
|
Register_Pointer(filename);
|
|
|
|
NotationFile *new_notation_file = new NotationFile(filename);
|
|
Register_Object(new_notation_file);
|
|
|
|
labOnly |= new_notation_file->IsMarkedLabOnly();
|
|
|
|
|
|
//
|
|
// Write out drop zones....
|
|
// Write out maps...
|
|
//
|
|
char short_name[100];
|
|
strcpy( short_name, StripDirectory(entry2_data) );
|
|
|
|
if (new_notation_file->PageCount())
|
|
{
|
|
if(
|
|
!WriteMaps(
|
|
short_name,
|
|
new_notation_file,
|
|
ops_file,
|
|
file,
|
|
resource_directories
|
|
)
|
|
)
|
|
{
|
|
std::cout << "Include Failed : " << filename << std::endl;
|
|
model_failed = True;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Include File : " << filename << " does not exist\n";
|
|
model_failed = True;
|
|
}
|
|
|
|
|
|
Unregister_Pointer(filename);
|
|
delete filename;
|
|
Unregister_Object(new_notation_file);
|
|
delete new_notation_file;
|
|
entry2 = entry2->GetNextEntry();
|
|
}
|
|
|
|
Unregister_Object(namelist2);
|
|
delete namelist2;
|
|
|
|
}
|
|
|
|
else if (!stricmp(type, "map"))
|
|
{
|
|
if (model_failed == False)
|
|
{
|
|
//
|
|
// Check to see that map is not already in resource file
|
|
//
|
|
|
|
ResourceDescription *test = file->FindResourceDescription(
|
|
short_map_name, ResourceDescription::MakeMessageStreamResourceType);
|
|
|
|
BoxedSolidList *box_solid_list = new BoxedSolidList;
|
|
Register_Object(box_solid_list);
|
|
|
|
if ( test == NULL)
|
|
{
|
|
NameList *namelist2 =
|
|
notation_file->MakeEntryList(entry_data, "instance");
|
|
Register_Object(namelist2);
|
|
NameList::Entry *entry2 = namelist2->GetFirstEntry();
|
|
|
|
//
|
|
//----------------
|
|
// Count instances
|
|
//----------------
|
|
//
|
|
int instance_count;
|
|
for (instance_count=0; entry2; ++instance_count)
|
|
{
|
|
entry2 = entry2->GetNextEntry();
|
|
}
|
|
entry2 = namelist2->GetFirstEntry();
|
|
|
|
//
|
|
//-------------------------------------------------------
|
|
// make a buffer to hold all these messages, and have the
|
|
// beginning store the number of messages
|
|
//-------------------------------------------------------
|
|
//
|
|
int streamSize = instance_count * 4 * sizeof(Entity::MakeMessage);
|
|
Entity::MakeMessage *stream = (Entity::MakeMessage*)new char[streamSize];
|
|
memset(stream, 0, streamSize);
|
|
|
|
MemoryStream m(stream, streamSize);
|
|
*(int*)m.GetPointer() = instance_count;
|
|
m.AdvancePointer(sizeof(int));
|
|
|
|
EulerAngles angular_position;
|
|
Degree value;
|
|
|
|
while (entry2)
|
|
{
|
|
char find_me[100];
|
|
strcpy(find_me, entry2->GetChar());
|
|
char *p = find_me;
|
|
char *token = strtok(p,White_Space);
|
|
ResourceDescription *res = NULL;
|
|
|
|
if (!stricmp( entry2->GetName(), "instancedropzone"))
|
|
{
|
|
|
|
char dropzone_name[32];
|
|
#if 0
|
|
Str_Copy(dropzone_name, short_map_name, sizeof(dropzone_name));
|
|
Str_Cat(dropzone_name, ".", sizeof(dropzone_name));
|
|
Str_Cat(dropzone_name,token,sizeof(dropzone_name));
|
|
#else
|
|
Str_Copy(dropzone_name,token,sizeof(dropzone_name));
|
|
#endif
|
|
|
|
|
|
DropZone::CreateMakeMessage(
|
|
(DropZone::MakeMessage*)m.GetPointer(),
|
|
token,
|
|
dropzone_name,
|
|
notation_file
|
|
);
|
|
}
|
|
else
|
|
{
|
|
notation_file->GetEntry(token,"type",&type);
|
|
|
|
if(!stricmp(type, "include"))
|
|
{
|
|
res =
|
|
file->FindResourceDescription(
|
|
token,
|
|
ResourceDescription::MakeMessageStreamResourceType
|
|
);
|
|
|
|
}
|
|
else
|
|
{
|
|
res =
|
|
file->FindResourceDescription(
|
|
token,
|
|
ResourceDescription::ModelListResourceType
|
|
);
|
|
}
|
|
|
|
if (!res)
|
|
{
|
|
std::cout << "Instance Not Found : " << token << std::endl;
|
|
|
|
Unregister_Object(namelist2);
|
|
Unregister_Pointer(stream);
|
|
Unregister_Object(namelist);
|
|
delete namelist2;
|
|
delete[] stream;
|
|
delete namelist;
|
|
|
|
return False;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
//--------------
|
|
// Next instance
|
|
//--------------
|
|
//
|
|
|
|
//
|
|
// If it's not a drop zone
|
|
//
|
|
if (!stricmp( entry2->GetName(), "instancedropzone"))
|
|
{
|
|
// do nothing this is already done.
|
|
}
|
|
else
|
|
{
|
|
|
|
notation_file->GetEntry(token,"type",&type);
|
|
|
|
|
|
if(!stricmp(type, "include"))
|
|
{
|
|
|
|
char name[100];
|
|
Str_Copy(name, token, 100);
|
|
|
|
|
|
ResourceDescription *map_res = file->FindResourceDescription(
|
|
name, ResourceDescription::MakeMessageStreamResourceType);
|
|
|
|
if (!map_res)
|
|
{
|
|
std::cout << "CreateMakeMapMessage Failed To Find Map : " << name << std::endl;
|
|
Unregister_Object(namelist2);
|
|
Unregister_Pointer(stream);
|
|
Unregister_Object(namelist);
|
|
delete namelist2;
|
|
delete[] stream;
|
|
delete namelist;
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
if (
|
|
!Entity::CreateMakeMapMessage(
|
|
(Entity::MakeMapMessage*)m.GetPointer(),
|
|
map_res->resourceID,
|
|
notation_file,
|
|
resource_directories
|
|
)
|
|
)
|
|
{
|
|
std::cout << "CreateMakeMapMessage Failed : " << type << std::endl;
|
|
Unregister_Object(namelist2);
|
|
Unregister_Pointer(stream);
|
|
Unregister_Object(namelist);
|
|
delete namelist2;
|
|
delete[] stream;
|
|
delete namelist;
|
|
|
|
return False;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!notation_file->GetEntry(token,"modelfile",&type))
|
|
{
|
|
std::cout << token << " has no modelfile specification\n";
|
|
Unregister_Object(namelist2);
|
|
Unregister_Pointer(stream);
|
|
Unregister_Object(namelist);
|
|
delete namelist2;
|
|
delete[] stream;
|
|
delete namelist;
|
|
|
|
return False;
|
|
}
|
|
|
|
char *filename3 =
|
|
MakePathedFilename(
|
|
resource_directories->modelDirectory,
|
|
type
|
|
);
|
|
Register_Pointer(filename3);
|
|
NotationFile *new_notation_file =
|
|
new NotationFile(filename3);
|
|
Register_Object(new_notation_file);
|
|
|
|
labOnly |= new_notation_file->IsMarkedLabOnly();
|
|
|
|
Unregister_Pointer(filename3);
|
|
delete filename3;
|
|
|
|
if (!new_notation_file->PageCount())
|
|
{
|
|
std::cout << filename3 << " does not exist!";
|
|
Unregister_Object(namelist2);
|
|
Unregister_Pointer(stream);
|
|
Unregister_Object(namelist);
|
|
delete namelist2;
|
|
delete[] stream;
|
|
delete namelist;
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
if(!new_notation_file->GetEntry("gamedata","class",&type))
|
|
{
|
|
std::cout << "gamedata not found\n";
|
|
Unregister_Object(namelist2);
|
|
Unregister_Pointer(stream);
|
|
Unregister_Object(namelist);
|
|
delete namelist2;
|
|
delete[] stream;
|
|
delete namelist;
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
if (
|
|
!CreateMakeMessage(
|
|
type,
|
|
(Entity::MakeMessage*)m.GetPointer(),
|
|
new_notation_file,
|
|
resource_directories
|
|
)
|
|
)
|
|
{
|
|
std::cout << "Invalid gamedata type : " << type << std::endl;
|
|
Unregister_Object(namelist2);
|
|
Unregister_Pointer(stream);
|
|
Unregister_Object(namelist);
|
|
delete namelist2;
|
|
delete[] stream;
|
|
delete namelist;
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
Unregister_Object(new_notation_file);
|
|
delete new_notation_file;
|
|
}
|
|
}
|
|
|
|
|
|
Entity::MakeMessage *message =
|
|
(Entity::MakeMessage*)m.GetPointer();
|
|
if (
|
|
!(message->messageFlags
|
|
& Entity::MakeMessage::MapStreamMarkerFlag)
|
|
)
|
|
{
|
|
message->resourceID =
|
|
(res) ?
|
|
res->resourceID
|
|
: ResourceDescription::NullResourceID;
|
|
message->ownerID = MapHostID;
|
|
message->interestZoneID = 0;
|
|
}
|
|
m.AdvancePointer(message->messageLength);
|
|
entry2 = entry2->GetNextEntry();
|
|
}
|
|
|
|
|
|
if (resource_id == -1)
|
|
{
|
|
file->AddResource(
|
|
short_map_name,
|
|
ResourceDescription::MakeMessageStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
stream,
|
|
(char*)m.GetPointer() - (char*)stream
|
|
);
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Check for resource_id , if there Fail.
|
|
//
|
|
|
|
ResourceDescription *res_des =
|
|
file->FindResourceDescription(resource_id);
|
|
|
|
if (!res_des)
|
|
{
|
|
|
|
file->AddResource(
|
|
short_map_name,
|
|
ResourceDescription::MakeMessageStreamResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
stream,
|
|
(char*)m.GetPointer() - (char*)stream,
|
|
resource_id
|
|
);
|
|
}
|
|
else
|
|
{
|
|
DEBUG_STREAM << short_map_name << " : " << resource_id << std::endl << std::flush;
|
|
Fail("Duplicate Resource Number");
|
|
}
|
|
|
|
}
|
|
|
|
|
|
Unregister_Pointer(stream);
|
|
delete[] stream;
|
|
Unregister_Object(namelist2);
|
|
delete namelist2;
|
|
}
|
|
Unregister_Object(box_solid_list);
|
|
delete box_solid_list;
|
|
}
|
|
else
|
|
{
|
|
Unregister_Object(namelist);
|
|
delete namelist;
|
|
return False;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Error: unknown type " << type << "!\n";
|
|
|
|
|
|
Unregister_Object(namelist);
|
|
delete namelist;
|
|
return False;
|
|
}
|
|
|
|
entry = entry->GetNextEntry();
|
|
}
|
|
|
|
strcpy(ops_file_page, ops_name);
|
|
strcat(ops_file_page, "::");
|
|
strcat(ops_file_page, short_map_name );
|
|
strcat(ops_file_page, "::DropZoneList");
|
|
ops_file->AppendEntry(ops_file_page, NULL, (char *)NULL);
|
|
|
|
Unregister_Object(namelist);
|
|
delete namelist;
|
|
return True;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// This does NOT write the maps to the ops file, Write maps does that.
|
|
// This just finishes the ops file up
|
|
//
|
|
void
|
|
ApplicationTool::WriteOpsFile(
|
|
NotationFile *ops_file,
|
|
StreamableResourceFile *file,
|
|
const ResourceDirectories *resource_directories
|
|
)
|
|
{
|
|
//
|
|
//--------------------------------------
|
|
// open vehicle table and write out info
|
|
//--------------------------------------
|
|
//
|
|
const char *ops_name;
|
|
ops_file->GetEntry("resource", "res name", &ops_name);
|
|
|
|
char *filename3 =
|
|
MakePathedFilename(resource_directories->modelDirectory, "vehicle.tbl");
|
|
Register_Pointer(filename3);
|
|
|
|
NotationFile *new_notation_file = new NotationFile(filename3);
|
|
Register_Object(new_notation_file);
|
|
|
|
//
|
|
// Kill if file isn't there.
|
|
//
|
|
|
|
if (!new_notation_file->PageCount())
|
|
{
|
|
std::cout << "vehicle.tbl does not exist in the resource directory!\n" << std::flush;
|
|
Unregister_Pointer(filename3);
|
|
delete filename3;
|
|
PostQuitMessage(AbortExitCodeID);
|
|
}
|
|
Unregister_Pointer(filename3);
|
|
delete filename3;
|
|
|
|
NameList *list = new_notation_file->MakeEntryList("color");
|
|
Register_Object(list);
|
|
NameList::Entry *entry = list->GetFirstEntry();
|
|
|
|
char ops_file_data[100];
|
|
char ops_file_page[100];
|
|
while(entry)
|
|
{
|
|
if(entry->GetChar() != NULL)
|
|
{
|
|
strcpy(ops_file_data, ops_name);
|
|
strcat(ops_file_data, "::Standard::Color::");
|
|
strcat(ops_file_data, entry->GetName() );
|
|
strcpy(ops_file_page, ops_name);
|
|
strcat(ops_file_page, "::Standard::ColorList");
|
|
|
|
ops_file->AppendEntry(ops_file_page, "color", ops_file_data);
|
|
ops_file->SetEntry(ops_file_data, "tag", entry->GetName() );
|
|
ops_file->AppendEntry(ops_file_data, NULL, (char *)NULL);
|
|
}
|
|
|
|
entry = entry->GetNextEntry();
|
|
}
|
|
|
|
ops_file->AppendEntry(ops_file_page, NULL, (char *)NULL);
|
|
Unregister_Pointer(list);
|
|
delete list;
|
|
|
|
list = new_notation_file->MakeEntryList("badge");
|
|
Register_Object(list);
|
|
entry = list->GetFirstEntry();
|
|
|
|
while(entry)
|
|
{
|
|
if(entry->GetChar() != NULL)
|
|
{
|
|
strcpy(ops_file_data, ops_name);
|
|
strcat(ops_file_data, "::Standard::Badge::");
|
|
strcat(ops_file_data, entry->GetName() );
|
|
strcpy(ops_file_page, ops_name);
|
|
strcat(ops_file_page, "::Standard::BadgeList");
|
|
|
|
ops_file->AppendEntry(ops_file_page, "badge", ops_file_data);
|
|
ops_file->SetEntry(ops_file_data, "tag", entry->GetName() );
|
|
ops_file->AppendEntry(ops_file_data, NULL, (char *)NULL);
|
|
}
|
|
|
|
entry = entry->GetNextEntry();
|
|
}
|
|
|
|
ops_file->AppendEntry(ops_file_page, NULL, (char *)NULL);
|
|
Unregister_Pointer(list);
|
|
delete list;
|
|
|
|
|
|
//
|
|
// Put spaces at the end of vehicles and maps
|
|
// that were defined elsewere.
|
|
//
|
|
|
|
char temp[30];
|
|
|
|
|
|
|
|
strcpy(temp, ops_name);
|
|
strcat(temp, "::Standard::LocationList");
|
|
ops_file->AppendEntry(temp, NULL, (char *)NULL);
|
|
strcpy(temp, ops_name);
|
|
strcat(temp, "::Standard::VehicleList");
|
|
ops_file->AppendEntry(temp, NULL, (char *)NULL);
|
|
|
|
|
|
//
|
|
// Add notation file to resource.
|
|
//
|
|
|
|
long size;
|
|
size = new_notation_file->SizeOfText();
|
|
Verify( size >= 0 );
|
|
#define EDO_DEBUG False
|
|
#if EDO_DEBUG
|
|
Dump( size );
|
|
#endif
|
|
char *notation_string = new char[size];
|
|
Register_Pointer(notation_string);
|
|
#if DEBUG_LEVEL>0
|
|
long actual_size;
|
|
actual_size =
|
|
#endif
|
|
new_notation_file->WriteText(notation_string, size);
|
|
|
|
#if EDO_DEBUG
|
|
{
|
|
ofstream edo_debug;
|
|
edo_debug.open("DEBUGRES.EDO",std::ios::out);
|
|
if (!edo_debug) { Fail("can't open DEBUGRES.EDO"); }
|
|
const char
|
|
*whip = notation_string,
|
|
*stop = whip + size;
|
|
std::cout << "writing DEBUGRES.EDO" << std::endl;
|
|
while (whip < stop)
|
|
{
|
|
edo_debug << "|" << whip << "|" << std::endl;
|
|
whip = strchr(whip, '\0') + 1;
|
|
}
|
|
edo_debug << "<EOF>" << std::endl;
|
|
edo_debug.close();
|
|
}
|
|
#endif
|
|
|
|
Verify( actual_size == size );
|
|
file->AddResource(
|
|
"vehicletable",
|
|
ResourceDescription::VehicleTableResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
notation_string,
|
|
sizeof(char) * size
|
|
);
|
|
|
|
Unregister_Pointer(notation_string);
|
|
Unregister_Object(new_notation_file);
|
|
delete[] notation_string;
|
|
delete new_notation_file;
|
|
}
|