Compare commits

...
Author SHA1 Message Date
phobos2077 a4a929f620 mapper: reworked writing paths logic
- Drop "target_path_base" and other references to BIS "fallout/dev/" folder in favor of new dev_temp_path setting
- Clear separation between "mapper temp assets" (.cfg, .txt, .tgt), "shipped assets" (.map, .edg, .pro) and "runtime temp assets" (.sav)
- dev_temp_path is mounted as highest priority xbase, so all saved data goes there by default
- as a consequence, play-mode assets (maps with .sav extension and party member .pro files) are explicitly saved to master_patches_path instead of implicitly by using xfileOpen in write mode
2026-06-14 13:29:22 +02:00
phobos2077 f5ba58f27a mapper: proto edit: fix bugs, light refactor and use correct colors 2026-06-13 00:29:09 +02:00
phobos2077 9bd179167c mapper: move proto TXT handling to separate file 2026-06-12 14:50:33 +02:00
phobos2077 9e88b16d71 mapper: fix incorrect strings 2026-06-12 00:37:21 +02:00
phobos2077 15a4f4275b mapper: critter proto editing + wire up all editors 2026-06-12 00:05:34 +02:00
phobos2077 466f12b22e mapper: proto TXT support 2026-06-11 23:45:48 +02:00
phobos2077 26622f3596 mapper: name/description editing 2026-06-11 23:41:07 +02:00
phobos2077 629d1f2b7b mapper: item proto editing 2026-06-11 21:37:20 +02:00
phobos2077 1453a3e294 mapper: scenery proto editing 2026-06-11 21:13:06 +02:00
phobos2077 b5c0fbdf86 mapper: simple proto editing 2026-06-11 21:02:42 +02:00
phobos2077 948558c64c mapper: misc proto editing functions 2026-06-07 11:52:53 +02:00
phobos2077 6412d14a28 mapper: proto_edit_init, proto_remove 2026-06-07 11:15:44 +02:00
26 changed files with 3499 additions and 244 deletions
+2
View File
@@ -614,6 +614,8 @@ if((NOT ANDROID) AND (NOT IOS) AND (NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten"))
target_sources(mapper-ce PUBLIC
${FALLOUT_ENGINE_SOURCES}
${FALLOUT_PLATFORM_SOURCES}
"src/proto_txt.cc"
"src/proto_txt.h"
"src/mapper/map_edge_setup.cc"
"src/mapper/map_edge_setup.h"
"src/mapper/map_func.cc"
+6
View File
@@ -367,6 +367,12 @@ void artToggleObjectTypeHidden(int objectType)
}
}
// art_total
int art_total(int objectType)
{
return objectType >= 0 && objectType < OBJ_TYPE_COUNT ? gArtListDescriptions[objectType].fileNamesLength : 0;
}
// 0x418F7C
int artGetFidgetCount(int headFid)
{
+1
View File
@@ -123,6 +123,7 @@ void artExit();
char* artGetObjectTypeName(int objectType);
int artIsObjectTypeHidden(int objectType);
void artToggleObjectTypeHidden(int objectType);
int art_total(int objectType);
int artGetFidgetCount(int headFid);
void artRender(int fid, unsigned char* dest, int width, int height, int pitch);
int art_list_str(int fid, char* name);
+24
View File
@@ -144,6 +144,30 @@ File* fileOpen(const char* filename, const char* mode)
return xfileOpen(filename, mode);
}
File* fileOpenDirect(const char* filename, const char* mode)
{
return xfileOpenDirect(filename, mode);
}
const char* buildDataPath(const char* root, const char* relative)
{
static char path[COMPAT_MAX_PATH];
size_t rootLen = strlen(root);
bool rootHasSeparator = rootLen > 0 && (root[rootLen - 1] == '\\' || root[rootLen - 1] == '/');
snprintf(path, sizeof(path), "%s%s%s", root, rootHasSeparator ? "" : "\\", relative);
char dir[COMPAT_MAX_PATH];
snprintf(dir, sizeof(dir), "%s", path);
char* separator = strrchr(dir, '\\');
if (separator != nullptr) {
*separator = '\0';
compat_mkdir_recursive(dir);
}
return path;
}
// 0x4C5ED0 db_fprintf
int filePrintFormatted(File* stream, const char* format, ...)
{
+5
View File
@@ -18,6 +18,7 @@ int dbGetFileSize(const char* filePath, int* sizePtr);
int dbGetFileContents(const char* filePath, void* ptr);
int fileClose(File* stream);
File* fileOpen(const char* filename, const char* mode);
File* fileOpenDirect(const char* filename, const char* mode);
int filePrintFormatted(File* stream, const char* format, ...);
int fileReadChar(File* stream);
char* fileReadString(char* str, size_t size, File* stream);
@@ -111,6 +112,10 @@ void fileNameListFree(char*** fileNames, int unused);
int fileGetSize(File* stream);
void fileSetReadProgressHandler(FileReadProgressHandler* handler, int size);
// Joins [root] and [relative] into a save path and creates its directory.
// Returns a pointer to a static buffer.
const char* buildDataPath(const char* root, const char* relative);
} // namespace fallout
#endif /* DB_H */
+28 -53
View File
@@ -723,38 +723,6 @@ const char* mapBuildPath(const char* name)
return name;
}
const char* mapBuildDataSavePath(const char* relativePath)
{
static char path[COMPAT_MAX_PATH];
// Save root: the validated mapper dev_path when set, otherwise the master patches path.
const std::string& devPath = settings.mapper.dev_path;
const char* root = !devPath.empty() ? devPath.c_str() : settings.system.master_patches_path.c_str();
// Join root + relativePath, tolerating a trailing separator on the root.
size_t rootLen = strlen(root);
bool rootHasSeparator = rootLen > 0 && (root[rootLen - 1] == '\\' || root[rootLen - 1] == '/');
snprintf(path, sizeof(path), "%s%s%s", root, rootHasSeparator ? "" : "\\", relativePath);
// Ensure the directory portion exists.
char dir[COMPAT_MAX_PATH];
snprintf(dir, sizeof(dir), "%s", path);
char* separator = strrchr(dir, '\\');
if (separator != nullptr) {
*separator = '\0';
compat_mkdir_recursive(dir);
}
return path;
}
const char* mapBuildSavePath(const char* name)
{
char relativePath[COMPAT_MAX_PATH];
snprintf(relativePath, sizeof(relativePath), "MAPS\\%s", name);
return mapBuildDataSavePath(relativePath);
}
// 0x482924
int mapSetEnteringLocation(int elevation, int tile_num, int orientation)
{
@@ -1375,34 +1343,41 @@ static void _map_fix_critter_combat_data()
}
}
// map_save
// 0x483850
int _map_save()
// Saves the current map (and its .EDG sidecar) under the given save root,
// writing directly to that folder without consulting the xbase database.
int mapSaveToRoot(const char* root)
{
int rc = -1;
if (gMapHeader.name[0] != '\0') {
const char* mapFilePath = mapBuildSavePath(gMapHeader.name);
File* stream = fileOpen(mapFilePath, "wb");
if (stream != nullptr) {
rc = _map_save_file(stream);
fileClose(stream);
} else {
debugPrint("Unable to open %s to write!", gMapHeader.name);
}
if (rc == 0) {
debugPrint("%s saved.", gMapHeader.name);
// Write the edge (.EDG) sidecar alongside the map.
mapEdgeSave(gMapHeader.name);
}
} else {
if (gMapHeader.name[0] == '\0') {
debugPrint("\nError: map_save: map header corrupt!");
return -1;
}
char relativePath[COMPAT_MAX_PATH];
snprintf(relativePath, sizeof(relativePath), "MAPS\\%s", gMapHeader.name);
int rc = -1;
File* stream = fileOpenDirect(buildDataPath(root, relativePath), "wb");
if (stream != nullptr) {
rc = _map_save_file(stream);
fileClose(stream);
} else {
debugPrint("Unable to open %s to write!", gMapHeader.name);
}
if (rc == 0) {
debugPrint("%s saved.", gMapHeader.name);
mapEdgeSave(root, gMapHeader.name);
}
return rc;
}
// 0x483850 map_save
int _map_save()
{
return mapSaveToRoot(settings.system.master_patches_path.c_str());
}
// 0x483980
static int _map_save_file(File* stream)
{
+1 -6
View File
@@ -106,12 +106,7 @@ void mapNewMap();
int mapLoadByName(char* fileName);
int mapLoadById(int map_index);
const char* mapBuildPath(const char* name);
// Resolves a VFS-relative data path (e.g. "MAPS\\ARROYO.MAP") to a writable path, creating its directory.
// Save root is the validated mapper dev_path when set, otherwise the master patches path.
const char* mapBuildDataSavePath(const char* relativePath);
// Convenience wrapper around mapBuildDataSavePath for files under MAPS\, mirroring
// mapBuildPath semantics: name is the bare filename, e.g. "ARROYO.MAP".
const char* mapBuildSavePath(const char* name);
int mapSaveToRoot(const char* root);
int mapLoadSaved(char* fileName);
int mapGetLoadedAreaId();
int mapSetTransition(MapTransition* transition);
+5 -3
View File
@@ -337,7 +337,7 @@ static bool writeEdgStream(File* stream)
return true;
}
void mapEdgeSave(const char* mapName)
void mapEdgeSave(const char* root, const char* mapName)
{
int totalZones = 0;
for (const EdgeElevationData& data : edgeData) {
@@ -349,9 +349,11 @@ void mapEdgeSave(const char* mapName)
char edgName[COMPAT_MAX_PATH];
buildEdgeFileName(mapName, edgName, sizeof(edgName));
const char* edgPath = mapBuildSavePath(edgName);
char relativePath[COMPAT_MAX_PATH];
snprintf(relativePath, sizeof(relativePath), "MAPS\\%s", edgName);
const char* edgPath = buildDataPath(root, relativePath);
File* stream = fileOpen(edgPath, "wb");
File* stream = fileOpenDirect(edgPath, "wb");
if (stream == nullptr) {
debugPrint("mapEdgeSave: unable to open %s for writing\n", edgPath);
return;
+3 -3
View File
@@ -46,9 +46,9 @@ struct EdgeElevationData {
// Silently does nothing if no .edg file is found.
void mapEdgeLoad(const char* mapName);
// Writes the current in-memory edge data to the map's EDG file (mapper save path).
// Does nothing when there are no edge zones. Used by the mapper map-save flow.
void mapEdgeSave(const char* mapName);
// Writes the current in-memory edge data to the map's EDG file under the given
// save root. Does nothing when there are no edge zones.
void mapEdgeSave(const char* root, const char* mapName);
// Mutable access to a single elevation's edge data. Used by the mapper edge editor,
// which edits this data in place; the disk write happens later via mapEdgeSave.
+10 -10
View File
@@ -284,7 +284,7 @@ void map_save_dialog()
strcat(newName, ".map");
strncpy(gMapHeader.name, newName, sizeof(gMapHeader.name) - 1);
gMapHeader.name[sizeof(gMapHeader.name) - 1] = '\0';
_map_save();
mapSaveToRoot(mapperEditRoot());
}
// map_save_as
@@ -292,7 +292,7 @@ int map_save_as(const char* name)
{
strncpy(gMapHeader.name, name, sizeof(gMapHeader.name) - 1);
gMapHeader.name[sizeof(gMapHeader.name) - 1] = '\0';
return _map_save();
return mapSaveToRoot(mapperEditRoot());
}
// map_get_name
@@ -660,8 +660,8 @@ static void copy_object_to_tile_pobj(int srcFid, int dstTile, Object* srcObj, bo
// Look for an existing duplicate (same tile + same fid) before placing.
for (Object* obj = objectFindFirstAtElevation(gElevation);
obj != nullptr;
obj = objectFindNextAtElevation()) {
obj != nullptr;
obj = objectFindNextAtElevation()) {
if (obj->tile == dstTile
&& obj->fid == srcFid
&& obj != gGameMouseBouncingCursor
@@ -755,8 +755,8 @@ void copyObject(int filterType)
if (tile == -1) continue;
for (Object* obj = objectFindFirstAtElevation(elevation);
obj != nullptr;
obj = objectFindNextAtElevation()) {
obj != nullptr;
obj = objectFindNextAtElevation()) {
if (obj->tile != tile
|| obj == gDude
|| obj == gGameMouseBouncingCursor
@@ -928,8 +928,8 @@ void eraseObject()
// Find topmost (last-rendered) object at gElevation under the mouse.
Object* hit = nullptr;
for (Object* obj = objectFindFirstAtElevation(gElevation);
obj != nullptr;
obj = objectFindNextAtElevation()) {
obj != nullptr;
obj = objectFindNextAtElevation()) {
if (obj == gDude) continue;
if (PID_TYPE(obj->pid) == OBJ_TYPE_INTERFACE) continue;
if ((obj->flags & OBJECT_HIDDEN) != 0) continue;
@@ -1016,8 +1016,8 @@ static void mapper_shift_map_once(int dx, int dy)
};
std::vector<Snap> snap;
for (Object* obj = objectFindFirstAtElevation(gElevation);
obj != nullptr;
obj = objectFindNextAtElevation()) {
obj != nullptr;
obj = objectFindNextAtElevation()) {
if (obj->tile < 0 || obj->tile >= kShiftMapTiles) continue;
if (obj == gDude) continue;
if (obj == gGameMouseBouncingCursor) continue;
+35 -11
View File
@@ -13,13 +13,11 @@
#include "automap.h"
#include "character_editor.h"
#include "color.h"
#include "config.h"
#include "critter.h"
#include "db.h"
#include "debug.h"
#include "draw.h"
#include "game.h"
#include "game_config.h"
#include "game_dialog.h"
#include "game_mouse.h"
#include "game_movie.h"
@@ -776,6 +774,21 @@ static void mapperValidateDevPath()
devPath.clear();
}
// Mounts the mapper temp folder (dev_temp_path, default "dev" relative to cwd) as
// the highest-priority xbase. Mapper-only artifacts are read from here, and any
// incidental relative write lands here instead of cluttering the dev_path mod
// folder. Reverts to "dev" if explicitly cleared, and is created if missing.
static void mapperMountDevTempPath()
{
std::string& devTemp = settings.mapper.dev_temp_path;
if (devTemp.empty()) {
devTemp = "dev";
}
compat_mkdir_recursive(devTemp.c_str());
xbaseOpen(devTemp.c_str());
}
static void initMenuBar(const int screenWidth)
{
int foregroundColor = _colorTable[20052];
@@ -1011,6 +1024,7 @@ int mapper_edit_init(int argc, char** argv)
target_init();
mouseShowCursor();
mapperValidateDevPath();
mapperMountDevTempPath();
mapEdgeSetupInit();
mapEdgeSetMapperMode(true);
@@ -2288,7 +2302,10 @@ void mapper_save_toolbar()
if (dot != nullptr) *dot = '\0';
strcat(name, ".cfg");
File* stream = fileOpen(mapBuildPath(name), "wb");
char relativePath[COMPAT_MAX_PATH];
snprintf(relativePath, sizeof(relativePath), "MAPS\\%s", name);
File* stream = fileOpenDirect(buildDataPath(mapperTempRoot(), relativePath), "wb");
if (stream != nullptr) {
fileWrite(toolbar_info, sizeof(ToolbarInfo), 6, stream);
fileClose(stream);
@@ -2359,7 +2376,7 @@ void update_toolname(int* pid, int type, int id)
switch (PID_TYPE(proto->pid)) {
case OBJ_TYPE_ITEM:
windowDrawText(tool_win,
gItemTypeNames[proto->item.type],
item_pro_type[proto->item.type],
kToolNameWidth,
kToolNameX,
kToolNameY2,
@@ -2713,12 +2730,18 @@ int mapper_mark_exit_grid()
// and on editor shutdown if the user quit while still in play mode.
static void mapper_remove_tmp_map_files()
{
remove(mapBuildSavePath(kTmpMapName));
remove(mapBuildSavePath("TMP$MAP#.EDG"));
char path[COMPAT_MAX_PATH];
// The play-mode snapshot (.MAP/.EDG) and its toolbar .CFG are all mapper-only
// temp artifacts living under the temp root.
const char* tempRoot = mapperTempRoot();
snprintf(path, sizeof(path), "%s\\MAPS\\%s", tempRoot, kTmpMapName);
remove(path);
snprintf(path, sizeof(path), "%s\\MAPS\\TMP$MAP#.EDG", tempRoot);
remove(path);
snprintf(path, sizeof(path), "%s\\MAPS\\TMP$MAP#.CFG", tempRoot);
remove(path);
char cfgPath[COMPAT_MAX_PATH];
snprintf(cfgPath, sizeof(cfgPath), "%s\\MAPS\\TMP$MAP#.CFG", settings.system.master_patches_path.c_str());
remove(cfgPath);
MapDirErase("MAPS\\", "SAV");
}
@@ -2751,10 +2774,11 @@ static void mapper_enter_play_mode(Object** pHlObj1)
map_toggle_block_obj_viewing(0);
// Save current map under the temp name so we can restore on exit.
// Save current map under the temp name (to the temp root) so we can restore
// it on exit. This is mapper-only state, kept out of the dev_path mod folder.
strncpy(gMapHeader.name, kTmpMapName, sizeof(gMapHeader.name) - 1);
gMapHeader.name[sizeof(gMapHeader.name) - 1] = '\0';
_map_save();
mapSaveToRoot(mapperTempRoot());
objectSetLocation(gDude, savedCenterTile, gElevation, nullptr);
+2758 -108
View File
File diff suppressed because it is too large Load Diff
+13 -14
View File
@@ -8,6 +8,7 @@
#include "mapper/mp_proto.h"
#include "memory.h"
#include "proto.h"
#include "settings.h"
#include "window_manager_private.h"
namespace fallout {
@@ -25,15 +26,9 @@ typedef struct TargetList {
int next_tid;
} TargetList;
// 0x53F354
static char default_target_path_base[] = "\\fallout2\\dev\\proto\\";
// 0x559CC4
static TargetList targetlist = { NULL, 0, 0 };
// 0x559CD0
static char* target_path_base = default_target_path_base;
// 0x559DBC
static bool tgt_overriden = false;
@@ -59,18 +54,22 @@ bool target_overriden()
return tgt_overriden;
}
// 0x49B34C
// Builds the mapper temp-root proto directory ("<temp>\proto\[<type>]"), creating
// it on disk. For pid -1 the path keeps a trailing separator so callers can
// append "target.dat"; otherwise it ends at the per-type folder.
void target_make_path(char* path, int pid)
{
if (_cd_path_base[0] != '\0' && _cd_path_base[1] == ':') {
strncpy(path, _cd_path_base, 2);
strcat(path, target_path_base);
} else {
strcpy(path, target_path_base);
}
const char* root = mapperTempRoot();
size_t rootLen = strlen(root);
const char* separator = (rootLen > 0 && (root[rootLen - 1] == '\\' || root[rootLen - 1] == '/')) ? "" : "\\";
if (pid != -1) {
strcat(path, artGetObjectTypeName(PID_TYPE(pid)));
snprintf(path, COMPAT_MAX_PATH, "%s%sproto\\%s", root, separator, artGetObjectTypeName(PID_TYPE(pid)));
compat_mkdir_recursive(path);
} else {
snprintf(path, COMPAT_MAX_PATH, "%s%sproto", root, separator);
compat_mkdir_recursive(path);
strcat(path, "\\");
}
}
+15
View File
@@ -1,5 +1,8 @@
#include "mapper/mp_utils.h"
#include <string.h>
#include "art.h"
#include "color.h"
#include "text_font.h"
#include "window_manager_private.h"
@@ -21,4 +24,16 @@ void mapperShowMessage(const char* msg)
_win_msg(msg, 80, 80, _colorTable[31744] | FONT_SHADOW);
}
void art_name_no_ext(int fid, char* buf)
{
if (art_list_str(fid, buf) == -1) {
strcpy(buf, "None");
} else {
char* pch = strchr(buf, '.');
if (pch != nullptr) {
*pch = '\0';
}
}
}
} // namespace fallout
+3
View File
@@ -7,6 +7,9 @@ void mapperShowTimedMsg(const char* msg);
bool mapperYesNoDialog(const char* msg);
void mapperShowMessage(const char* msg);
// Resolves an art FID to its filename without extension, falling back to "None".
void art_name_no_ext(int fid, char* buf);
} // namespace fallout
#endif /* FALLOUT_MAPPER_MP_UTILS_H_ */
+38
View File
@@ -297,6 +297,44 @@ err:
return success;
}
// message_insert
bool message_insert(MessageList* msg, MessageListItem* entry)
{
if (msg == nullptr || entry == nullptr) {
return false;
}
return _message_add(msg, entry);
}
// message_save
bool message_save(MessageList* msg, const char* path)
{
if (msg == nullptr || path == nullptr) {
return false;
}
char localized_path[COMPAT_MAX_PATH];
snprintf(localized_path, sizeof(localized_path), "%s\\%s\\%s", "text", settings.system.language.c_str(), path);
File* stream = fileOpen(localized_path, "wt");
if (stream == nullptr) {
return false;
}
for (int index = 0; index < msg->entries_num; index++) {
MessageListItem* entry = &(msg->entries[index]);
filePrintFormatted(stream, "%c%d%c%c%s%c%c%s%c\n",
'{', entry->num, '}',
'{', entry->audio, '}',
'{', entry->text, '}');
}
fileClose(stream);
return true;
}
// 0x484C30 message_search
bool messageListGetItem(MessageList* msg, MessageListItem* entry)
{
+2
View File
@@ -77,6 +77,8 @@ void badwordsExit();
bool messageListInit(MessageList* msg);
bool messageListFree(MessageList* msg);
bool messageListLoad(MessageList* msg, const char* path);
bool message_insert(MessageList* msg, MessageListItem* entry);
bool message_save(MessageList* msg, const char* path);
bool messageListGetItem(MessageList* msg, MessageListItem* entry);
bool _message_make_path(char* dest, size_t size, const char* path);
char* getmsg(MessageList* msg, MessageListItem* entry, int num);
+121 -32
View File
@@ -34,7 +34,6 @@ static int protoRead(Proto* buf, File* stream);
static int protoItemDataWrite(ItemProtoData* item_data, int type, File* stream);
static int protoSceneryDataWrite(SceneryProtoData* scenery_data, int type, File* stream);
static int protoWrite(Proto* buf, File* stream);
static int _proto_load_pid(int pid, Proto** out_proto);
static int _proto_find_free_subnode(int type, Proto** out_ptr);
static void _proto_remove_some_list(int type);
static void _proto_remove_list(int type);
@@ -119,19 +118,19 @@ static int _init_true = 0;
static int _retval = 0;
// 0x66452C mp_perk_code_None
static char* _mp_perk_code_None;
char* _mp_perk_code_None;
// 0x664530 mp_perk_code_strs
static char* _mp_perk_code_strs[PERK_COUNT];
char* _mp_perk_code_strs[PERK_COUNT];
// 0x66470C mp_critter_stats_list
static char* _mp_critter_stats_list;
char* _mp_critter_stats_list;
// 0x664710 critter_stats_list_None
static char* _critter_stats_list_None;
char* _critter_stats_list_None;
// 0x664714 critter_stats_list_strs
static char* _critter_stats_list_strs[STAT_COUNT];
char* _critter_stats_list_strs[STAT_COUNT];
// Message list by object type
// 0 - pro_item.msg
@@ -142,13 +141,13 @@ static char* _critter_stats_list_strs[STAT_COUNT];
// 5 - pro_misc.msg
//
// 0x6647AC proto_msg_files
static MessageList _proto_msg_files[6];
MessageList _proto_msg_files[6];
// 0x6647DC race_type_strs
static char* gRaceTypeNames[RACE_TYPE_COUNT];
// 0x6647E4 scenery_pro_type
static char* gSceneryTypeNames[SCENERY_TYPE_COUNT];
char* gSceneryTypeNames[SCENERY_TYPE_COUNT];
// proto.msg
//
@@ -156,34 +155,34 @@ static char* gSceneryTypeNames[SCENERY_TYPE_COUNT];
MessageList gProtoMessageList;
// 0x664804 item_pro_material
static char* gMaterialTypeNames[MATERIAL_TYPE_COUNT];
char* gMaterialTypeNames[MATERIAL_TYPE_COUNT];
// "<None>" from proto.msg
//
// 0x664824 proto_none_str
char* _proto_none_str;
char* proto_none_str;
// 0x664828 body_type_strs
static char* gBodyTypeNames[BODY_TYPE_COUNT];
char* body_type_strs[BODY_TYPE_COUNT];
// 0x664834 item_pro_type
char* gItemTypeNames[ITEM_TYPE_COUNT];
char* item_pro_type[ITEM_TYPE_COUNT];
// 0x66484C
static char* gDamageTypeNames[DAMAGE_TYPE_COUNT];
char* gDamageTypeNames[DAMAGE_TYPE_COUNT];
// 0x66486C cal_type_strs
static char* gCaliberTypeNames[CALIBER_TYPE_COUNT];
char* cal_type_strs[CALIBER_TYPE_COUNT];
// Perk names.
//
// 0x6648B8 perk_code_strs
static char** _perk_code_strs;
static char** perk_code_strs;
// Stat names.
//
// 0x6648BC critter_stats_list
static char** _critter_stats_list;
static char** critter_stats_list;
// 0x49E270 proto_make_path
void proto_make_path(char* path, int pid)
@@ -334,7 +333,7 @@ int _proto_action_can_pickup(int pid)
// 0x49EAA4 proto_get_msg_info
char* protoGetMessage(int pid, int message)
{
char* messageText = _proto_none_str;
char* messageText = proto_none_str;
Proto* proto;
if (protoGetProto(pid, &proto) != -1) {
@@ -1388,7 +1387,7 @@ int protoInit()
}
_mp_critter_stats_list = _aDrugStatSpecia;
_critter_stats_list = _critter_stats_list_strs;
critter_stats_list = _critter_stats_list_strs;
_critter_stats_list_None = _aNone_1;
for (i = 0; i < STAT_COUNT; i++) {
_critter_stats_list_strs[i] = statGetName(i);
@@ -1399,7 +1398,7 @@ int protoInit()
}
_mp_perk_code_None = _aNone_1;
_perk_code_strs = _mp_perk_code_strs;
perk_code_strs = _mp_perk_code_strs;
for (i = 0; i < PERK_COUNT; i++) {
_mp_perk_code_strs[i] = perkGetName(i);
if (_mp_perk_code_strs[i] == nullptr) {
@@ -1420,7 +1419,7 @@ int protoInit()
return -1;
}
_proto_none_str = getmsg(&gProtoMessageList, &messageListItem, 10);
proto_none_str = getmsg(&gProtoMessageList, &messageListItem, 10);
// material type names
for (i = 0; i < MATERIAL_TYPE_COUNT; i++) {
@@ -1429,7 +1428,7 @@ int protoInit()
// item type names
for (i = 0; i < ITEM_TYPE_COUNT; i++) {
gItemTypeNames[i] = getmsg(&gProtoMessageList, &messageListItem, 150 + i);
item_pro_type[i] = getmsg(&gProtoMessageList, &messageListItem, 150 + i);
}
// scenery type names
@@ -1444,7 +1443,7 @@ int protoInit()
// caliber types
for (i = 0; i < CALIBER_TYPE_COUNT; i++) {
gCaliberTypeNames[i] = getmsg(&gProtoMessageList, &messageListItem, 300 + i);
cal_type_strs[i] = getmsg(&gProtoMessageList, &messageListItem, 300 + i);
}
// race types
@@ -1454,7 +1453,7 @@ int protoInit()
// body types
for (i = 0; i < BODY_TYPE_COUNT; i++) {
gBodyTypeNames[i] = getmsg(&gProtoMessageList, &messageListItem, 400 + i);
body_type_strs[i] = getmsg(&gProtoMessageList, &messageListItem, 400 + i);
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_PROTO, &gProtoMessageList);
@@ -1918,21 +1917,20 @@ static int protoWrite(Proto* proto, File* stream)
return -1;
}
// 0x4A1B30 proto_save_pid
int _proto_save_pid(int pid)
// Writes the proto's binary .pro directly into <root>\proto\<type>\, bypassing
// the xbase database so it lands in that exact folder.
static int proto_save_pid_to_root(int pid, const char* root)
{
Proto* proto;
if (protoGetProto(pid, &proto) == -1) {
return -1;
}
char path[260];
proto_make_path(path, pid);
strcat(path, "\\");
char relativePath[COMPAT_MAX_PATH];
int len = snprintf(relativePath, sizeof(relativePath), "proto\\%s\\", artGetObjectTypeName(PID_TYPE(pid)));
_proto_list_str(pid, relativePath + len);
_proto_list_str(pid, path + strlen(path));
File* stream = fileOpen(path, "wb");
File* stream = fileOpenDirect(buildDataPath(root, relativePath), "wb");
if (stream == nullptr) {
return -1;
}
@@ -1944,8 +1942,21 @@ int _proto_save_pid(int pid)
return rc;
}
// 0x4A1B30 proto_save_pid
// Runtime/savegame save: protos go to the master patches path.
int _proto_save_pid(int pid)
{
return proto_save_pid_to_root(pid, settings.system.master_patches_path.c_str());
}
// Mapper editing save: edited protos are a shipped asset, saved to the edit root.
int proto_save_pid_edit(int pid)
{
return proto_save_pid_to_root(pid, mapperEditRoot());
}
// 0x4A1C3C proto_load_pid
static int _proto_load_pid(int pid, Proto** protoPtr)
int _proto_load_pid(int pid, Proto** protoPtr)
{
char path[COMPAT_MAX_PATH];
proto_make_path(path, pid);
@@ -2120,6 +2131,84 @@ void _proto_remove_all()
}
}
// Free the now-empty tail extent and make the preceding extent the new tail.
static void _proto_drop_empty_tail(ProtoList* protoList)
{
protoList->length--;
internal_free(protoList->tail);
if (protoList->length == 0) {
protoList->head = nullptr;
protoList->tail = nullptr;
return;
}
ProtoListExtent* prev = protoList->head;
while (prev->next != protoList->tail) {
prev = prev->next;
}
prev->next = nullptr;
protoList->tail = prev;
}
// proto_remove
void proto_remove(int pid)
{
if (pid == -1) {
return;
}
int type = PID_TYPE(pid);
if (type >= 11) {
return;
}
ProtoList* protoList = &(_protoLists[type]);
ProtoListExtent* extent = protoList->head;
int index = 0;
while (extent != nullptr) {
for (index = 0; index < extent->length; index++) {
if (extent->proto[index]->pid == pid) {
break;
}
}
if (index < extent->length) {
break;
}
extent = extent->next;
}
if (extent == nullptr) {
return;
}
internal_free(extent->proto[index]);
extent->proto[index] = nullptr;
ProtoListExtent* tail = protoList->tail;
if (extent == tail && index + 1 == tail->length) {
tail->length--;
if (tail->length == 0) {
_proto_drop_empty_tail(protoList);
}
} else {
tail->length--;
extent->proto[index] = tail->proto[tail->length];
if (tail->length != 0) {
tail->proto[tail->length] = nullptr;
} else {
_proto_drop_empty_tail(protoList);
}
}
}
// proto_header_save
int proto_header_save()
{
return 0;
}
// proto_ptr
// 0x4A2108 proto_ptr
int protoGetProto(int pid, Proto** protoPtr)
+18 -2
View File
@@ -4,6 +4,7 @@
#include "db.h"
#include "message.h"
#include "obj_types.h"
#include "perk_defs.h"
#include "platform_compat.h"
#include "proto_types.h"
@@ -100,8 +101,19 @@ typedef enum PrototypeMessage {
extern char _cd_path_base[COMPAT_MAX_PATH];
extern MessageList gProtoMessageList;
extern char* _proto_none_str;
extern char* gItemTypeNames[ITEM_TYPE_COUNT];
extern MessageList _proto_msg_files[6];
extern char* proto_none_str;
extern char* item_pro_type[ITEM_TYPE_COUNT];
extern char* gMaterialTypeNames[MATERIAL_TYPE_COUNT];
extern char* gSceneryTypeNames[SCENERY_TYPE_COUNT];
extern char* gDamageTypeNames[DAMAGE_TYPE_COUNT];
extern char* body_type_strs[BODY_TYPE_COUNT];
extern char* cal_type_strs[CALIBER_TYPE_COUNT];
extern char* _mp_perk_code_strs[PERK_COUNT];
extern char* _mp_perk_code_None;
extern char* _mp_critter_stats_list;
extern char* _critter_stats_list_None;
extern char* _critter_stats_list_strs[];
void proto_make_path(char* path, int pid);
int _proto_list_str(int pid, char* proto_path);
@@ -134,11 +146,15 @@ int protoInit();
void protoReset();
void protoExit();
int _proto_save_pid(int pid);
int proto_save_pid_edit(int pid);
int proto_new(int* pid, int type);
void _proto_remove_all();
int protoGetProto(int pid, Proto** protoPtr);
int _ResetPlayer();
int proto_max_id(int type);
void proto_remove(int pid);
int proto_header_save();
int _proto_load_pid(int pid, Proto** protoPtr);
static bool isExitGridPid(int pid)
{
+1 -1
View File
@@ -266,7 +266,7 @@ int objectExamineFunc(Object* critter, Object* target, void (*fn)(const char* st
if (!scriptOverrides) {
char* description = objectGetDescription(target);
if (description != nullptr && strcmp(description, _proto_none_str) == 0) {
if (description != nullptr && strcmp(description, proto_none_str) == 0) {
description = nullptr;
}

Some files were not shown because too many files have changed in this diff Show More