mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
f5d2a43863
Before this change, Descent 3 would look for all of its game data files in a single directory. This change allows users to spread out Descent 3’s game data over multiple directories. Building Descent 3 produces multiple files that can be freely redistributed (Descent3, d3-linux.hog, online/Direct TCP~IP.d3c, etc.). Running Descent 3 requires those files and several additional files that cannot be freely redistributed. Before this change, the files that were redistributable had to be in the same directory as the files that were not redistributable. This change makes it so that they can be in separate directories. The main motivation behind this change is to allow people to package Descent 3 for Linux in a reasonable manner. For the most part, binary packages for Descent 3 will contain all of the freely redistributable components. Package managers will copy those components into system directories that are owned by root and that users probably shouldn’t edit manually. Users will then create a new directory and copy the game data from their copy of Descent 3 into that new directory. Users will then be able to run: Descent3 -setdir <path-to-proprietary-files> -additionaldir <path-to-open-source-files> The -additionaldir option can also be used to support more complicated scenarios. For example, if the user is using Debian’s game-data-packager [1], then they would do something like this: Descent3 -setdir <path-to-writable-directory> -additionaldir <path-to-gdp-directory> -additionaldir <path-to-open-source-files> The -additionaldir option can also be used to load a mod that replaces .hog files: Descent3 -setdir <path-to-base-game-data> -additionaldir <path-to-mod-files> [1]: <https://github.com/DescentDevelopers/Descent3/issues/373#issuecomment-2120330650>
3066 lines
94 KiB
C++
3066 lines
94 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* 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/>.
|
|
|
|
--- HISTORICAL COMMENTS FOLLOW ---
|
|
|
|
* $Logfile: /DescentIII/Main/manage/manage.cpp $
|
|
* $Revision: 103 $
|
|
* $Date: 10/10/01 11:32a $
|
|
* $Author: Matt $
|
|
*
|
|
* Jason should put something here
|
|
*
|
|
* $Log: /DescentIII/Main/manage/manage.cpp $
|
|
*
|
|
* 103 10/10/01 11:32a Matt
|
|
* Added system to check for errors when reading in add-on data.
|
|
*
|
|
* 102 10/08/01 1:50p Matt
|
|
* Added a case for gamefile pagetype to avoid int3
|
|
*
|
|
* 101 4/19/00 5:07p Matt
|
|
* From Duane for 1.4
|
|
* Added checks, asserts, and fixes for bad return values
|
|
*
|
|
* 100 3/20/00 12:27p Matt
|
|
* Merge of Duane's post-1.3 changes.
|
|
* Mac pilot directory stuff.
|
|
*
|
|
* 99 10/26/99 3:30p Jeff
|
|
* handle extra.gam addon tablefile
|
|
*
|
|
* 98 10/20/99 6:27p Jeff
|
|
* sped up addon page popping (by saving page offsets)
|
|
*
|
|
* 97 10/19/99 9:14p Chris
|
|
* Fixed a memory free bug
|
|
*
|
|
* 96 8/11/99 5:32p Jeff
|
|
* changes to fix addon tablefile support so it works correctly
|
|
*
|
|
* 95 7/28/99 2:29p Kevin
|
|
* Added macintosh DLL extentions (msl)
|
|
*
|
|
* 94 5/14/99 12:45p Matt
|
|
* Removed yet more static data
|
|
*
|
|
* 93 5/14/99 12:33p Matt
|
|
* Fixed another case of too much local data for the Mac.
|
|
*
|
|
* 92 5/13/99 8:36p Matt
|
|
* Made some local variables global to get around the 32K local variable
|
|
* limit on the Mac.
|
|
*
|
|
* 91 5/12/99 3:01p Matt
|
|
* Declared one texpage structure statically for all the functions that
|
|
* need it, because the Mac limits local data to 32K.
|
|
*
|
|
* 90 4/30/99 8:53p Matt
|
|
* Added a "voice" directory for gamefiles.
|
|
*
|
|
* 89 4/22/99 3:26p Jason
|
|
* added transferring of pagelocks
|
|
*
|
|
* 88 4/20/99 12:06a Jeff
|
|
* added so files to data/scripts search path
|
|
*
|
|
* 87 4/15/99 5:21p Jason
|
|
* sped up table file loading
|
|
*
|
|
* 86 4/14/99 10:46a Kevin
|
|
* Removed OutrageMessageBox from release builds
|
|
*
|
|
* 85 4/14/99 1:33a Jeff
|
|
* fixed case mismatched #includes
|
|
*
|
|
* 84 4/12/99 3:05p Jason
|
|
* changes for 256 textures
|
|
*
|
|
* 83 3/05/99 10:42a Jason
|
|
* more deletion of pagelocls
|
|
*
|
|
* 82 3/04/99 1:46p Jason
|
|
* fixed some manage problems
|
|
*
|
|
* 81 2/27/99 5:15p Jason
|
|
* fixed search path bug
|
|
*
|
|
* 80 2/17/99 12:11p Jason
|
|
* added music directory to searchable list
|
|
*
|
|
* 79 2/16/99 11:35a Samir
|
|
* added art directory.
|
|
*
|
|
* 78 2/10/99 3:47p Jason
|
|
* before doing a backup, makes sure that the tablefile version is the
|
|
* same on the net and on the local machine
|
|
*
|
|
* 77 1/29/99 6:29p Jason
|
|
* first pass at adding bumpmaps
|
|
*
|
|
* 76 1/21/99 11:16p Jeff
|
|
* pulled out some structs and defines from header files and moved them
|
|
* into separate header files so that multiplayer dlls don't require major
|
|
* game headers, just those new headers. Side effect is a shorter build
|
|
* time. Also cleaned up some header file #includes that weren't needed.
|
|
* This affected polymodel.h, object.h, player.h, vecmat.h, room.h,
|
|
* manage.h and multi.h
|
|
*
|
|
* 75 1/13/99 2:49p Jeff
|
|
* added .msg to the search path for data\scripts
|
|
*
|
|
* 74 1/13/99 7:08a Jeff
|
|
* put some #ifdef's around some window's specific code (really only used
|
|
* in the editor, but EDITOR is never defined when building manage) so it
|
|
* builds in linux
|
|
*
|
|
* 73 12/30/98 6:52p Matt
|
|
* Fixed compile warnings
|
|
*
|
|
* 72 12/29/98 4:30p Jason
|
|
* added add-on data functionality
|
|
*
|
|
* 71 12/13/98 7:51p Jeff
|
|
* only check the script directory for cpp,dll and def files
|
|
*
|
|
* 70 12/11/98 5:50p Jeff
|
|
* implemented and added changes regarding Level&Scripting manage system
|
|
* and compiler interface
|
|
*
|
|
* 69 11/28/98 2:19p Jason
|
|
* fixed stupid filecopy bug
|
|
*
|
|
* 68 11/18/98 11:02a Jason
|
|
* temp fix for table problems
|
|
*
|
|
* 67 11/16/98 3:49p Jason
|
|
* changes for manage system
|
|
*
|
|
* 66 11/16/98 2:43p Jason
|
|
* better file checking for old files
|
|
*
|
|
* 65 11/13/98 12:30p Jason
|
|
* fixed reordered pages bug
|
|
*
|
|
* 64 11/13/98 12:30p Jason
|
|
* changes for weapons
|
|
*
|
|
* 63 11/06/98 6:00p Josh
|
|
* fixed dumb bug
|
|
*
|
|
* 62 11/06/98 5:28p Josh
|
|
* FROM JASON:upped tracklock limit
|
|
*
|
|
* 61 11/06/98 12:35p Jason
|
|
* more speedups for manage system
|
|
*
|
|
* 60 11/05/98 7:55p Jason
|
|
* changes for new manage system
|
|
*
|
|
* 59 11/04/98 11:02a Jason
|
|
* added levels and briefing directories to new "old files" update method
|
|
*
|
|
* 58 11/02/98 6:35p Jason
|
|
* changes for filter
|
|
*
|
|
* 57 11/02/98 6:02p Jason
|
|
* made yes network updates much faster
|
|
*
|
|
* 56 10/15/98 8:48a Matt
|
|
* Changed some errors to use Error() instead of OutrageMessageBox()
|
|
*
|
|
* 55 10/14/98 5:15p Jason
|
|
* added version checking to the table file
|
|
*
|
|
* 54 10/12/98 11:38p Jeff
|
|
* wrapped all the Object_info[].description whenever freed...trying to
|
|
* find an obscure bug. Added icon_name to manage page of Generic
|
|
*
|
|
* 53 10/12/98 10:31a Jason
|
|
* don't seach data directories if release
|
|
*
|
|
* 52 10/09/98 4:39p Jason
|
|
* fixed local table file message
|
|
*
|
|
* 51 10/09/98 2:27p Jason
|
|
* reorganized table file system
|
|
*
|
|
* 50 10/09/98 2:40a Jason
|
|
* fixed table file issues with demo
|
|
*
|
|
* 49 10/08/98 10:03p Jason
|
|
* more filtered table file stuff
|
|
*
|
|
* 48 10/08/98 7:05p Jason
|
|
* added file filter support
|
|
*
|
|
* 47 9/28/98 6:53p Kevin
|
|
* localized some multiplayer menus
|
|
*
|
|
* 46 9/25/98 4:37p Jason
|
|
* fixed dedicated server printing out progress messages
|
|
*
|
|
* 45 9/25/98 2:53p Jason
|
|
* added progress bar
|
|
*
|
|
* 44 9/25/98 12:24p Samir
|
|
* fixed bugs for release version.
|
|
*
|
|
* 43 9/24/98 6:22p Jason
|
|
* fixed RELEASE version asking to update network files
|
|
*
|
|
* 42 9/18/98 3:58p Jason
|
|
* change weapon reordering to do countermeasure weapons after generics
|
|
*
|
|
* 41 9/15/98 4:31p Jason
|
|
* added more functionality for the dedicated server
|
|
*
|
|
* 40 9/14/98 6:28p Jason
|
|
* first pass at getting dedicated server working
|
|
*
|
|
* 39 8/25/98 3:42p Jason
|
|
* fixed generic object problems
|
|
*
|
|
* 38 8/25/98 3:25p Jason
|
|
* turned off fast load trick
|
|
*
|
|
* 37 8/17/98 4:00p Jason
|
|
* Added mprintf
|
|
*
|
|
* 36 8/15/98 5:17p Matt
|
|
* Added new Base_directory variable. Got rid of D3_LOCAL check and
|
|
* 'local directory' registry variable.
|
|
*
|
|
* 35 8/13/98 6:34p Jason
|
|
* made table file loading much faster
|
|
*
|
|
* 34 8/10/98 1:49p Samir
|
|
* added music directory.
|
|
*
|
|
* 33 8/03/98 6:44p Jason
|
|
* set custom graphics in the search path
|
|
*
|
|
* 32 7/27/98 6:25p Jeff
|
|
* added creation of custom directories
|
|
*
|
|
* 31 6/23/98 2:43p Matt
|
|
* Changed calls to OutrageMessageBox() & Debug_MessageBox() to deal with
|
|
* int return value (instead of bool).
|
|
*
|
|
* 30 6/12/98 1:06p Jason
|
|
* added smart loading from local table file
|
|
*
|
|
* 29 5/04/98 5:00p Keneta
|
|
* FROM JASON:Fixed copyfile bug
|
|
*
|
|
* 28 5/04/98 4:42p Jason
|
|
* even better error checking
|
|
*
|
|
* 26 5/04/98 4:24p Jason
|
|
* upped MAX_TRIES
|
|
*
|
|
* 25 5/04/98 4:18p Jason
|
|
* added assert to prevent table file problems
|
|
*
|
|
* 24 3/31/98 3:49p Jason
|
|
* added memory lib
|
|
*
|
|
* 23 3/19/98 3:51p Samir
|
|
* added misc data directory.
|
|
*
|
|
* 22 2/23/98 2:00p Jason
|
|
* Pop up a message box when table file couldn't be opened
|
|
*
|
|
* 21 2/06/98 12:15p Jason
|
|
* upped max times program will try to delete the table file before
|
|
* bailing
|
|
*
|
|
* 20 2/04/98 11:47a Jason
|
|
* added dynamic description field to generic pages
|
|
*
|
|
* 19 1/26/98 11:32a Jason
|
|
* upped the number of times the system will try to delete a table file
|
|
*
|
|
* 18 1/22/98 2:49p Samir
|
|
* Added D3 Local Dir to the search path.
|
|
*
|
|
* 17 1/15/98 6:22p Jason
|
|
* added safety checks so the network won't copy over a primitive you have
|
|
* held locally
|
|
*
|
|
* 16 1/15/98 4:54p Mark
|
|
* FROM JASON:Do switcheroo a few times before giving up
|
|
*
|
|
* 15 12/22/97 3:50p Chris
|
|
*
|
|
* 14 11/17/97 4:16p Jason
|
|
* added briefings directory
|
|
*
|
|
* 13 9/09/97 4:07p Matt
|
|
* Added mprintf()
|
|
*
|
|
* 12 9/04/97 2:53p Samir
|
|
* Added gamefile and generic page strings to PageNames array.
|
|
*
|
|
* 11 8/12/97 12:47p Matt
|
|
* Only copy pagefile from net if different from local copy.
|
|
* When loading pages, print different char for each type
|
|
* Show how long it took to load the pagefile
|
|
*
|
|
* 10 8/11/97 1:54p Matt
|
|
* Ripped out robot & powerup pages, and added generic page
|
|
*
|
|
* 9 8/08/97 5:17p Jason
|
|
* made it so that when you update from the network it doesn't halt other
|
|
* users
|
|
*
|
|
* 8 8/08/97 3:44p Jason
|
|
* added code to support new generic page
|
|
*
|
|
* 7 8/08/97 1:57p Matt
|
|
* Took out error message now handled by mng_MakeLocker()
|
|
*
|
|
* 6 7/29/97 12:07p Jason
|
|
* added gamefile page for auto retrieval of non-standard page types
|
|
*
|
|
* 50 6/27/97 3:11p Jason
|
|
* added room directories
|
|
*
|
|
*
|
|
* 49 6/11/97 1:07p Samir
|
|
* The removal of gameos and replaced with oeApplication, oeDatabase
|
|
*
|
|
* 48 6/10/97 5:08p Jason
|
|
* added reorderpages menu item
|
|
*
|
|
* 47 6/05/97 2:52p Jason
|
|
* added megacell functions
|
|
*
|
|
* 46 5/30/97 11:41a Jason
|
|
* made a better error message if someone already has the table file
|
|
* locked upon startup
|
|
*
|
|
* 45 5/22/97 3:08p Jason
|
|
* added the ReorderPage function
|
|
*
|
|
* 44 5/16/97 3:53p Jason
|
|
* added filepage dialog
|
|
*
|
|
* 43 5/15/97 5:56 PM Jeremy
|
|
* made initlocaltable files check if the file exists by using cfexist
|
|
* rather than trying to open the file and checking the error code
|
|
*
|
|
* 42 5/14/97 6:38p Jason
|
|
* fixed a plethora of potential problems by locking the table file when
|
|
* someone is starting up.
|
|
*
|
|
* 41 5/14/97 6:44 PM Jeremy
|
|
* fixed a bug where local dir backup directory was not being set
|
|
* correctly in init local table files
|
|
*
|
|
* 40 5/13/97 3:41p Jason
|
|
* made all manage code work with the new device independent database
|
|
*
|
|
* 39 5/08/97 12:41p Jason
|
|
* made manage system work with device dependant path names
|
|
*
|
|
* 38 4/29/97 5:07p Samir
|
|
* Added levels directory to search path
|
|
*
|
|
* 37 4/25/97 6:16p Jason
|
|
* added switcheroo function
|
|
*
|
|
* 36 4/01/97 2:13p Jason
|
|
* changes for sound page functionality
|
|
*
|
|
* 35 3/31/97 4:35p Jason
|
|
* added player ship and weapon pages
|
|
*
|
|
* 34 3/25/97 3:10p Jason
|
|
* added robots and robot page functionality
|
|
*
|
|
* 33 3/17/97 4:27p Jason
|
|
* added sounds directory to path list
|
|
*
|
|
* 32 3/14/97 7:18p Matt
|
|
* Added missing include
|
|
*
|
|
* 31 3/14/97 6:13 PM Jeremy
|
|
* changed calls to windows "MessageBox" to OutrageMessageBox, changed
|
|
* call of GetUserName to os_database->get_user_name, #included descent.h
|
|
* in order to refer to the OS_database object, unincluded <windows.h> and
|
|
* <direct.h>
|
|
*
|
|
* 30 3/13/97 7:39p Matt
|
|
* Changed code to use getenv() (ANSI-standard) instead of
|
|
* GetEnvironmentVariable()
|
|
*
|
|
* 29 3/13/97 12:34p Matt
|
|
* Changed code to not leave directory changed after checking for presence
|
|
* of a directory.
|
|
*
|
|
* 28 3/13/97 11:37a Samir
|
|
* Changed os file functions to ddio file functions
|
|
*
|
|
* 27 3/10/97 2:23p Jason
|
|
* added auto creation of models directory
|
|
*
|
|
* 26 3/07/97 1:02p Jason
|
|
* Now uses Samir's OS specific directory functions
|
|
*
|
|
* 25 3/05/97 3:10p Jason
|
|
* added more door functionality
|
|
*
|
|
* 24 3/05/97 12:16p Jason
|
|
* added code to support our new 3d doors
|
|
*
|
|
* 23 3/03/97 6:21p Matt
|
|
* Changed cfile functions to use D3 naming convention
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include <cerrno>
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <filesystem>
|
|
|
|
#include "chrono_timer.h"
|
|
#include "descent.h"
|
|
#include "manage.h"
|
|
#include "pserror.h"
|
|
#include "gametexture.h"
|
|
#include "texpage.h"
|
|
#include "doorpage.h"
|
|
#include "soundpage.h"
|
|
#include "megapage.h"
|
|
#include "shippage.h"
|
|
#include "weaponpage.h"
|
|
#include "gamefilepage.h"
|
|
#include "log.h"
|
|
#include "object.h"
|
|
#include "ddio.h"
|
|
#include "cfile.h"
|
|
#include "appdatabase.h"
|
|
#include "genericpage.h"
|
|
#include "mem.h"
|
|
#include "dedicated_server.h"
|
|
#include "init.h"
|
|
#include "stringtable.h"
|
|
#include "args.h"
|
|
#include "vclip.h"
|
|
#include "polymodel.h"
|
|
|
|
int Old_table_method = 0;
|
|
void mng_WriteNewUnknownPage(CFILE *outfile);
|
|
// This is for levels
|
|
char LocalLevelsDir[TABLE_NAME_LEN];
|
|
// This is for pages
|
|
std::filesystem::path TableLockFilename;
|
|
char TableFilename[TABLE_NAME_LEN];
|
|
char TempTableLockFilename[TABLE_NAME_LEN], TempTableFilename[TABLE_NAME_LEN];
|
|
char LocalTableFilename[TABLE_NAME_LEN], LocalTempTableFilename[TABLE_NAME_LEN];
|
|
char BackupTableFilename[TABLE_NAME_LEN], BackupLockFilename[TABLE_NAME_LEN];
|
|
std::filesystem::path ManageGraphicsDir, LocalManageGraphicsDir;
|
|
std::filesystem::path LocalModelsDir, NetModelsDir;
|
|
std::filesystem::path LocalSoundsDir, NetSoundsDir;
|
|
std::filesystem::path LocalRoomsDir, NetRoomsDir;
|
|
std::filesystem::path LocalBriefingDir, NetBriefingDir;
|
|
char LocalScriptDir[TABLE_NAME_LEN], NetScriptDir[TABLE_NAME_LEN];
|
|
std::filesystem::path LocalMiscDir, NetMiscDir;
|
|
std::filesystem::path LocalArtDir, NetArtDir;
|
|
std::filesystem::path LocalMusicDir, NetMusicDir;
|
|
std::filesystem::path LocalVoiceDir, NetVoiceDir;
|
|
std::filesystem::path NetTableDir, LocalTableDir;
|
|
char LocalD3Dir[TABLE_NAME_LEN], NetD3Dir[TABLE_NAME_LEN];
|
|
char LocalCustomGraphicsDir[TABLE_NAME_LEN];
|
|
char LocalCustomSoundsDir[TABLE_NAME_LEN];
|
|
std::filesystem::path LockerFile;
|
|
std::filesystem::path VersionFile;
|
|
char TableUser[TABLE_NAME_LEN];
|
|
char ErrorString[INFO_STRING_LEN], InfoString[INFO_STRING_LEN];
|
|
mngs_track_lock GlobalTrackLocks[MAX_TRACKLOCKS];
|
|
bool Use_old_update_method = false;
|
|
char *TablefileNameOverride = NULL;
|
|
// Only valid when first starting the editor
|
|
#define MAX_LOCKLIST_ELEMENTS 1000
|
|
mngs_Pagelock *LockList;
|
|
int Num_locklist, Starting_editor = 0, Loading_locals = 0, Fast_load_trick = 0;
|
|
#define PRIMTYPE_OOF 0
|
|
#define PRIMTYPE_OGF 1
|
|
#define PRIMTYPE_WAV 2
|
|
#define PRIMTYPE_OAF 3
|
|
#define PRIMTYPE_FILE 4
|
|
#if defined(WIN32)
|
|
FILETIME TableTimeThreshold;
|
|
// Builds a list of old files so we know which ones to update
|
|
// Searches through all our netdirectories for old files
|
|
void BuildOldFileList(FILETIME threshold);
|
|
#endif
|
|
struct old_file {
|
|
uint8_t type;
|
|
char name[PAGENAME_LEN];
|
|
};
|
|
#define MAX_OLDFILE_ELEMENTS 10000
|
|
int Num_old_files = 0;
|
|
old_file *OldFiles;
|
|
const char *PageNames[] = {"Unknown", "Texture", "Weapon", "Robot", "Powerup", "Door",
|
|
"Player ship", "Sound", "Megacell", "Files", "Generic objects"};
|
|
#ifndef RELEASE
|
|
int Network_up = 1;
|
|
int Stand_alone = 0;
|
|
#else
|
|
int Network_up = 0;
|
|
int Stand_alone = 1;
|
|
#endif
|
|
void mng_BackupTableFile();
|
|
|
|
// returns 1 if network is up, 0 if down
|
|
int mng_IsNetworkUp() {
|
|
char dir[100];
|
|
if (Stand_alone)
|
|
return 0;
|
|
|
|
char net_dir[255] = {0};
|
|
int dirlen = 255;
|
|
Database->read("net directory", net_dir, &dirlen);
|
|
if (net_dir[0] == 0)
|
|
return 0;
|
|
ddio_MakePath(dir, net_dir, "data", NULL);
|
|
|
|
std::error_code ec;
|
|
std::filesystem::create_directories(dir, ec);
|
|
if (!ec) {
|
|
char old_dir[100];
|
|
ddio_GetWorkingDir(old_dir, 100);
|
|
if (!ddio_SetWorkingDir(dir))
|
|
return 0; // network down
|
|
else {
|
|
ddio_SetWorkingDir(old_dir); // restore directory
|
|
return 1; // directory is already there
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
void ReorderPages(int);
|
|
// #define JASONS_REORDERING
|
|
void Read256TextureNames();
|
|
// Sets up our table files, get their filenames, etc.
|
|
// Returns 1 on success, zero on error
|
|
int mng_InitTableFiles() {
|
|
size_t size = TABLE_NAME_LEN;
|
|
int answer;
|
|
Database->get_user_name(TableUser, &size);
|
|
if (FindArg("-filter"))
|
|
Use_old_update_method = true;
|
|
// Read256TextureNames ();
|
|
|
|
if (FindArg("-oldmethod"))
|
|
Use_old_update_method = true;
|
|
if (mng_IsNetworkUp()) {
|
|
#ifndef RELEASE
|
|
answer = OutrageMessageBox(MBOX_YESNO, "Do you wish to update your data files from the network?\n(If NO is "
|
|
"selected then you will have to restart to use networking functions)");
|
|
#else
|
|
answer = IDNO;
|
|
#endif
|
|
if (answer == IDNO)
|
|
Network_up = 0;
|
|
else {
|
|
Network_up = 1;
|
|
#ifndef RELEASE
|
|
#if defined(WIN32)
|
|
if (cfexist("c:\\edload"))
|
|
Use_old_update_method = true;
|
|
else {
|
|
CFILE *fp = cfopen("c:\\edload", "wt");
|
|
cfclose(fp);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
} else {
|
|
LOG_WARNING << "Network is down...";
|
|
Network_up = 0;
|
|
}
|
|
|
|
if (Network_up == 0) {
|
|
mng_InitLocalTables();
|
|
mng_InitLocalDirectories();
|
|
mng_CheckToCreateLocalTables();
|
|
mng_InitTrackLocks();
|
|
} else {
|
|
// Do locals
|
|
mng_InitLocalTables();
|
|
mng_InitLocalDirectories();
|
|
mng_CheckToCreateLocalTables();
|
|
|
|
// Do network
|
|
mng_InitNetTables();
|
|
mng_InitNetDirectories();
|
|
mng_CheckToCreateNetTables();
|
|
mng_BackupTableFile();
|
|
mng_InitPagelocks();
|
|
mng_InitTrackLocks();
|
|
#ifdef JASONS_REORDERING
|
|
ReorderPages(0);
|
|
return 0;
|
|
#endif
|
|
}
|
|
return 1;
|
|
}
|
|
// Loads our tables
|
|
int mng_LoadTableFiles(int show_progress) {
|
|
if (Network_up) {
|
|
LockList = mem_rmalloc<mngs_Pagelock>(MAX_LOCKLIST_ELEMENTS);
|
|
Num_locklist = mng_GetListOfLocks(LockList, MAX_LOCKLIST_ELEMENTS, TableUser);
|
|
OldFiles = mem_rmalloc<old_file>(MAX_OLDFILE_ELEMENTS);
|
|
Num_old_files = 0;
|
|
ASSERT(OldFiles);
|
|
#if defined(WIN32)
|
|
if (TableTimeThreshold.dwHighDateTime != -1)
|
|
BuildOldFileList(TableTimeThreshold);
|
|
#endif
|
|
|
|
Starting_editor = 1;
|
|
}
|
|
int ret1, ret2;
|
|
if (Fast_load_trick && !FindArg("-filter"))
|
|
Network_up = 0;
|
|
|
|
ret1 = mng_LoadNetPages(show_progress);
|
|
if (Fast_load_trick) {
|
|
Network_up = 1;
|
|
Fast_load_trick = 0;
|
|
}
|
|
ret2 = mng_LoadLocalPages();
|
|
if (Network_up) {
|
|
Starting_editor = 0;
|
|
Num_locklist = 0;
|
|
Num_old_files = 0;
|
|
mem_free(OldFiles);
|
|
mem_free(LockList);
|
|
#ifndef RELEASE
|
|
#if defined(WIN32)
|
|
remove("c:\\edload");
|
|
#endif
|
|
#endif
|
|
}
|
|
RemapEverything();
|
|
|
|
if (!ret1 || !ret2)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
// This is for initting tables on STAND_ALONE, if the network is down, or if
|
|
// the user doesn't want network support
|
|
int mng_InitLocalTables() {
|
|
// Set the local table directory from the base directory.
|
|
auto writable_base_directory_string = cf_GetWritableBaseDirectory().u8string();
|
|
strncpy(LocalD3Dir, writable_base_directory_string.c_str(), sizeof LocalD3Dir);
|
|
LocalD3Dir[sizeof LocalD3Dir - 1] = '\0';
|
|
if (strlen(LocalD3Dir) != strlen(writable_base_directory_string.c_str())) {
|
|
LOG_WARNING << "cf_GetWritableBaseDirectory() is too long to fit in LocalD3Dir, so LocalD3Dir was truncated.";
|
|
}
|
|
LOG_INFO << "Local dir: " << LocalD3Dir;
|
|
|
|
// Make the CFILE system first look at our local directories. If the goods aren't
|
|
// found there, try out on the network
|
|
// TODO: temp variable until LocalD3Dir will be std::fs::path
|
|
std::filesystem::path localdir = std::filesystem::path(LocalD3Dir);
|
|
LocalTableDir = localdir / "data" / "tables";
|
|
LocalManageGraphicsDir = localdir / "data" / "graphics";
|
|
LocalModelsDir = localdir / "data" / "models";
|
|
LocalSoundsDir = localdir / "data" / "sounds";
|
|
ddio_MakePath(LocalCustomSoundsDir, LocalD3Dir, "custom", "sounds", NULL);
|
|
ddio_MakePath(LocalCustomGraphicsDir, LocalD3Dir, "custom", "graphics", NULL);
|
|
LocalRoomsDir = localdir / "data" / "rooms";
|
|
LocalBriefingDir = localdir / "data" / "briefings";
|
|
ddio_MakePath(LocalScriptDir, LocalD3Dir, "data", "scripts", NULL);
|
|
LocalMiscDir = localdir / "data" / "misc";
|
|
LocalArtDir = localdir / "data" / "art";
|
|
LocalMusicDir = localdir / "data" / "music";
|
|
LocalVoiceDir = localdir / "data" / "voice";
|
|
ddio_MakePath(LocalLevelsDir, LocalD3Dir, "data", "levels", NULL);
|
|
cf_SetSearchPath(LocalD3Dir);
|
|
#ifndef RELEASE
|
|
cf_SetSearchPath(LocalLevelsDir);
|
|
cf_SetSearchPath(LocalTableDir); // Local table directory
|
|
|
|
cf_SetSearchPath(LocalManageGraphicsDir);
|
|
cf_SetSearchPath(LocalModelsDir);
|
|
cf_SetSearchPath(LocalSoundsDir);
|
|
cf_SetSearchPath(LocalRoomsDir);
|
|
cf_SetSearchPath(LocalBriefingDir);
|
|
cf_SetSearchPath(LocalScriptDir, {".cpp", ".dll", ".def", ".msg", ".so", ".msl", ".dylib"});
|
|
cf_SetSearchPath(LocalMiscDir);
|
|
cf_SetSearchPath(LocalArtDir);
|
|
cf_SetSearchPath(LocalMusicDir);
|
|
cf_SetSearchPath(LocalVoiceDir);
|
|
#endif
|
|
|
|
if (Network_up) {
|
|
ddio_MakePath(LocalTableFilename, LocalTableDir.u8string().c_str(), LOCAL_TABLE, NULL);
|
|
ddio_MakePath(LocalTempTableFilename, LocalTableDir.u8string().c_str(), TEMP_LOCAL_TABLE, NULL);
|
|
} else {
|
|
strcpy(LocalTableFilename, LOCAL_TABLE);
|
|
strcpy(LocalTempTableFilename, TEMP_LOCAL_TABLE);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
int mng_InitNetTables() {
|
|
char dir[255];
|
|
int dirlen = 255;
|
|
Database->read("net directory", dir, &dirlen);
|
|
if (dir[0] == 0)
|
|
Error("D3_DIR environment variable not set.");
|
|
|
|
strcpy(NetD3Dir, dir);
|
|
LOG_INFO << "Net dir: " << NetD3Dir;
|
|
// TODO: temp variable until NetD3Dir will be std::fs::path
|
|
std::filesystem::path netdir = std::filesystem::path(NetD3Dir);
|
|
|
|
NetModelsDir = netdir / "data" / "models";
|
|
NetSoundsDir = netdir / "data" / "sounds";
|
|
NetRoomsDir = netdir / "data" / "rooms";
|
|
NetBriefingDir = netdir / "data" / "briefings";
|
|
ddio_MakePath(NetScriptDir, NetD3Dir, "data", "scripts", NULL);
|
|
NetMiscDir = netdir / "data" / "misc";
|
|
ManageGraphicsDir = netdir / "data" / "graphics";
|
|
NetTableDir = netdir / "data" / "tables";
|
|
NetArtDir = netdir / "data" / "art";
|
|
NetMusicDir = netdir / "data" / "music";
|
|
NetVoiceDir = netdir / "data" / "voice";
|
|
TableLockFilename = NetTableDir / "table.lok";
|
|
ddio_MakePath(BackupLockFilename, NetTableDir.u8string().c_str(), "tablelok.bak", NULL);
|
|
ddio_MakePath(BackupTableFilename, NetTableDir.u8string().c_str(), "table.bak", NULL);
|
|
ddio_MakePath(TableFilename, NetTableDir.u8string().c_str(), NET_TABLE, NULL);
|
|
ddio_MakePath(TempTableLockFilename, NetTableDir.u8string().c_str(), "lock.tmp", NULL);
|
|
ddio_MakePath(TempTableFilename, NetTableDir.u8string().c_str(), TEMP_NET_TABLE, NULL);
|
|
LockerFile = NetTableDir / "locker";
|
|
VersionFile = NetTableDir / "TableVersion";
|
|
|
|
cf_SetSearchPath(ManageGraphicsDir);
|
|
cf_SetSearchPath(NetModelsDir);
|
|
cf_SetSearchPath(NetSoundsDir);
|
|
cf_SetSearchPath(NetRoomsDir);
|
|
cf_SetSearchPath(NetMiscDir);
|
|
cf_SetSearchPath(NetMusicDir);
|
|
cf_SetSearchPath(NetVoiceDir);
|
|
return 1;
|
|
}
|
|
void mng_CheckToCreateNetTables() {
|
|
CFILE *infile, *outfile;
|
|
|
|
ASSERT(Stand_alone != 1);
|
|
|
|
infile = (CFILE *)cfopen(TableFilename, "rb");
|
|
if (infile == NULL) {
|
|
if (errno == ENOENT) {
|
|
outfile = (CFILE *)cfopen(TableFilename, "wb");
|
|
if (!outfile) {
|
|
LOG_WARNING << "Error creating table file! The network must be down...";
|
|
Network_up = 0;
|
|
} else {
|
|
mng_WriteNewUnknownPage(outfile);
|
|
cfclose(outfile);
|
|
}
|
|
} else {
|
|
LOG_WARNING << "Error creating table file! The network must be down...";
|
|
Network_up = 0;
|
|
}
|
|
}
|
|
|
|
if (infile)
|
|
cfclose(infile);
|
|
}
|
|
// Checks to see if there is a table file...if not, create one with a dummy page
|
|
void mng_CheckToCreateLocalTables() {
|
|
CFILE *outfile;
|
|
|
|
if (!Network_up) {
|
|
strcpy(TableFilename, NET_TABLE);
|
|
LOG_DEBUG << "table filename = " << TableFilename;
|
|
return;
|
|
}
|
|
|
|
if (!cfexist(LocalTableFilename)) {
|
|
outfile = (CFILE *)cfopen(LocalTableFilename, "wb");
|
|
if (!outfile) {
|
|
Error("Error creating local table file!");
|
|
return;
|
|
} else {
|
|
mng_WriteNewUnknownPage(outfile);
|
|
cfclose(outfile);
|
|
}
|
|
}
|
|
}
|
|
// Creates directories if needed
|
|
void mng_InitLocalDirectories() {
|
|
std::filesystem::path dir = LocalD3Dir;
|
|
std::error_code ec;
|
|
std::filesystem::create_directories(dir / "custom", ec);
|
|
std::filesystem::create_directories(dir / "custom" / "graphics", ec);
|
|
std::filesystem::create_directories(dir / "custom" / "sounds", ec);
|
|
std::filesystem::create_directories(dir / "custom" / "settings", ec);
|
|
|
|
cf_SetSearchPath(LocalCustomGraphicsDir);
|
|
cf_SetSearchPath(LocalCustomSoundsDir);
|
|
|
|
if (Network_up) {
|
|
std::filesystem::create_directories(dir / "data", ec);
|
|
std::filesystem::create_directories(dir / "data" / "tables", ec);
|
|
std::filesystem::create_directories(dir / "data" / "graphics", ec);
|
|
std::filesystem::create_directories(dir / "data" / "sounds", ec);
|
|
std::filesystem::create_directories(dir / "data" / "rooms", ec);
|
|
std::filesystem::create_directories(dir / "data" / "levels", ec);
|
|
std::filesystem::create_directories(dir / "data" / "models", ec);
|
|
std::filesystem::create_directories(dir / "data" / "briefings", ec);
|
|
std::filesystem::create_directories(dir / "data" / "scripts", ec);
|
|
std::filesystem::create_directories(dir / "data" / "misc", ec);
|
|
std::filesystem::create_directories(dir / "data" / "art", ec);
|
|
std::filesystem::create_directories(dir / "data" / "music", ec);
|
|
std::filesystem::create_directories(dir / "data" / "voice", ec);
|
|
}
|
|
}
|
|
|
|
void mng_InitNetDirectories() {
|
|
std::filesystem::path dir = NetD3Dir;
|
|
std::error_code ec;
|
|
|
|
if (Stand_alone)
|
|
return;
|
|
std::filesystem::create_directories(dir / "data", ec);
|
|
std::filesystem::create_directories(dir / "data" / "tables", ec);
|
|
std::filesystem::create_directories(dir / "data" / "graphics", ec);
|
|
std::filesystem::create_directories(dir / "data" / "sounds", ec);
|
|
std::filesystem::create_directories(dir / "data" / "rooms", ec);
|
|
std::filesystem::create_directories(dir / "data" / "levels", ec);
|
|
std::filesystem::create_directories(dir / "data" / "models", ec);
|
|
std::filesystem::create_directories(dir / "data" / "briefings", ec);
|
|
std::filesystem::create_directories(dir / "data" / "scripts", ec);
|
|
std::filesystem::create_directories(dir / "data" / "misc", ec);
|
|
std::filesystem::create_directories(dir / "data" / "art", ec);
|
|
std::filesystem::create_directories(dir / "data" / "music", ec);
|
|
std::filesystem::create_directories(dir / "data" / "voice", ec);
|
|
}
|
|
|
|
extern int TableVersionCurrent();
|
|
#if !defined(WIN32)
|
|
void mng_BackupTableFile() {}
|
|
#else
|
|
void mng_BackupTableFile() {
|
|
Fast_load_trick = 0;
|
|
if (!TableVersionCurrent()) {
|
|
Error("You must do a source update and recompile. The data on the network is newer that your sourcecode.");
|
|
return;
|
|
}
|
|
|
|
std::filesystem::path str = LocalTableDir / NET_TABLE;
|
|
if (!cfexist(str)) {
|
|
TableTimeThreshold.dwHighDateTime = 0;
|
|
TableTimeThreshold.dwLowDateTime = 0;
|
|
} else {
|
|
WIN32_FIND_DATA filedata;
|
|
HANDLE filehandle = FindFirstFile(str.u8string().c_str(), &filedata);
|
|
if (filehandle == INVALID_HANDLE_VALUE) {
|
|
Error("Couldn't open net table file for some reason!");
|
|
return;
|
|
}
|
|
TableTimeThreshold = filedata.ftLastWriteTime;
|
|
FindClose(filehandle);
|
|
}
|
|
|
|
if (!cfexist(str) || cf_Diff(str, TableFilename)) {
|
|
LOG_INFO << "Making local copy of table file.";
|
|
|
|
if (!cf_CopyFile(str, TableFilename, 1))
|
|
Error("There was an error making a backup copy of the table file.\n");
|
|
str = LocalTableDir / "tablelok.loc";
|
|
if (!cf_CopyFile(str, TableLockFilename, 1))
|
|
Error("There was an error making a backup copy of the locker table file.\n");
|
|
} else {
|
|
LOG_INFO << "Local table file same as network copy.";
|
|
TableTimeThreshold.dwHighDateTime = -1;
|
|
Fast_load_trick = 1;
|
|
}
|
|
}
|
|
#endif
|
|
void mng_WriteUnknownPage(CFILE *outfile) {
|
|
// Function for writing out "undefined" page...useful for placeholding
|
|
cf_WriteByte(outfile, PAGETYPE_UNKNOWN);
|
|
}
|
|
void mng_WriteNewUnknownPage(CFILE *outfile) {
|
|
// Function for writing out "undefined" page...useful for placeholding
|
|
int offset = StartManagePage(outfile, PAGETYPE_UNKNOWN);
|
|
EndManagePage(outfile, offset);
|
|
}
|
|
// Clear out tracklocks
|
|
void mng_InitTrackLocks() {
|
|
for (int i = 0; i < MAX_TRACKLOCKS; i++) {
|
|
GlobalTrackLocks[i].used = 0;
|
|
GlobalTrackLocks[i].pagetype = PAGETYPE_UNKNOWN;
|
|
GlobalTrackLocks[i].name[0] = 0;
|
|
}
|
|
}
|
|
// Given a name, returns the index of the tracklock with that name
|
|
// -1 indicates that it wasn't found
|
|
int mng_FindTrackLock(char *name, int pagetype) {
|
|
int i;
|
|
for (i = 0; i < MAX_TRACKLOCKS; i++) {
|
|
if (GlobalTrackLocks[i].used && GlobalTrackLocks[i].pagetype == pagetype &&
|
|
!stricmp(name, GlobalTrackLocks[i].name))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
// Searches through global array of tracklocks and returns first free one
|
|
// Sets the tracklock to be named "name" and its type "pagetype"
|
|
// returns -1 if none free
|
|
int mng_AllocTrackLock(char *name, int pagetype) {
|
|
int i;
|
|
for (i = 0; i < MAX_TRACKLOCKS; i++)
|
|
if (GlobalTrackLocks[i].used == 0) {
|
|
strcpy(GlobalTrackLocks[i].name, name);
|
|
GlobalTrackLocks[i].pagetype = pagetype;
|
|
GlobalTrackLocks[i].used = 1;
|
|
LOG_DEBUG.printf("Tracklock %s allocated.", name);
|
|
return i;
|
|
}
|
|
Error("Couldn't get a free tracklock!");
|
|
return -1;
|
|
}
|
|
// Frees a tracklock
|
|
void mng_FreeTrackLock(int n) {
|
|
LOG_DEBUG.printf("Tracklock %s freed.", GlobalTrackLocks[n].name);
|
|
GlobalTrackLocks[n].pagetype = PAGETYPE_UNKNOWN;
|
|
GlobalTrackLocks[n].used = 0;
|
|
GlobalTrackLocks[n].name[0] = 0;
|
|
}
|
|
// Displays all the network locks of "name"
|
|
void mng_DisplayLockList(char *name) {
|
|
mngs_Pagelock list[100];
|
|
char temp[200];
|
|
int max = 100;
|
|
int num, i;
|
|
int length = 0;
|
|
char str[5000];
|
|
#ifndef RELEASE
|
|
// Get the list
|
|
if ((num = mng_GetListOfLocks(list, max, name)) < 0) {
|
|
OutrageMessageBox(MBOX_OK, ErrorString);
|
|
return;
|
|
} else if (num == 0) {
|
|
OutrageMessageBox(MBOX_OK, "User has no pages locked.");
|
|
return;
|
|
}
|
|
|
|
// Make a large string with all the info in it
|
|
snprintf(str, sizeof(str), "User %s has the following pages locked:\n\n", TableUser);
|
|
for (i = 0; i < num; i++) {
|
|
snprintf(temp, sizeof(temp), "%s:%s", PageNames[list[i].pagetype], list[i].name);
|
|
strncat(str, temp, sizeof(str) - strlen(str) - 1);
|
|
strncat(str, "\n", sizeof(str) - strlen(str) - 1);
|
|
length += strlen(temp);
|
|
if (length > 5000 - 100)
|
|
break;
|
|
}
|
|
// Display that string
|
|
OutrageMessageBox(MBOX_OK, str);
|
|
#endif
|
|
}
|
|
// Declare these here because it's too big to put on the stack on the Mac
|
|
static mngs_texture_page texpage;
|
|
static mngs_door_page doorpage;
|
|
static mngs_generic_page genericpage;
|
|
static mngs_sound_page soundpage;
|
|
static mngs_megacell_page megacellpage;
|
|
static mngs_ship_page shippage;
|
|
static mngs_weapon_page weaponpage;
|
|
static mngs_gamefile_page gamefilepage;
|
|
// IF YOU ADD ANY NEW PAGETYPE YOU MUST CHANGE THE FUNCTIONS LISTED UNDER THIS LINE
|
|
// TO DEAL WITH YOUR PAGE TYPE. IF YOU FORGET, YOU CAN CORRUPT THE PAGEFILE!!!!!
|
|
//------------------------------------------------------------------------------
|
|
// Given a pagetype, reads it in but discards it. Useful for parsing.
|
|
void mng_ReadDummyPage(CFILE *infile, uint8_t pagetype) {
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE:
|
|
mng_ReadNewTexturePage(infile, &texpage);
|
|
break;
|
|
case PAGETYPE_POWERUP:
|
|
case PAGETYPE_ROBOT:
|
|
Error("Your local table file is invalid. You must update from the network.");
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
mng_ReadNewDoorPage(infile, &doorpage);
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
mng_ReadNewGenericPage(infile, &genericpage);
|
|
if (genericpage.objinfo_struct.description != NULL) {
|
|
mem_free(genericpage.objinfo_struct.description);
|
|
genericpage.objinfo_struct.description = NULL;
|
|
}
|
|
break;
|
|
case PAGETYPE_GAMEFILE:
|
|
mng_ReadNewGamefilePage(infile, &gamefilepage);
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
mng_ReadNewSoundPage(infile, &soundpage);
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
mng_ReadNewShipPage(infile, &shippage);
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
mng_ReadNewWeaponPage(infile, &weaponpage);
|
|
break;
|
|
case PAGETYPE_MEGACELL:
|
|
mng_ReadNewMegacellPage(infile, &megacellpage);
|
|
break;
|
|
case PAGETYPE_UNKNOWN:
|
|
break;
|
|
default:
|
|
Int3(); // unrecognized pagetype
|
|
break;
|
|
}
|
|
}
|
|
// Reads a page in that we don't care about, and writes it right back out
|
|
// This is useful for replacing a specific page in a file but ignoring others
|
|
void mng_ReadWriteDummyPage(CFILE *infile, CFILE *outfile, uint8_t pagetype) {
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE:
|
|
// Read it in, write it out.
|
|
mng_ReadNewTexturePage(infile, &texpage);
|
|
mng_WriteNewTexturePage(outfile, &texpage);
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
// Read it in, write it out.
|
|
mng_ReadNewDoorPage(infile, &doorpage);
|
|
mng_WriteNewDoorPage(outfile, &doorpage);
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
// Read it in, write it out.
|
|
mng_ReadNewGenericPage(infile, &genericpage);
|
|
mng_WriteNewGenericPage(outfile, &genericpage);
|
|
if (genericpage.objinfo_struct.description != NULL) {
|
|
mem_free(genericpage.objinfo_struct.description);
|
|
genericpage.objinfo_struct.description = NULL;
|
|
}
|
|
break;
|
|
case PAGETYPE_GAMEFILE:
|
|
// Read it in, write it out.
|
|
mng_ReadNewGamefilePage(infile, &gamefilepage);
|
|
mng_WriteNewGamefilePage(outfile, &gamefilepage);
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
// Read it in, write it out.
|
|
mng_ReadNewSoundPage(infile, &soundpage);
|
|
mng_WriteNewSoundPage(outfile, &soundpage);
|
|
break;
|
|
case PAGETYPE_MEGACELL:
|
|
// Read it in, write it out.
|
|
mng_ReadNewMegacellPage(infile, &megacellpage);
|
|
mng_WriteNewMegacellPage(outfile, &megacellpage);
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
// Read it in, write it out.
|
|
mng_ReadNewShipPage(infile, &shippage);
|
|
mng_WriteNewShipPage(outfile, &shippage);
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
// Read it in, write it out.
|
|
mng_ReadNewWeaponPage(infile, &weaponpage);
|
|
mng_WriteNewWeaponPage(outfile, &weaponpage);
|
|
break;
|
|
case PAGETYPE_UNKNOWN:
|
|
mng_WriteNewUnknownPage(outfile);
|
|
break;
|
|
default:
|
|
Int3(); // unrecognized pagetype
|
|
break;
|
|
}
|
|
}
|
|
// Renames a page on the network
|
|
// This function is called when you rename your object, regardless if you check
|
|
// it in
|
|
int mng_RenamePage(char *oldname, char *newname, int pagetype) {
|
|
int l, i;
|
|
mngs_Pagelock pl;
|
|
char oname[PAGENAME_LEN];
|
|
|
|
LOG_INFO.printf("Renaming %s to %s...", oldname, newname);
|
|
strcpy(oname, oldname);
|
|
strcpy(pl.name, oname);
|
|
pl.pagetype = pagetype;
|
|
// Make sure we own it
|
|
l = mng_CheckIfPageOwned(&pl, TableUser);
|
|
ASSERT(l == 1);
|
|
strcpy(pl.name, newname);
|
|
strcpy(pl.holder, TableUser);
|
|
|
|
// First, change the name of the network pagelock
|
|
l = mng_ReplacePagelock(oname, &pl);
|
|
ASSERT(l == 1);
|
|
switch (pagetype) {
|
|
// Find the page type with this name and rename it
|
|
case PAGETYPE_TEXTURE:
|
|
i = FindTextureName(oname);
|
|
ASSERT(i != -1);
|
|
strcpy(GameTextures[i].name, newname);
|
|
|
|
l = mng_ReplacePage(oname, GameTextures[i].name, i, PAGETYPE_TEXTURE, 0);
|
|
ASSERT(l == 1);
|
|
if (mng_FindTrackLock(oname, PAGETYPE_TEXTURE) != -1)
|
|
mng_ReplacePage(oname, GameTextures[i].name, i, PAGETYPE_TEXTURE, 1);
|
|
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
i = FindDoorName(oname);
|
|
ASSERT(i != -1);
|
|
strcpy(Doors[i].name, newname);
|
|
l = mng_ReplacePage(oname, Doors[i].name, i, PAGETYPE_DOOR, 0);
|
|
if (mng_FindTrackLock(oname, PAGETYPE_DOOR) != -1)
|
|
mng_ReplacePage(oname, Doors[i].name, i, PAGETYPE_DOOR, 1);
|
|
ASSERT(l == 1);
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
i = FindObjectIDName(oname);
|
|
ASSERT(i != -1);
|
|
strcpy(Object_info[i].name, newname);
|
|
|
|
l = mng_ReplacePage(oname, Object_info[i].name, i, PAGETYPE_GENERIC, 0);
|
|
|
|
if (mng_FindTrackLock(oname, PAGETYPE_GENERIC) != -1)
|
|
mng_ReplacePage(oname, Object_info[i].name, i, PAGETYPE_GENERIC, 1);
|
|
|
|
ASSERT(l == 1);
|
|
break;
|
|
case PAGETYPE_GAMEFILE:
|
|
i = FindGamefileName(oname);
|
|
ASSERT(i != -1);
|
|
strcpy(Gamefiles[i].name, newname);
|
|
|
|
l = mng_ReplacePage(oname, Gamefiles[i].name, i, PAGETYPE_GAMEFILE, 0);
|
|
if (mng_FindTrackLock(oname, PAGETYPE_GAMEFILE) != -1)
|
|
mng_ReplacePage(oname, Gamefiles[i].name, i, PAGETYPE_GAMEFILE, 1);
|
|
ASSERT(l == 1);
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
i = FindSoundName(oname);
|
|
ASSERT(i != -1);
|
|
strcpy(Sounds[i].name, newname);
|
|
l = mng_ReplacePage(oname, Sounds[i].name, i, PAGETYPE_SOUND, 0);
|
|
if (mng_FindTrackLock(oname, PAGETYPE_SOUND) != -1)
|
|
mng_ReplacePage(oname, Sounds[i].name, i, PAGETYPE_SOUND, 1);
|
|
ASSERT(l == 1);
|
|
break;
|
|
case PAGETYPE_MEGACELL:
|
|
i = FindMegacellName(oname);
|
|
ASSERT(i != -1);
|
|
strcpy(Megacells[i].name, newname);
|
|
l = mng_ReplacePage(oname, Megacells[i].name, i, PAGETYPE_MEGACELL, 0);
|
|
|
|
if (mng_FindTrackLock(oname, PAGETYPE_MEGACELL) != -1)
|
|
mng_ReplacePage(oname, Megacells[i].name, i, PAGETYPE_MEGACELL, 1);
|
|
ASSERT(l == 1);
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
i = FindShipName(oname);
|
|
ASSERT(i != -1);
|
|
strcpy(Ships[i].name, newname);
|
|
|
|
l = mng_ReplacePage(oname, Ships[i].name, i, PAGETYPE_SHIP, 0);
|
|
if (mng_FindTrackLock(oname, PAGETYPE_SHIP) != -1)
|
|
mng_ReplacePage(oname, Ships[i].name, i, PAGETYPE_SHIP, 1);
|
|
|
|
ASSERT(l == 1);
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
i = FindWeaponName(oname);
|
|
ASSERT(i != -1);
|
|
strcpy(Weapons[i].name, newname);
|
|
|
|
l = mng_ReplacePage(oname, Weapons[i].name, i, PAGETYPE_WEAPON, 0);
|
|
if (mng_FindTrackLock(oname, PAGETYPE_WEAPON) != -1)
|
|
mng_ReplacePage(oname, Weapons[i].name, i, PAGETYPE_WEAPON, 1);
|
|
ASSERT(l == 1);
|
|
break;
|
|
default:
|
|
Int3(); // Unknown type, get Jason
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
#define PROGRESS_PERCENTAGE_THRESHOLD 20
|
|
// This is the function that opens the table files and reads in the individual pages
|
|
// If you want your data to be in the game, it must hook into this function
|
|
int mng_LoadNetPages(int show_progress) {
|
|
CFILE *infile;
|
|
uint8_t pagetype;
|
|
std::filesystem::path tablename;
|
|
float start_time;
|
|
int n_pages = 0;
|
|
int total_bytes;
|
|
int current_byte;
|
|
float progress;
|
|
int int_progress = 0;
|
|
int len;
|
|
|
|
LOG_INFO << "Loading pages...";
|
|
if (Dedicated_server)
|
|
show_progress = 0; // turn off progress meter for dedicated server
|
|
// If the network is up we still want to read from the local table because it
|
|
// will allow others to start the game at the same time
|
|
if (Network_up) {
|
|
int farg = FindArg("-filter");
|
|
if (farg)
|
|
tablename = GameArgs[farg + 1];
|
|
else {
|
|
tablename = LocalTableDir / NET_TABLE;
|
|
}
|
|
infile = cfopen(tablename, "rb");
|
|
} else
|
|
infile = cfopen(TableFilename, "rb");
|
|
if (!infile) {
|
|
LOG_ERROR.printf("Couldn't open table file (%s) to read pages!\n", TableFilename);
|
|
Error("Cannot open table file <%s>", TableFilename);
|
|
return 0;
|
|
}
|
|
if (show_progress) {
|
|
cfseek(infile, 0, SEEK_END);
|
|
total_bytes = cftell(infile);
|
|
cfseek(infile, 0, SEEK_SET);
|
|
}
|
|
start_time = timer_GetTime();
|
|
while (!cfeof(infile)) {
|
|
// Read in a pagetype. If it is a page we recognize, load it.
|
|
// mprintf(0,".");
|
|
if (show_progress) {
|
|
|
|
current_byte = cftell(infile);
|
|
progress = (float)current_byte / (float)total_bytes;
|
|
progress *= PROGRESS_PERCENTAGE_THRESHOLD;
|
|
int temp_int_progress = progress;
|
|
if (temp_int_progress > int_progress) {
|
|
int_progress = temp_int_progress;
|
|
InitMessage(TXT_INITDATA, progress / PROGRESS_PERCENTAGE_THRESHOLD);
|
|
}
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
if (!Old_table_method)
|
|
len = cf_ReadInt(infile);
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE:
|
|
mng_LoadNetTexturePage(infile);
|
|
break;
|
|
case PAGETYPE_POWERUP:
|
|
case PAGETYPE_ROBOT:
|
|
Error("Your local table file is invalid. You must update from the network.");
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
mng_LoadNetDoorPage(infile);
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
mng_LoadNetGenericPage(infile);
|
|
break;
|
|
case PAGETYPE_GAMEFILE:
|
|
mng_LoadNetGamefilePage(infile);
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
mng_LoadNetSoundPage(infile);
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
mng_LoadNetShipPage(infile);
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
mng_LoadNetWeaponPage(infile);
|
|
break;
|
|
case PAGETYPE_MEGACELL:
|
|
mng_LoadNetMegacellPage(infile);
|
|
break;
|
|
case PAGETYPE_UNKNOWN:
|
|
break;
|
|
default:
|
|
Int3(); // Unrecognized pagetype, possible corrupt data following
|
|
return 0;
|
|
break;
|
|
}
|
|
n_pages++;
|
|
}
|
|
LOG_INFO.printf("%d pages read in %.1f seconds.", n_pages, timer_GetTime() - start_time);
|
|
|
|
cfclose(infile);
|
|
|
|
// attempt to load extra.gam if found
|
|
char name_override[256];
|
|
strcpy(name_override, "extra.gam");
|
|
infile = cfopen(name_override, "rb");
|
|
if (!infile)
|
|
return 1;
|
|
|
|
LOG_INFO << "Loading extra.gam";
|
|
n_pages = 0;
|
|
|
|
TablefileNameOverride = name_override;
|
|
|
|
while (!cfeof(infile)) {
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE:
|
|
mng_LoadNetTexturePage(infile, true);
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
mng_LoadNetDoorPage(infile, true);
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
mng_LoadNetGenericPage(infile, true);
|
|
break;
|
|
case PAGETYPE_GAMEFILE:
|
|
mng_LoadNetGamefilePage(infile, true);
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
mng_LoadNetSoundPage(infile, true);
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
mng_LoadNetShipPage(infile, true);
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
mng_LoadNetWeaponPage(infile, true);
|
|
break;
|
|
case PAGETYPE_UNKNOWN:
|
|
break;
|
|
default:
|
|
Int3(); // Unrecognized pagetype, possible corrupt data following
|
|
cfclose(infile);
|
|
TablefileNameOverride = NULL;
|
|
return 0;
|
|
break;
|
|
}
|
|
n_pages++;
|
|
}
|
|
LOG_INFO.printf("%d extra pages read.", n_pages);
|
|
TablefileNameOverride = NULL;
|
|
cfclose(infile);
|
|
return 1;
|
|
}
|
|
// Loads and allocs all pages found locally
|
|
int mng_LoadLocalPages() {
|
|
CFILE *infile;
|
|
uint8_t pagetype;
|
|
int len;
|
|
|
|
LOG_INFO << "Overlaying local pages...";
|
|
infile = cfopen(LocalTableFilename, "rb");
|
|
if (!infile) {
|
|
LOG_WARNING.printf("Couldn't open local table file (%s) to read pages!", LocalTableFilename);
|
|
return 1;
|
|
}
|
|
Loading_locals = 1;
|
|
while (!cfeof(infile)) {
|
|
// Read in a pagetype. If it is a page we recognize, load it.
|
|
|
|
pagetype = cf_ReadByte(infile);
|
|
if (!Old_table_method)
|
|
len = cf_ReadInt(infile);
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE:
|
|
mng_LoadLocalTexturePage(infile);
|
|
break;
|
|
case PAGETYPE_POWERUP:
|
|
case PAGETYPE_ROBOT:
|
|
Error("Your local table file is invalid. You must update from the network.");
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
mng_LoadLocalDoorPage(infile);
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
mng_LoadLocalGenericPage(infile);
|
|
break;
|
|
case PAGETYPE_GAMEFILE:
|
|
mng_LoadLocalGamefilePage(infile);
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
mng_LoadLocalSoundPage(infile);
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
mng_LoadLocalShipPage(infile);
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
mng_LoadLocalWeaponPage(infile);
|
|
break;
|
|
case PAGETYPE_MEGACELL:
|
|
mng_LoadLocalMegacellPage(infile);
|
|
break;
|
|
case PAGETYPE_UNKNOWN:
|
|
break;
|
|
default:
|
|
Int3(); // Unrecognized pagetype, possible corrupt data following
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
cfclose(infile);
|
|
Loading_locals = 0;
|
|
return 1;
|
|
}
|
|
#define MAX_TRIES 10000
|
|
// Removes a file, then renames another file to be the removed file. Get it?
|
|
// Returns 1 on success, else 0 on fail
|
|
int SwitcherooFiles(const char *name, char *tempname) {
|
|
/*// If we're changing the net table file, make a backup first!
|
|
if ((!stricmp (name,TableFilename)))
|
|
{
|
|
cf_CopyFile (BackupTableFilename,TableFilename);
|
|
cf_CopyFile (BackupLockFilename,TableLockFilename);
|
|
}*/
|
|
int num_tries = 0;
|
|
while (!ddio_DeleteFile(name) && num_tries < MAX_TRIES) {
|
|
D3::ChronoTimer::SleepMS(100);
|
|
num_tries++;
|
|
}
|
|
if (num_tries >= MAX_TRIES) {
|
|
strcpy(ErrorString, "MANAGE:There was a problem deleting the table file.");
|
|
ASSERT(0); // GET JASON IMMEDIATELY
|
|
Int3();
|
|
return (0);
|
|
}
|
|
num_tries = 0;
|
|
while ((rename(tempname, name)) && num_tries <= MAX_TRIES) {
|
|
D3::ChronoTimer::SleepMS(100);
|
|
num_tries++;
|
|
}
|
|
if (num_tries >= MAX_TRIES) {
|
|
strcpy(ErrorString, "MANAGE:There was a problem renaming the temp file.");
|
|
ASSERT(0); // Get JASON IMMEDIATELY
|
|
Int3();
|
|
return (0);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void mng_TransferPages() {
|
|
CFILE *infile, *outfile;
|
|
int pagetype;
|
|
int num_tracklocks = 0;
|
|
LOG_INFO << "Transferring pages, please wait...";
|
|
if (!mng_MakeLocker())
|
|
return;
|
|
infile = cfopen(TableFilename, "rb");
|
|
|
|
if (!infile) {
|
|
LOG_ERROR << "Couldn't open table file to transfer!";
|
|
Int3();
|
|
return;
|
|
}
|
|
auto local_tracklocks = mem_rmalloc<mngs_track_lock>(5000);
|
|
// Do textures
|
|
int done = 0;
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
int len = cf_ReadInt(infile);
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE:
|
|
mng_ReadNewTexturePage(infile, &texpage);
|
|
strcpy(local_tracklocks[num_tracklocks].name, texpage.tex_struct.name);
|
|
local_tracklocks[num_tracklocks].pagetype = PAGETYPE_TEXTURE;
|
|
num_tracklocks++;
|
|
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
mng_ReadNewSoundPage(infile, &soundpage);
|
|
strcpy(local_tracklocks[num_tracklocks].name, soundpage.sound_struct.name);
|
|
local_tracklocks[num_tracklocks].pagetype = PAGETYPE_SOUND;
|
|
num_tracklocks++;
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
mng_ReadNewWeaponPage(infile, &weaponpage);
|
|
strcpy(local_tracklocks[num_tracklocks].name, weaponpage.weapon_struct.name);
|
|
local_tracklocks[num_tracklocks].pagetype = PAGETYPE_WEAPON;
|
|
num_tracklocks++;
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
mng_ReadNewGenericPage(infile, &genericpage);
|
|
strcpy(local_tracklocks[num_tracklocks].name, genericpage.objinfo_struct.name);
|
|
local_tracklocks[num_tracklocks].pagetype = PAGETYPE_GENERIC;
|
|
num_tracklocks++;
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
mng_ReadNewDoorPage(infile, &doorpage);
|
|
strcpy(local_tracklocks[num_tracklocks].name, doorpage.door_struct.name);
|
|
local_tracklocks[num_tracklocks].pagetype = PAGETYPE_DOOR;
|
|
num_tracklocks++;
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
mng_ReadNewShipPage(infile, &shippage);
|
|
strcpy(local_tracklocks[num_tracklocks].name, shippage.ship_struct.name);
|
|
local_tracklocks[num_tracklocks].pagetype = PAGETYPE_SHIP;
|
|
num_tracklocks++;
|
|
break;
|
|
case PAGETYPE_GAMEFILE:
|
|
mng_ReadNewGamefilePage(infile, &gamefilepage);
|
|
strcpy(local_tracklocks[num_tracklocks].name, gamefilepage.gamefile_struct.name);
|
|
local_tracklocks[num_tracklocks].pagetype = PAGETYPE_GAMEFILE;
|
|
num_tracklocks++;
|
|
break;
|
|
case PAGETYPE_MEGACELL:
|
|
Int3(); // huh?
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
cfclose(infile);
|
|
// Now go through and filter out all unused lock files
|
|
infile = (CFILE *)cfopen(TableLockFilename, "rb");
|
|
if (!infile) {
|
|
strcpy(ErrorString, "Couldn't open Table lock file!");
|
|
goto done;
|
|
}
|
|
outfile = (CFILE *)cfopen(TempTableLockFilename, "wb");
|
|
if (!outfile) {
|
|
cfclose(infile);
|
|
strcpy(ErrorString, "Couldn't open temporary table lock file!");
|
|
goto done;
|
|
}
|
|
done = 0;
|
|
mngs_Pagelock temp_pl;
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
if (mng_ReadPagelock(infile, &temp_pl)) {
|
|
int found = -1;
|
|
for (int i = 0; i < num_tracklocks && found == -1; i++) {
|
|
if (local_tracklocks[i].pagetype == temp_pl.pagetype && !stricmp(local_tracklocks[i].name, temp_pl.name)) {
|
|
found = i;
|
|
}
|
|
}
|
|
if (found != -1)
|
|
mng_WritePagelock(outfile, &temp_pl);
|
|
else {
|
|
LOG_WARNING.printf("Found unused lock file %s", temp_pl.name);
|
|
}
|
|
|
|
} else
|
|
done = 1;
|
|
}
|
|
cfclose(infile);
|
|
cfclose(outfile);
|
|
|
|
if (remove(TableLockFilename)) {
|
|
snprintf(ErrorString, sizeof(ErrorString), "There was a problem deleting the temp file - errno %d", errno);
|
|
goto done;
|
|
}
|
|
if (rename(TempTableLockFilename, TableLockFilename.u8string().c_str())) {
|
|
snprintf(ErrorString, sizeof(ErrorString), "There was a problem renaming the temp file - errno %d", errno);
|
|
|
|
goto done;
|
|
}
|
|
mng_EraseLocker();
|
|
LOG_INFO << "Done transferring pages...good luck!";
|
|
done:;
|
|
mem_free(local_tracklocks);
|
|
}
|
|
// #define DELETING_PAGELOCKS 1
|
|
// #define CLEANING_PAGELOCKS 1
|
|
// Given a list of names and a pagetype, unlocks the ones already inside the lock file
|
|
int mng_UnlockPagelockSeries(const char *names[], int *pagetypes, int num);
|
|
// Goes through the pagelock table and deletes all duplicate entries
|
|
int mng_DeleteDuplicatePagelocks();
|
|
void ReorderPagelocks() {
|
|
const char *names[] = {"Lava"};
|
|
int types[] = {PAGETYPE_SOUND};
|
|
if (!mng_MakeLocker())
|
|
return;
|
|
mng_UnlockPagelockSeries(names, types, 1);
|
|
|
|
// mng_DeleteDuplicatePagelocks ();
|
|
|
|
mng_EraseLocker();
|
|
}
|
|
// THIS IS A SPECIAL FUNCTION THAT YOU SHOULD ONLY USE IF YOU KNOW WHAT YOU ARE
|
|
// DOING...it simply reorders the table file so that the "primitives" are first.
|
|
// This helps in the load time of a table file.
|
|
void ReorderPages(int local) {
|
|
CFILE *infile, *outfile;
|
|
uint8_t pagetype;
|
|
int done = 0;
|
|
int len;
|
|
#ifdef CLEANING_PAGELOCKS
|
|
mng_TransferPages();
|
|
return;
|
|
#endif
|
|
#ifdef DELETING_PAGELOCKS
|
|
ReorderPagelocks();
|
|
return;
|
|
#endif
|
|
LOG_INFO << "Reordering pages, please wait...";
|
|
if (local)
|
|
infile = cfopen(LocalTableFilename, "rb");
|
|
else {
|
|
if (!mng_MakeLocker())
|
|
return;
|
|
infile = cfopen(TableFilename, "rb");
|
|
}
|
|
if (!infile) {
|
|
LOG_ERROR << "Couldn't open table file to reorder!";
|
|
Int3();
|
|
return;
|
|
}
|
|
if (local)
|
|
outfile = cfopen(LocalTempTableFilename, "wb");
|
|
else
|
|
outfile = cfopen(TempTableFilename, "wb");
|
|
if (!outfile) {
|
|
LOG_ERROR << "Couldn't open temp table file to reorder!";
|
|
cfclose(infile);
|
|
Int3();
|
|
return;
|
|
}
|
|
// Do textures
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a door page, just read it in and ignore it
|
|
if (pagetype != PAGETYPE_TEXTURE) {
|
|
cfseek(infile, len - 4, SEEK_CUR);
|
|
continue;
|
|
}
|
|
mng_ReadNewTexturePage(infile, &texpage);
|
|
mng_WriteNewTexturePage(outfile, &texpage);
|
|
}
|
|
// Do sounds
|
|
done = 0;
|
|
cfseek(infile, 0, SEEK_SET);
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a door page, just read it in and ignore it
|
|
if (pagetype != PAGETYPE_SOUND) {
|
|
cfseek(infile, len - 4, SEEK_CUR);
|
|
continue;
|
|
}
|
|
mng_ReadNewSoundPage(infile, &soundpage);
|
|
mng_WriteNewSoundPage(outfile, &soundpage);
|
|
}
|
|
// Do weapons
|
|
done = 0;
|
|
cfseek(infile, 0, SEEK_SET);
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a door page, just read it in and ignore it
|
|
if (pagetype != PAGETYPE_WEAPON) {
|
|
cfseek(infile, len - 4, SEEK_CUR);
|
|
continue;
|
|
}
|
|
mng_ReadNewWeaponPage(infile, &weaponpage);
|
|
// Ignore counter measure weapons
|
|
if (!(weaponpage.weapon_struct.flags & WF_SPAWNS_ROBOT))
|
|
mng_WriteNewWeaponPage(outfile, &weaponpage);
|
|
}
|
|
// Do powerup generics
|
|
done = 0;
|
|
cfseek(infile, 0, SEEK_SET);
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a door page, just read it in and ignore it
|
|
if (pagetype != PAGETYPE_GENERIC) {
|
|
cfseek(infile, len - 4, SEEK_CUR);
|
|
continue;
|
|
}
|
|
mng_ReadNewGenericPage(infile, &genericpage);
|
|
if (genericpage.objinfo_struct.type == OBJ_POWERUP)
|
|
mng_WriteNewGenericPage(outfile, &genericpage);
|
|
if (genericpage.objinfo_struct.description != NULL) {
|
|
mem_free(genericpage.objinfo_struct.description);
|
|
genericpage.objinfo_struct.description = NULL;
|
|
}
|
|
}
|
|
// Do standard generics
|
|
done = 0;
|
|
cfseek(infile, 0, SEEK_SET);
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a door page, just read it in and ignore it
|
|
if (pagetype != PAGETYPE_GENERIC) {
|
|
cfseek(infile, len - 4, SEEK_CUR);
|
|
continue;
|
|
}
|
|
mng_ReadNewGenericPage(infile, &genericpage);
|
|
if (genericpage.objinfo_struct.type != OBJ_POWERUP)
|
|
mng_WriteNewGenericPage(outfile, &genericpage);
|
|
if (genericpage.objinfo_struct.description != NULL) {
|
|
mem_free(genericpage.objinfo_struct.description);
|
|
genericpage.objinfo_struct.description = NULL;
|
|
}
|
|
}
|
|
// Do countermeasure weapons
|
|
done = 0;
|
|
cfseek(infile, 0, SEEK_SET);
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a door page, just read it in and ignore it
|
|
if (pagetype != PAGETYPE_WEAPON) {
|
|
cfseek(infile, len - 4, SEEK_CUR);
|
|
continue;
|
|
}
|
|
mng_ReadNewWeaponPage(infile, &weaponpage);
|
|
if ((weaponpage.weapon_struct.flags & WF_SPAWNS_ROBOT))
|
|
mng_WriteNewWeaponPage(outfile, &weaponpage);
|
|
}
|
|
|
|
// Do doors
|
|
done = 0;
|
|
cfseek(infile, 0, SEEK_SET);
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a door page, just read it in and ignore it
|
|
if (pagetype != PAGETYPE_DOOR) {
|
|
cfseek(infile, len - 4, SEEK_CUR);
|
|
continue;
|
|
}
|
|
mng_ReadNewDoorPage(infile, &doorpage);
|
|
mng_WriteNewDoorPage(outfile, &doorpage);
|
|
}
|
|
// Do player ships
|
|
done = 0;
|
|
cfseek(infile, 0, SEEK_SET);
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a door page, just read it in and ignore it
|
|
if (pagetype != PAGETYPE_SHIP) {
|
|
cfseek(infile, len - 4, SEEK_CUR);
|
|
continue;
|
|
}
|
|
mng_ReadNewShipPage(infile, &shippage);
|
|
mng_WriteNewShipPage(outfile, &shippage);
|
|
}
|
|
// Do gamefiles
|
|
done = 0;
|
|
cfseek(infile, 0, SEEK_SET);
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a door page, just read it in and ignore it
|
|
if (pagetype != PAGETYPE_GAMEFILE) {
|
|
cfseek(infile, len - 4, SEEK_CUR);
|
|
continue;
|
|
}
|
|
mng_ReadNewGamefilePage(infile, &gamefilepage);
|
|
mng_WriteNewGamefilePage(outfile, &gamefilepage);
|
|
}
|
|
// Do megacells
|
|
done = 0;
|
|
cfseek(infile, 0, SEEK_SET);
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a door page, just read it in and ignore it
|
|
if (pagetype != PAGETYPE_MEGACELL) {
|
|
cfseek(infile, len - 4, SEEK_CUR);
|
|
continue;
|
|
}
|
|
mng_ReadNewMegacellPage(infile, &megacellpage);
|
|
mng_WriteNewMegacellPage(outfile, &megacellpage);
|
|
}
|
|
|
|
cfclose(infile);
|
|
cfclose(outfile);
|
|
if (local) {
|
|
SwitcherooFiles(LocalTableFilename, LocalTempTableFilename);
|
|
} else {
|
|
SwitcherooFiles(TableFilename, TempTableFilename);
|
|
}
|
|
mng_EraseLocker();
|
|
}
|
|
// Returns true if the passed in pagelock is in the LockList, else false
|
|
bool InLockList(mngs_Pagelock *pl) {
|
|
if (Starting_editor) {
|
|
for (int i = 0; i < Num_locklist; i++) {
|
|
if (LockList[i].pagetype == pl->pagetype) {
|
|
if (!stricmp(LockList[i].name, pl->name))
|
|
return true;
|
|
}
|
|
}
|
|
} else {
|
|
if ((mng_CheckIfPageOwned(pl, TableUser)) > 0) // DAJ -1FIX
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
// Given a filename, returns the type of primitive it is
|
|
int GetPrimType(const std::filesystem::path &name) {
|
|
int primtype;
|
|
std::filesystem::path ext = name.extension();
|
|
if (!stricmp(".oof", ext.u8string().c_str()))
|
|
primtype = PRIMTYPE_OOF;
|
|
else if (!stricmp(".ogf", ext.u8string().c_str()))
|
|
primtype = PRIMTYPE_OGF;
|
|
else if (!stricmp(".oaf", ext.u8string().c_str()))
|
|
primtype = PRIMTYPE_OAF;
|
|
else if (!stricmp(".wav", ext.u8string().c_str()))
|
|
primtype = PRIMTYPE_WAV;
|
|
else
|
|
primtype = PRIMTYPE_FILE;
|
|
return primtype;
|
|
}
|
|
|
|
#if defined(WIN32)
|
|
// Builds a list of old files in a path
|
|
void BuildOldFilesForDirectory(const std::filesystem::path& path, FILETIME threshold) {
|
|
HANDLE filehandle;
|
|
WIN32_FIND_DATA filedata;
|
|
std::filesystem::path newpath = path / "*.*";
|
|
filehandle = FindFirstFile(newpath.u8string().c_str(), &filedata);
|
|
bool go_ahead = true;
|
|
if (filehandle == INVALID_HANDLE_VALUE)
|
|
go_ahead = false;
|
|
while (go_ahead) {
|
|
bool add_it = false;
|
|
|
|
// if this file is newer than the last time we updated, add it to the list
|
|
|
|
if (filedata.ftLastWriteTime.dwHighDateTime > threshold.dwHighDateTime)
|
|
add_it = true;
|
|
if (filedata.ftLastWriteTime.dwHighDateTime == threshold.dwHighDateTime) {
|
|
if (filedata.ftLastWriteTime.dwLowDateTime > threshold.dwLowDateTime)
|
|
add_it = true;
|
|
}
|
|
if (filedata.ftCreationTime.dwHighDateTime > threshold.dwHighDateTime)
|
|
add_it = true;
|
|
if (filedata.ftCreationTime.dwHighDateTime == threshold.dwHighDateTime) {
|
|
if (filedata.ftCreationTime.dwLowDateTime > threshold.dwLowDateTime)
|
|
add_it = true;
|
|
}
|
|
// Add it to the list!
|
|
if (add_it) {
|
|
int primtype = GetPrimType(filedata.cFileName);
|
|
OldFiles[Num_old_files].type = primtype;
|
|
strcpy(OldFiles[Num_old_files].name, filedata.cFileName);
|
|
Num_old_files++;
|
|
}
|
|
go_ahead = (FindNextFile(filehandle, &filedata) != 0);
|
|
}
|
|
if (filehandle != INVALID_HANDLE_VALUE)
|
|
FindClose(filehandle);
|
|
}
|
|
|
|
// Builds a list of old files so we know which ones to update
|
|
// Searches through all our netdirectories for old files
|
|
void BuildOldFileList(FILETIME threshold) {
|
|
char str[_MAX_PATH];
|
|
LOG_INFO << "Building old files list!";
|
|
BuildOldFilesForDirectory(NetModelsDir, threshold);
|
|
BuildOldFilesForDirectory(NetSoundsDir, threshold);
|
|
BuildOldFilesForDirectory(NetMiscDir, threshold);
|
|
BuildOldFilesForDirectory(ManageGraphicsDir, threshold);
|
|
BuildOldFilesForDirectory(NetArtDir, threshold);
|
|
BuildOldFilesForDirectory(NetMusicDir, threshold);
|
|
BuildOldFilesForDirectory(NetVoiceDir, threshold);
|
|
ddio_MakePath(str, NetD3Dir, "data", "levels", NULL);
|
|
BuildOldFilesForDirectory(str, threshold);
|
|
ddio_MakePath(str, NetD3Dir, "data", "briefings", NULL);
|
|
BuildOldFilesForDirectory(str, threshold);
|
|
ddio_MakePath(str, NetD3Dir, "data", "scripts", NULL);
|
|
BuildOldFilesForDirectory(str, threshold);
|
|
LOG_INFO.printf("Found %d old files.", Num_old_files);
|
|
}
|
|
#endif
|
|
|
|
// Returns true if the passed in primitive is old (ie needs to be updated from the network)
|
|
bool IsPrimitiveOld(char *name) {
|
|
int primtype = GetPrimType(name);
|
|
for (int i = 0; i < Num_old_files; i++) {
|
|
if (OldFiles[i].type == primtype && !stricmp(OldFiles[i].name, name))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Updates a primitive if needed
|
|
// Localname = local version of the primname (with path)
|
|
// Netname = Network version of the primname (with path)
|
|
void UpdatePrimitive(const std::filesystem::path &localname, const std::filesystem::path &netname, char *primname,
|
|
int pagetype, char *pagename) {
|
|
bool update = false;
|
|
if (Starting_editor && !Use_old_update_method) {
|
|
if (IsPrimitiveOld(primname))
|
|
update = true;
|
|
} else {
|
|
if (!cfexist(localname) || (cfexist(netname) && cf_Diff(localname, netname)))
|
|
update = true;
|
|
}
|
|
|
|
if (update) {
|
|
mngs_Pagelock temp_pl;
|
|
temp_pl.pagetype = pagetype;
|
|
strcpy(temp_pl.name, pagename);
|
|
if (!InLockList(&temp_pl)) {
|
|
LOG_DEBUG.printf("Making a local copy of %s for next time.", primname);
|
|
if (!cf_CopyFile(localname, netname, 1)) {
|
|
Int3(); // get Jason
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Writes a chunk header. Writes chunk id & placeholder length. Returns chunk start pos
|
|
int StartManagePage(CFILE *ofile, uint8_t pagetype) {
|
|
int chunk_start_pos;
|
|
// Write pagetype
|
|
cf_WriteByte(ofile, pagetype);
|
|
chunk_start_pos = cftell(ofile);
|
|
cf_WriteInt(ofile, 0); // placeholder for chunk len
|
|
|
|
return chunk_start_pos;
|
|
}
|
|
// Fill in page length when done writing
|
|
void EndManagePage(CFILE *ofile, int chunk_start_pos) {
|
|
int save_pos = cftell(ofile);
|
|
int len = save_pos - chunk_start_pos;
|
|
// seek back to len field and fill in value
|
|
cfseek(ofile, chunk_start_pos, SEEK_SET);
|
|
cf_WriteInt(ofile, len); // write chunk length
|
|
// go back to end of file
|
|
cfseek(ofile, save_pos, SEEK_SET);
|
|
}
|
|
// Assigns a page to its appropriate structure and writes it out
|
|
void mng_AssignAndWritePage(int handle, int pagetype, CFILE *outfile) {
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE:
|
|
mng_AssignTextureToTexPage(handle, &texpage);
|
|
mng_WriteNewTexturePage(outfile, &texpage);
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
mng_AssignSoundToSoundPage(handle, &soundpage);
|
|
mng_WriteNewSoundPage(outfile, &soundpage);
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
mng_AssignWeaponToWeaponPage(handle, &weaponpage);
|
|
mng_WriteNewWeaponPage(outfile, &weaponpage);
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
mng_AssignObjInfoToGenericPage(handle, &genericpage);
|
|
mng_WriteNewGenericPage(outfile, &genericpage);
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
mng_AssignDoorToDoorPage(handle, &doorpage);
|
|
mng_WriteNewDoorPage(outfile, &doorpage);
|
|
break;
|
|
case PAGETYPE_MEGACELL:
|
|
mng_AssignMegacellToMegacellPage(handle, &megacellpage);
|
|
mng_WriteNewMegacellPage(outfile, &megacellpage);
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
mng_AssignShipToShipPage(handle, &shippage);
|
|
mng_WriteNewShipPage(outfile, &shippage);
|
|
break;
|
|
case PAGETYPE_GAMEFILE:
|
|
mng_AssignGamefileToGamefilePage(handle, &gamefilepage);
|
|
mng_WriteNewGamefilePage(outfile, &gamefilepage);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#define COPYBUFFER_SIZE 200000
|
|
// Given a texture handle, searches the table file and replaces the texture with the same name
|
|
// If local=1, then does it to the users local copy
|
|
// Returns 0 on error, else 1 if all is good
|
|
int mng_ReplacePage(char *srcname, char *destname, int handle, int dest_pagetype, int local) {
|
|
CFILE *infile, *outfile;
|
|
uint8_t pagetype, replaced = 0;
|
|
int done = 0, len;
|
|
LOG_DEBUG.printf("Replacing '%s' with '%s' (%s).", srcname, destname, local ? "locally" : "to network");
|
|
|
|
if (local)
|
|
infile = cfopen(LocalTableFilename, "rb");
|
|
else
|
|
infile = cfopen(TableFilename, "rb");
|
|
if (!infile) {
|
|
LOG_ERROR.printf("Couldn't open table file to replace page %s!", srcname);
|
|
Int3();
|
|
return 0;
|
|
}
|
|
if (local)
|
|
outfile = cfopen(LocalTempTableFilename, "wb");
|
|
else
|
|
outfile = cfopen(TempTableFilename, "wb");
|
|
if (!outfile) {
|
|
LOG_ERROR.printf("Couldn't open temp table file to replace page %s!", srcname);
|
|
cfclose(infile);
|
|
Int3();
|
|
return 0;
|
|
}
|
|
// Allocate memory for copying
|
|
uint8_t *copybuffer = mem_rmalloc<uint8_t>(COPYBUFFER_SIZE);
|
|
if (!copybuffer) {
|
|
LOG_ERROR.printf("Couldn't allocate memory to replace page %s!", srcname);
|
|
cfclose(infile);
|
|
cfclose(outfile);
|
|
Int3();
|
|
return 0;
|
|
}
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
// If not a texture page, just read it in and write it right back out
|
|
if (pagetype != dest_pagetype) {
|
|
ASSERT(len < COPYBUFFER_SIZE);
|
|
|
|
cf_ReadBytes(copybuffer, len - 4, infile);
|
|
cf_WriteByte(outfile, pagetype);
|
|
cf_WriteInt(outfile, len);
|
|
if (len - 4 > 0)
|
|
cf_WriteBytes(copybuffer, len - 4, outfile);
|
|
|
|
continue;
|
|
}
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE: {
|
|
mng_ReadNewTexturePage(infile, &texpage);
|
|
if (!stricmp(srcname, texpage.tex_struct.name)) {
|
|
mng_AssignAndWritePage(handle, pagetype, outfile);
|
|
replaced = 1;
|
|
} else {
|
|
mng_WriteNewTexturePage(outfile, &texpage);
|
|
}
|
|
break;
|
|
}
|
|
case PAGETYPE_SOUND: {
|
|
mng_ReadNewSoundPage(infile, &soundpage);
|
|
if (!stricmp(srcname, soundpage.sound_struct.name)) {
|
|
mng_AssignAndWritePage(handle, pagetype, outfile);
|
|
replaced = 1;
|
|
} else {
|
|
mng_WriteNewSoundPage(outfile, &soundpage);
|
|
}
|
|
break;
|
|
}
|
|
case PAGETYPE_WEAPON: {
|
|
mng_ReadNewWeaponPage(infile, &weaponpage);
|
|
if (!stricmp(srcname, weaponpage.weapon_struct.name)) {
|
|
mng_AssignAndWritePage(handle, pagetype, outfile);
|
|
replaced = 1;
|
|
} else {
|
|
mng_WriteNewWeaponPage(outfile, &weaponpage);
|
|
}
|
|
break;
|
|
}
|
|
case PAGETYPE_GENERIC: {
|
|
mng_ReadNewGenericPage(infile, &genericpage);
|
|
if (!stricmp(srcname, genericpage.objinfo_struct.name)) {
|
|
// This is the page we want to replace, so write the new one out.
|
|
if (genericpage.objinfo_struct.description != NULL) {
|
|
mem_free(genericpage.objinfo_struct.description);
|
|
genericpage.objinfo_struct.description = NULL;
|
|
}
|
|
genericpage.objinfo_struct.icon_name[0] = '\0';
|
|
|
|
mng_AssignAndWritePage(handle, pagetype, outfile);
|
|
replaced = 1;
|
|
} else {
|
|
mng_WriteNewGenericPage(outfile, &genericpage);
|
|
}
|
|
break;
|
|
}
|
|
case PAGETYPE_DOOR: {
|
|
mng_ReadNewDoorPage(infile, &doorpage);
|
|
if (!stricmp(srcname, doorpage.door_struct.name)) {
|
|
mng_AssignAndWritePage(handle, pagetype, outfile);
|
|
replaced = 1;
|
|
} else {
|
|
mng_WriteNewDoorPage(outfile, &doorpage);
|
|
}
|
|
break;
|
|
}
|
|
case PAGETYPE_MEGACELL: {
|
|
mng_ReadNewMegacellPage(infile, &megacellpage);
|
|
if (!stricmp(srcname, megacellpage.megacell_struct.name)) {
|
|
mng_AssignAndWritePage(handle, pagetype, outfile);
|
|
replaced = 1;
|
|
} else {
|
|
mng_WriteNewMegacellPage(outfile, &megacellpage);
|
|
}
|
|
break;
|
|
}
|
|
case PAGETYPE_SHIP: {
|
|
mng_ReadNewShipPage(infile, &shippage);
|
|
if (!stricmp(srcname, shippage.ship_struct.name)) {
|
|
mng_AssignAndWritePage(handle, pagetype, outfile);
|
|
replaced = 1;
|
|
} else {
|
|
mng_WriteNewShipPage(outfile, &shippage);
|
|
}
|
|
break;
|
|
}
|
|
case PAGETYPE_GAMEFILE: {
|
|
mng_ReadNewGamefilePage(infile, &gamefilepage);
|
|
if (!stricmp(srcname, gamefilepage.gamefile_struct.name)) {
|
|
mng_AssignAndWritePage(handle, pagetype, outfile);
|
|
replaced = 1;
|
|
} else {
|
|
mng_WriteNewGamefilePage(outfile, &gamefilepage);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!replaced) {
|
|
// This is a new page, so append it to the end of file.
|
|
mng_AssignAndWritePage(handle, dest_pagetype, outfile);
|
|
}
|
|
if (replaced)
|
|
LOG_DEBUG << "Page replaced.";
|
|
else
|
|
LOG_DEBUG << "New page added.";
|
|
cfclose(infile);
|
|
cfclose(outfile);
|
|
mem_free(copybuffer);
|
|
if (local) {
|
|
if (!SwitcherooFiles(LocalTableFilename, LocalTempTableFilename))
|
|
return 0;
|
|
} else {
|
|
if (!SwitcherooFiles(TableFilename, TempTableFilename))
|
|
return 0;
|
|
}
|
|
return 1; // successful!
|
|
}
|
|
// Given a texture name, finds it in the table file and deletes it
|
|
// If local is 1, deletes from the local table file
|
|
int mng_DeletePage(char *name, int dest_pagetype, int local) {
|
|
CFILE *infile, *outfile;
|
|
uint8_t pagetype, replaced = 0;
|
|
int done = 0;
|
|
int deleted = 0;
|
|
|
|
LOG_DEBUG.printf("Deleting %s (%s).", name, local ? "locally" : "on network");
|
|
|
|
if (local)
|
|
infile = cfopen(LocalTableFilename, "rb");
|
|
else
|
|
infile = cfopen(TableFilename, "rb");
|
|
if (!infile) {
|
|
LOG_ERROR << "Couldn't open table file to delete page!";
|
|
Int3();
|
|
return 0;
|
|
}
|
|
if (local)
|
|
outfile = cfopen(LocalTempTableFilename, "wb");
|
|
else
|
|
outfile = cfopen(TempTableFilename, "wb");
|
|
if (!outfile) {
|
|
LOG_ERROR << "Couldn't open temp table file to delete page!";
|
|
cfclose(infile);
|
|
Int3();
|
|
return 0;
|
|
}
|
|
// Allocate memory for copying
|
|
uint8_t *copybuffer = mem_rmalloc<uint8_t>(COPYBUFFER_SIZE);
|
|
if (!copybuffer) {
|
|
LOG_ERROR << "Couldn't allocate memory to delete page!";
|
|
cfclose(infile);
|
|
cfclose(outfile);
|
|
Int3();
|
|
return 0;
|
|
}
|
|
while (!done) {
|
|
if (cfeof(infile)) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
pagetype = cf_ReadByte(infile);
|
|
int len = cf_ReadInt(infile);
|
|
if (pagetype != dest_pagetype) {
|
|
ASSERT(len < COPYBUFFER_SIZE);
|
|
|
|
cf_ReadBytes(copybuffer, len - 4, infile);
|
|
cf_WriteByte(outfile, pagetype);
|
|
cf_WriteInt(outfile, len);
|
|
if (len - 4 > 0)
|
|
cf_WriteBytes(copybuffer, len - 4, outfile);
|
|
|
|
continue;
|
|
}
|
|
switch (pagetype) {
|
|
|
|
case PAGETYPE_TEXTURE: {
|
|
mng_ReadNewTexturePage(infile, &texpage);
|
|
if (stricmp(name, texpage.tex_struct.name))
|
|
mng_WriteNewTexturePage(outfile, &texpage);
|
|
else
|
|
deleted = 1; // Don't write out the one we want to delete
|
|
break;
|
|
}
|
|
case PAGETYPE_DOOR: {
|
|
mng_ReadNewDoorPage(infile, &doorpage);
|
|
if (stricmp(name, doorpage.door_struct.name))
|
|
mng_WriteNewDoorPage(outfile, &doorpage);
|
|
else
|
|
deleted = 1; // Don't write out the one we want to delete
|
|
break;
|
|
}
|
|
case PAGETYPE_GENERIC: {
|
|
mng_ReadNewGenericPage(infile, &genericpage);
|
|
if (stricmp(name, genericpage.objinfo_struct.name))
|
|
mng_WriteNewGenericPage(outfile, &genericpage);
|
|
else
|
|
deleted = 1; // Don't write out the one we want to delete
|
|
if (genericpage.objinfo_struct.description != NULL) {
|
|
mem_free(genericpage.objinfo_struct.description);
|
|
genericpage.objinfo_struct.description = NULL;
|
|
}
|
|
genericpage.objinfo_struct.icon_name[0] = '\0';
|
|
break;
|
|
}
|
|
case PAGETYPE_SOUND: {
|
|
mng_ReadNewSoundPage(infile, &soundpage);
|
|
if (stricmp(name, soundpage.sound_struct.name))
|
|
mng_WriteNewSoundPage(outfile, &soundpage);
|
|
else
|
|
deleted = 1; // Don't write out the one we want to delete
|
|
break;
|
|
}
|
|
case PAGETYPE_SHIP: {
|
|
mng_ReadNewShipPage(infile, &shippage);
|
|
if (stricmp(name, shippage.ship_struct.name))
|
|
mng_WriteNewShipPage(outfile, &shippage);
|
|
else
|
|
deleted = 1; // Don't write out the one we want to delete
|
|
break;
|
|
}
|
|
case PAGETYPE_WEAPON: {
|
|
mng_ReadNewWeaponPage(infile, &weaponpage);
|
|
if (stricmp(name, weaponpage.weapon_struct.name))
|
|
mng_WriteNewWeaponPage(outfile, &weaponpage);
|
|
else
|
|
deleted = 1; // Don't write out the one we want to delete
|
|
break;
|
|
}
|
|
case PAGETYPE_MEGACELL: {
|
|
mng_ReadNewMegacellPage(infile, &megacellpage);
|
|
if (stricmp(name, megacellpage.megacell_struct.name))
|
|
mng_WriteNewMegacellPage(outfile, &megacellpage);
|
|
else
|
|
deleted = 1; // Don't write out the one we want to delete
|
|
break;
|
|
}
|
|
case PAGETYPE_GAMEFILE: {
|
|
mng_ReadNewGamefilePage(infile, &gamefilepage);
|
|
if (stricmp(name, gamefilepage.gamefile_struct.name))
|
|
mng_WriteNewGamefilePage(outfile, &gamefilepage);
|
|
else
|
|
deleted = 1; // Don't write out the one we want to delete
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!local) {
|
|
// It's gotta be there if on the network
|
|
ASSERT(deleted == 1);
|
|
} else {
|
|
if (!deleted) {
|
|
LOG_ERROR << "Not found locally?!";
|
|
}
|
|
}
|
|
cfclose(infile);
|
|
cfclose(outfile);
|
|
mem_free(copybuffer);
|
|
// Now, remove our table file and rename the temp file to be the table file
|
|
if (local) {
|
|
if (!SwitcherooFiles(LocalTableFilename, LocalTempTableFilename))
|
|
return 0;
|
|
} else {
|
|
if (!SwitcherooFiles(TableFilename, TempTableFilename))
|
|
return 0;
|
|
}
|
|
return 1; // successful!
|
|
}
|
|
// Reads in a physics chunk from an open file
|
|
void mng_ReadPhysicsChunk(physics_info *phys_info, CFILE *infile) {
|
|
phys_info->mass = cf_ReadFloat(infile);
|
|
phys_info->drag = cf_ReadFloat(infile);
|
|
phys_info->full_thrust = cf_ReadFloat(infile);
|
|
phys_info->flags = cf_ReadInt(infile);
|
|
phys_info->rotdrag = cf_ReadFloat(infile);
|
|
phys_info->full_rotthrust = cf_ReadFloat(infile);
|
|
phys_info->num_bounces = cf_ReadInt(infile);
|
|
phys_info->velocity.z = cf_ReadFloat(infile);
|
|
phys_info->rotvel.x = cf_ReadFloat(infile);
|
|
phys_info->rotvel.y = cf_ReadFloat(infile);
|
|
phys_info->rotvel.z = cf_ReadFloat(infile);
|
|
phys_info->wiggle_amplitude = cf_ReadFloat(infile);
|
|
phys_info->wiggles_per_sec = cf_ReadFloat(infile);
|
|
phys_info->coeff_restitution = cf_ReadFloat(infile);
|
|
phys_info->hit_die_dot = cf_ReadFloat(infile);
|
|
phys_info->max_turnroll_rate = cf_ReadFloat(infile);
|
|
phys_info->turnroll_ratio = cf_ReadFloat(infile);
|
|
}
|
|
// Writes out a physics chunk to an open file
|
|
void mng_WritePhysicsChunk(physics_info *phys_info, CFILE *outfile) {
|
|
cf_WriteFloat(outfile, phys_info->mass);
|
|
cf_WriteFloat(outfile, phys_info->drag);
|
|
cf_WriteFloat(outfile, phys_info->full_thrust);
|
|
cf_WriteInt(outfile, phys_info->flags);
|
|
cf_WriteFloat(outfile, phys_info->rotdrag);
|
|
cf_WriteFloat(outfile, phys_info->full_rotthrust);
|
|
cf_WriteInt(outfile, phys_info->num_bounces);
|
|
cf_WriteFloat(outfile, phys_info->velocity.z);
|
|
cf_WriteFloat(outfile, phys_info->rotvel.x);
|
|
cf_WriteFloat(outfile, phys_info->rotvel.y);
|
|
cf_WriteFloat(outfile, phys_info->rotvel.z);
|
|
cf_WriteFloat(outfile, phys_info->wiggle_amplitude);
|
|
cf_WriteFloat(outfile, phys_info->wiggles_per_sec);
|
|
cf_WriteFloat(outfile, phys_info->coeff_restitution);
|
|
cf_WriteFloat(outfile, phys_info->hit_die_dot);
|
|
cf_WriteFloat(outfile, phys_info->max_turnroll_rate);
|
|
cf_WriteFloat(outfile, phys_info->turnroll_ratio);
|
|
}
|
|
// Writes out weapon battery info
|
|
void mng_WriteWeaponBatteryChunk(otype_wb_info *static_wb, CFILE *outfile) {
|
|
int j;
|
|
cf_WriteFloat(outfile, static_wb->energy_usage);
|
|
cf_WriteFloat(outfile, static_wb->ammo_usage);
|
|
for (j = 0; j < MAX_WB_GUNPOINTS; j++) {
|
|
cf_WriteShort(outfile, static_wb->gp_weapon_index[j]);
|
|
}
|
|
|
|
for (j = 0; j < MAX_WB_FIRING_MASKS; j++) {
|
|
cf_WriteByte(outfile, static_wb->gp_fire_masks[j]);
|
|
cf_WriteFloat(outfile, static_wb->gp_fire_wait[j]);
|
|
cf_WriteFloat(outfile, static_wb->anim_time[j]);
|
|
cf_WriteFloat(outfile, static_wb->anim_start_frame[j]);
|
|
cf_WriteFloat(outfile, static_wb->anim_fire_frame[j]);
|
|
cf_WriteFloat(outfile, static_wb->anim_end_frame[j]);
|
|
}
|
|
cf_WriteByte(outfile, static_wb->num_masks);
|
|
cf_WriteShort(outfile, static_wb->aiming_gp_index);
|
|
cf_WriteByte(outfile, static_wb->aiming_flags);
|
|
cf_WriteFloat(outfile, static_wb->aiming_3d_dot);
|
|
cf_WriteFloat(outfile, static_wb->aiming_3d_dist);
|
|
cf_WriteFloat(outfile, static_wb->aiming_XZ_dot);
|
|
cf_WriteShort(outfile, static_wb->flags);
|
|
cf_WriteByte(outfile, static_wb->gp_quad_fire_mask);
|
|
}
|
|
// Reads in weapon battery info
|
|
void mng_ReadWeaponBatteryChunk(otype_wb_info *static_wb, CFILE *infile, int version) {
|
|
int j;
|
|
|
|
static_wb->energy_usage = cf_ReadFloat(infile);
|
|
static_wb->ammo_usage = cf_ReadFloat(infile);
|
|
|
|
for (j = 0; j < MAX_WB_GUNPOINTS; j++) {
|
|
static_wb->gp_weapon_index[j] = cf_ReadShort(infile);
|
|
}
|
|
|
|
for (j = 0; j < MAX_WB_FIRING_MASKS; j++) {
|
|
static_wb->gp_fire_masks[j] = cf_ReadByte(infile);
|
|
static_wb->gp_fire_wait[j] = cf_ReadFloat(infile);
|
|
static_wb->anim_time[j] = cf_ReadFloat(infile);
|
|
static_wb->anim_start_frame[j] = cf_ReadFloat(infile);
|
|
static_wb->anim_fire_frame[j] = cf_ReadFloat(infile);
|
|
static_wb->anim_end_frame[j] = cf_ReadFloat(infile);
|
|
}
|
|
static_wb->num_masks = cf_ReadByte(infile);
|
|
static_wb->aiming_gp_index = cf_ReadShort(infile);
|
|
static_wb->aiming_flags = cf_ReadByte(infile);
|
|
static_wb->aiming_3d_dot = cf_ReadFloat(infile);
|
|
static_wb->aiming_3d_dist = cf_ReadFloat(infile);
|
|
static_wb->aiming_XZ_dot = cf_ReadFloat(infile);
|
|
if (version >= 2)
|
|
static_wb->flags = cf_ReadShort(infile);
|
|
else
|
|
static_wb->flags = cf_ReadByte(infile);
|
|
static_wb->gp_quad_fire_mask = cf_ReadByte(infile);
|
|
}
|
|
// Writes a lighting chunk in from an open file
|
|
void mng_WriteLightingChunk(light_info *lighting_info, CFILE *outfile) {
|
|
cf_WriteFloat(outfile, lighting_info->light_distance);
|
|
cf_WriteFloat(outfile, lighting_info->red_light1);
|
|
cf_WriteFloat(outfile, lighting_info->green_light1);
|
|
cf_WriteFloat(outfile, lighting_info->blue_light1);
|
|
cf_WriteFloat(outfile, lighting_info->time_interval);
|
|
cf_WriteFloat(outfile, lighting_info->flicker_distance);
|
|
cf_WriteFloat(outfile, lighting_info->directional_dot);
|
|
cf_WriteFloat(outfile, lighting_info->red_light2);
|
|
cf_WriteFloat(outfile, lighting_info->green_light2);
|
|
cf_WriteFloat(outfile, lighting_info->blue_light2);
|
|
cf_WriteInt(outfile, lighting_info->flags);
|
|
cf_WriteInt(outfile, lighting_info->timebits);
|
|
cf_WriteByte(outfile, lighting_info->angle);
|
|
cf_WriteByte(outfile, lighting_info->lighting_render_type);
|
|
}
|
|
// Reads a lighting chunk in from an open file
|
|
void mng_ReadLightingChunk(light_info *lighting_info, CFILE *infile) {
|
|
lighting_info->light_distance = cf_ReadFloat(infile);
|
|
lighting_info->red_light1 = cf_ReadFloat(infile);
|
|
lighting_info->green_light1 = cf_ReadFloat(infile);
|
|
lighting_info->blue_light1 = cf_ReadFloat(infile);
|
|
lighting_info->time_interval = cf_ReadFloat(infile);
|
|
lighting_info->flicker_distance = cf_ReadFloat(infile);
|
|
lighting_info->directional_dot = cf_ReadFloat(infile);
|
|
lighting_info->red_light2 = cf_ReadFloat(infile);
|
|
lighting_info->green_light2 = cf_ReadFloat(infile);
|
|
lighting_info->blue_light2 = cf_ReadFloat(infile);
|
|
lighting_info->flags = cf_ReadInt(infile);
|
|
lighting_info->timebits = cf_ReadInt(infile);
|
|
lighting_info->angle = cf_ReadByte(infile);
|
|
lighting_info->lighting_render_type = cf_ReadByte(infile);
|
|
}
|
|
|
|
// Record keeping
|
|
AddOnTablefile AddOnDataTables[MAX_ADDON_TABLES];
|
|
|
|
// the number of addon tables currently in memory
|
|
int Num_addon_tables = 0;
|
|
|
|
// if not -1, then it is the addon table we are working with
|
|
int Loading_addon_table = -1;
|
|
|
|
//----------------------
|
|
// Add-on data routines
|
|
//----------------------
|
|
// Frees all the primitives associated with an page
|
|
void mng_FreePagetypePrimitives(int pagetype, char *name, int freetype) {
|
|
int n;
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE:
|
|
n = FindTextureName(name);
|
|
ASSERT(n >= 0);
|
|
if (GameTextures[n].flags & TF_ANIMATED)
|
|
FreeVClip(GameTextures[n].bm_handle);
|
|
else
|
|
bm_FreeBitmap(GameTextures[n].bm_handle);
|
|
if (GameTextures[n].flags & TF_DESTROYABLE && GameTextures[n].destroy_handle >= 0 &&
|
|
GameTextures[n].destroy_handle != n) {
|
|
int oldn = n;
|
|
n = GameTextures[n].destroy_handle;
|
|
if (GameTextures[n].flags & TF_ANIMATED)
|
|
FreeVClip(GameTextures[n].bm_handle);
|
|
else
|
|
bm_FreeBitmap(GameTextures[n].bm_handle);
|
|
n = oldn;
|
|
}
|
|
if (freetype)
|
|
FreeTexture(n);
|
|
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
n = FindSoundName(name);
|
|
ASSERT(n >= 0);
|
|
FreeSoundFile(Sounds[n].sample_index);
|
|
if (freetype)
|
|
FreeSound(n);
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
n = FindWeaponName(name);
|
|
ASSERT(n >= 0);
|
|
// Free weapon images
|
|
if (Weapons[n].flags & WF_HUD_ANIMATED)
|
|
FreeVClip(Weapons[n].hud_image_handle);
|
|
else
|
|
bm_FreeBitmap(Weapons[n].hud_image_handle);
|
|
if (Weapons[n].fire_image_handle != -1) {
|
|
if (Weapons[n].flags & WF_IMAGE_BITMAP)
|
|
bm_FreeBitmap(Weapons[n].fire_image_handle);
|
|
else if (Weapons[n].flags & WF_IMAGE_VCLIP)
|
|
FreeVClip(Weapons[n].fire_image_handle);
|
|
else
|
|
FreePolyModel(Weapons[n].fire_image_handle);
|
|
}
|
|
if (freetype)
|
|
FreeWeapon(n);
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
n = FindShipName(name);
|
|
ASSERT(n >= 0);
|
|
FreePolyModel(Ships[n].model_handle);
|
|
if (Ships[n].dying_model_handle != -1)
|
|
FreePolyModel(Ships[n].dying_model_handle);
|
|
if (Ships[n].med_render_handle != -1)
|
|
FreePolyModel(Ships[n].med_render_handle);
|
|
if (Ships[n].lo_render_handle != -1)
|
|
FreePolyModel(Ships[n].lo_render_handle);
|
|
if (freetype)
|
|
FreeShip(n);
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
n = FindObjectIDName(name);
|
|
ASSERT(n >= 0);
|
|
|
|
FreePolyModel(Object_info[n].render_handle);
|
|
if (Object_info[n].med_render_handle != -1)
|
|
FreePolyModel(Object_info[n].med_render_handle);
|
|
if (Object_info[n].lo_render_handle != -1)
|
|
FreePolyModel(Object_info[n].lo_render_handle);
|
|
if (freetype)
|
|
FreeObjectID(n);
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
n = FindDoorName(name);
|
|
ASSERT(n >= 0);
|
|
FreePolyModel(Doors[n].model_handle);
|
|
if (freetype)
|
|
FreeDoor(n);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
// Takes our addon pages and frees/restores our data to the appropriate pages
|
|
void mng_PopAddonPages() {
|
|
int i, n, ok;
|
|
|
|
ASSERT(Num_addon_tables > 0);
|
|
if (Num_addon_tables <= 0)
|
|
return; // no addon pages to pop off
|
|
|
|
Num_addon_tables--;
|
|
Loading_locals = 0;
|
|
AddOnTablefile *addondata = &AddOnDataTables[Num_addon_tables];
|
|
|
|
for (i = 0; i < addondata->Num_addon_tracklocks; i++) {
|
|
LOG_DEBUG.printf("Freeing addon page %s [%s].", addondata->Addon_tracklocks[i].name, addondata->AddOnTableFilename);
|
|
|
|
// set the Loading_addon_table to the appropriate value...
|
|
// it depends on if we are overlaying from a previous tablefile
|
|
// or this isn't an overlay at all
|
|
// overlay = 0 (not an overlay of anything)
|
|
// overlay = 1 (overlay of main tablefile)
|
|
// overlay > 1 (overlay of addon table [overlay-2])
|
|
if (addondata->Addon_tracklocks[i].overlay > 1) {
|
|
// this is an overlay of another table file
|
|
Loading_addon_table = addondata->Addon_tracklocks[i].overlay - 2;
|
|
} else {
|
|
// this is an overlay of the main table file
|
|
// or not an overlay at all
|
|
Loading_addon_table = -1;
|
|
}
|
|
|
|
if (addondata->Addon_tracklocks[i].overlay > 0) {
|
|
// Free this data, then read the old stuff back in
|
|
mng_FreePagetypePrimitives(addondata->Addon_tracklocks[i].pagetype, addondata->Addon_tracklocks[i].name, 0);
|
|
char *name = addondata->Addon_tracklocks[i].name;
|
|
switch (addondata->Addon_tracklocks[i].pagetype) {
|
|
case PAGETYPE_TEXTURE: {
|
|
n = FindTextureName(name);
|
|
ASSERT(n >= 0);
|
|
ok = mng_FindSpecificTexPage(name, &texpage, addondata->Addon_tracklocks[i].stack_filepos);
|
|
if (ok) {
|
|
ok = mng_AssignTexPageToTexture(&texpage, n);
|
|
if (!ok)
|
|
Error("Error 1 restoring page %s from addon data!", name);
|
|
} else
|
|
Error("Error 2 restoring page %s from addon data!", name);
|
|
break;
|
|
}
|
|
case PAGETYPE_SOUND: {
|
|
n = FindSoundName(name);
|
|
ASSERT(n >= 0);
|
|
ok = mng_FindSpecificSoundPage(name, &soundpage, addondata->Addon_tracklocks[i].stack_filepos);
|
|
if (ok) {
|
|
ok = mng_AssignSoundPageToSound(&soundpage, n);
|
|
if (!ok)
|
|
Error("Error 1 restoring page %s from addon data!", name);
|
|
} else
|
|
Error("Error 2 restoring page %s from addon data!", name);
|
|
break;
|
|
}
|
|
case PAGETYPE_DOOR: {
|
|
n = FindDoorName(name);
|
|
ASSERT(n >= 0);
|
|
ok = mng_FindSpecificDoorPage(name, &doorpage, addondata->Addon_tracklocks[i].stack_filepos);
|
|
if (ok) {
|
|
ok = mng_AssignDoorPageToDoor(&doorpage, n);
|
|
if (!ok)
|
|
Error("Error 1 restoring page %s from addon data!", name);
|
|
} else
|
|
Error("Error 2 restoring page %s from addon data!", name);
|
|
break;
|
|
}
|
|
case PAGETYPE_GENERIC: {
|
|
n = FindObjectIDName(name);
|
|
ASSERT(n >= 0);
|
|
ok = mng_FindSpecificGenericPage(name, &genericpage, addondata->Addon_tracklocks[i].stack_filepos);
|
|
if (ok) {
|
|
ok = mng_AssignGenericPageToObjInfo(&genericpage, n);
|
|
if (!ok)
|
|
Error("Error 1 restoring page %s from addon data!", name);
|
|
} else
|
|
Error("Error 2 restoring page %s from addon data!", name);
|
|
break;
|
|
}
|
|
case PAGETYPE_SHIP: {
|
|
n = FindShipName(name);
|
|
ASSERT(n >= 0);
|
|
ok = mng_FindSpecificShipPage(name, &shippage, addondata->Addon_tracklocks[i].stack_filepos);
|
|
if (ok) {
|
|
ok = mng_AssignShipPageToShip(&shippage, n);
|
|
if (!ok)
|
|
Error("Error 1 restoring page %s from addon data!", name);
|
|
} else
|
|
Error("Error 2 restoring page %s from addon data!", name);
|
|
break;
|
|
}
|
|
case PAGETYPE_WEAPON: {
|
|
n = FindWeaponName(name);
|
|
ASSERT(n >= 0);
|
|
ok = mng_FindSpecificWeaponPage(name, &weaponpage, addondata->Addon_tracklocks[i].stack_filepos);
|
|
if (ok) {
|
|
ok = mng_AssignWeaponPageToWeapon(&weaponpage, n);
|
|
if (!ok)
|
|
Error("Error 1 restoring page %s from addon data!", name);
|
|
} else
|
|
Error("Error 2 restoring page %s from addon data!", name);
|
|
break;
|
|
}
|
|
case PAGETYPE_GAMEFILE:
|
|
// I don't think there's anything we need to do here
|
|
break;
|
|
default:
|
|
Int3(); // bad type in list? Get Jason
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
// Not overlay, just free this data
|
|
mng_FreePagetypePrimitives(addondata->Addon_tracklocks[i].pagetype, addondata->Addon_tracklocks[i].name, 1);
|
|
}
|
|
}
|
|
|
|
Loading_addon_table = -1;
|
|
}
|
|
// Simply sets no addon data to be loaded
|
|
void mng_ClearAddonTables() {
|
|
int count = Num_addon_tables;
|
|
while (count > 0) {
|
|
ASSERT(count == Num_addon_tables);
|
|
|
|
if (AddOnDataTables[count - 1].Addon_tracklocks) {
|
|
mng_PopAddonPages();
|
|
mem_free(AddOnDataTables[count - 1].Addon_tracklocks);
|
|
AddOnDataTables[count - 1].Addon_tracklocks = NULL;
|
|
AddOnDataTables[count - 1].Num_addon_tracklocks = 0;
|
|
}
|
|
|
|
count--;
|
|
}
|
|
}
|
|
// Push the given table file as an addon table file
|
|
// returns true on success
|
|
bool mng_SetAddonTable(char *name) {
|
|
ASSERT(Num_addon_tables < MAX_ADDON_TABLES);
|
|
if (Num_addon_tables >= MAX_ADDON_TABLES)
|
|
return false;
|
|
|
|
// make sure the table file exists!
|
|
if (!cfexist(name))
|
|
return false;
|
|
|
|
strcpy(AddOnDataTables[Num_addon_tables].AddOnTableFilename, name);
|
|
AddOnDataTables[Num_addon_tables].Addon_tracklocks =
|
|
mem_rmalloc<mngs_track_lock>(MAX_ADDON_TRACKLOCKS);
|
|
AddOnDataTables[Num_addon_tables].Num_addon_tracklocks = 0;
|
|
ASSERT(AddOnDataTables[Num_addon_tables].Addon_tracklocks);
|
|
memset(AddOnDataTables[Num_addon_tables].Addon_tracklocks, 0, MAX_ADDON_TRACKLOCKS * sizeof(mngs_track_lock));
|
|
|
|
Num_addon_tables++;
|
|
return true;
|
|
}
|
|
|
|
// Pushes an addon pack onto the stack so we can keep track of it
|
|
void mng_PushAddonPage(int pagetype, char *name, int overlay) {
|
|
ASSERT(Loading_addon_table >= 0 && Loading_addon_table < MAX_ADDON_TABLES);
|
|
|
|
AddOnTablefile *addon = &AddOnDataTables[Loading_addon_table];
|
|
|
|
ASSERT(addon->Num_addon_tracklocks < MAX_ADDON_TRACKLOCKS);
|
|
|
|
// First check to see if this is a redundant page
|
|
for (int i = 0; i < addon->Num_addon_tracklocks; i++) {
|
|
if (addon->Addon_tracklocks[i].used && addon->Addon_tracklocks[i].pagetype == pagetype) {
|
|
if (!stricmp(addon->Addon_tracklocks[i].name, name)) {
|
|
Int3();
|
|
Error("Redundant addon page '%s' loaded...", name);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
LOG_DEBUG.printf("Adding addon page %s [%s] to list.", name, addon->AddOnTableFilename);
|
|
addon->Addon_tracklocks[addon->Num_addon_tracklocks].used = 1;
|
|
addon->Addon_tracklocks[addon->Num_addon_tracklocks].pagetype = pagetype;
|
|
addon->Addon_tracklocks[addon->Num_addon_tracklocks].overlay = overlay;
|
|
addon->Addon_tracklocks[addon->Num_addon_tracklocks].stack_filepos = 0;
|
|
strcpy(addon->Addon_tracklocks[addon->Num_addon_tracklocks].name, name);
|
|
addon->Num_addon_tracklocks++;
|
|
}
|
|
|
|
// Compiles the addon pages. By looking at all the addon pages (after they have been
|
|
// loaded) and does some compiling and saving of information to speed up addon page
|
|
// freeing
|
|
void mng_CompileAddonPages(void) {
|
|
if (Num_addon_tables <= 0)
|
|
return; // no addon pages to pop off
|
|
|
|
int curr_tablefile, i, tf, pagetype, len, next_pos, page_pos;
|
|
CFILE *file;
|
|
char pagename[256];
|
|
bool found_page;
|
|
|
|
for (curr_tablefile = 1; curr_tablefile <= Num_addon_tables; curr_tablefile++) {
|
|
// find all the pages that are loaded from this tablefile
|
|
// overlay = 0 (not from any tablefile)
|
|
// overlay = 1 (from main tablefile)
|
|
// overlay > 1 (from addontable[overlay-2]
|
|
if (curr_tablefile == 1) {
|
|
file = cfopen(TableFilename, "rb");
|
|
LOG_DEBUG << "Compiling addon pages of " << TableFilename;
|
|
} else {
|
|
file = cfopen(AddOnDataTables[curr_tablefile - 2].AddOnTableFilename, "rb");
|
|
LOG_DEBUG << "Compiling addon pages of " << AddOnDataTables[curr_tablefile - 2].AddOnTableFilename;
|
|
}
|
|
ASSERT(file != NULL);
|
|
|
|
// start reading the pages from this tablefile
|
|
// as we come across each page, check to see if it was
|
|
// ever overlayed.
|
|
while (!cfeof(file)) {
|
|
// Read in a pagetype. If it is a page we recognize, load it.
|
|
page_pos = cftell(file);
|
|
pagetype = cf_ReadByte(file);
|
|
len = cf_ReadInt(file);
|
|
next_pos = (page_pos + 1) + len;
|
|
|
|
// get the name of the page
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE:
|
|
cf_ReadShort(file); // version
|
|
cf_ReadString(pagename, PAGENAME_LEN, file);
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
cf_ReadShort(file); // version
|
|
cf_ReadString(pagename, PAGENAME_LEN, file);
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
cf_ReadShort(file); // version
|
|
cf_ReadByte(file); // type
|
|
cf_ReadString(pagename, PAGENAME_LEN, file);
|
|
break;
|
|
case PAGETYPE_GAMEFILE:
|
|
cf_ReadShort(file); // version
|
|
cf_ReadString(pagename, PAGENAME_LEN, file);
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
cf_ReadShort(file); // version
|
|
cf_ReadString(pagename, PAGENAME_LEN, file);
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
cf_ReadShort(file); // version
|
|
cf_ReadString(pagename, PAGENAME_LEN, file);
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
cf_ReadShort(file); // version
|
|
cf_ReadString(pagename, PAGENAME_LEN, file);
|
|
break;
|
|
case PAGETYPE_UNKNOWN:
|
|
continue;
|
|
break;
|
|
default:
|
|
Int3(); // Unrecognized pagetype, possible corrupt data following
|
|
break;
|
|
}
|
|
|
|
// now look for all the places where this page is overlayed
|
|
found_page = false;
|
|
for (tf = 0; tf < Num_addon_tables; tf++) {
|
|
for (i = 0; i < AddOnDataTables[tf].Num_addon_tracklocks; i++) {
|
|
if (!AddOnDataTables[tf].Addon_tracklocks[i].used)
|
|
continue;
|
|
if (AddOnDataTables[tf].Addon_tracklocks[i].overlay != curr_tablefile)
|
|
continue;
|
|
if (AddOnDataTables[tf].Addon_tracklocks[i].pagetype != pagetype)
|
|
continue;
|
|
if (stricmp(pagename, AddOnDataTables[tf].Addon_tracklocks[i].name))
|
|
continue;
|
|
|
|
// this is it!
|
|
LOG_INFO.printf("Compiling: %s[%s] to %d", AddOnDataTables[tf].Addon_tracklocks[i].name,
|
|
(curr_tablefile == 1) ? TableFilename : AddOnDataTables[curr_tablefile - 2].AddOnTableFilename,
|
|
page_pos);
|
|
ASSERT(AddOnDataTables[tf].Addon_tracklocks[i].stack_filepos == 0);
|
|
AddOnDataTables[tf].Addon_tracklocks[i].stack_filepos = page_pos;
|
|
found_page = true;
|
|
break;
|
|
}
|
|
|
|
if (found_page)
|
|
break;
|
|
}
|
|
|
|
// move on to the next page
|
|
cfseek(file, next_pos, SEEK_SET);
|
|
}
|
|
|
|
// done with this tablefile
|
|
cfclose(file);
|
|
}
|
|
}
|
|
|
|
// Error checking variables
|
|
bool Loading_addon = false;
|
|
int Data_error_count = 0;
|
|
FILE *Data_error_file = NULL;
|
|
char *Addon_filename;
|
|
|
|
// Loads and allocs all pages found locally
|
|
void mng_LoadAddonPages() {
|
|
CFILE *infile;
|
|
uint8_t pagetype;
|
|
int len;
|
|
|
|
// Set flag & Clear error count
|
|
Loading_addon = true;
|
|
Data_error_count = 0;
|
|
|
|
if (Num_addon_tables == 0)
|
|
return; // No addons to load
|
|
|
|
Loading_locals = 0;
|
|
|
|
int c;
|
|
AddOnTablefile *addon;
|
|
|
|
for (c = 0; c < Num_addon_tables; c++) {
|
|
addon = &AddOnDataTables[c];
|
|
|
|
LOG_INFO.printf("Loading addon pages for %s...", addon->AddOnTableFilename);
|
|
Addon_filename = addon->AddOnTableFilename;
|
|
infile = cfopen(addon->AddOnTableFilename, "rb");
|
|
if (!infile) {
|
|
LOG_ERROR.printf("Couldn't addon table file (%s) to read pages!\n", addon->AddOnTableFilename);
|
|
return;
|
|
}
|
|
Loading_addon_table = c;
|
|
while (!cfeof(infile)) {
|
|
// Read in a pagetype. If it is a page we recognize, load it.
|
|
pagetype = cf_ReadByte(infile);
|
|
len = cf_ReadInt(infile);
|
|
|
|
switch (pagetype) {
|
|
case PAGETYPE_TEXTURE:
|
|
mng_LoadLocalTexturePage(infile);
|
|
break;
|
|
case PAGETYPE_POWERUP:
|
|
case PAGETYPE_ROBOT:
|
|
Error("Your local table file is invalid. You must update from the network.");
|
|
break;
|
|
case PAGETYPE_DOOR:
|
|
mng_LoadLocalDoorPage(infile);
|
|
break;
|
|
case PAGETYPE_GENERIC:
|
|
mng_LoadLocalGenericPage(infile);
|
|
break;
|
|
case PAGETYPE_GAMEFILE:
|
|
mng_LoadLocalGamefilePage(infile);
|
|
break;
|
|
case PAGETYPE_SOUND:
|
|
mng_LoadLocalSoundPage(infile);
|
|
break;
|
|
case PAGETYPE_SHIP:
|
|
mng_LoadLocalShipPage(infile);
|
|
break;
|
|
case PAGETYPE_WEAPON:
|
|
mng_LoadLocalWeaponPage(infile);
|
|
break;
|
|
case PAGETYPE_MEGACELL:
|
|
mng_LoadLocalMegacellPage(infile);
|
|
break;
|
|
case PAGETYPE_UNKNOWN:
|
|
break;
|
|
default:
|
|
Int3(); // Unrecognized pagetype, possible corrupt data following
|
|
break;
|
|
}
|
|
}
|
|
cfclose(infile);
|
|
}
|
|
|
|
Loading_locals = 0;
|
|
Loading_addon_table = -1;
|
|
|
|
mng_CompileAddonPages();
|
|
|
|
// Close error file
|
|
if (Data_error_file != NULL) {
|
|
fprintf(Data_error_file, "\nTotal errors: %d", Data_error_count);
|
|
fclose(Data_error_file);
|
|
}
|
|
|
|
// Clear flag
|
|
Loading_addon = false;
|
|
}
|
|
/*
|
|
#define MAX_256s 200
|
|
int Num_256s=0;
|
|
char Texture256Names[MAX_256s][80];
|
|
void Read256TextureNames ()
|
|
{
|
|
int n=FindArg ("-File256");
|
|
if (!n)
|
|
return;
|
|
CFILE *infile;
|
|
infile=(CFILE *)cfopen (GameArgs[n+1],"rt");
|
|
if (!infile)
|
|
{
|
|
mprintf(0,"Couldn't open 256 file!\n");
|
|
return;
|
|
}
|
|
|
|
char curline[200];
|
|
int done=0;
|
|
while (!done)
|
|
{
|
|
if (cfeof(infile))
|
|
{
|
|
done=1;
|
|
continue;
|
|
}
|
|
|
|
// Read a line and parse it
|
|
cf_ReadString (curline,200,infile);
|
|
if (curline[0]==';' || curline[1]==';' || curline[0]==' ' || curline[1]==' ')
|
|
continue;
|
|
if (!(isalnum(curline[0])))
|
|
continue;
|
|
strcpy (Texture256Names[Num_256s],curline);
|
|
Num_256s++;
|
|
}
|
|
cfclose (infile);
|
|
}*/
|
|
|
|
void DataError(const char *fmt, ...) {
|
|
// Got a data error!
|
|
// Int3();
|
|
|
|
// Ignore this if not loading add-on data
|
|
if (!Loading_addon)
|
|
return;
|
|
|
|
// Increment error count
|
|
Data_error_count++;
|
|
|
|
// Write to file if switch specified
|
|
if (FindArg("-datacheck")) {
|
|
static char last_filename[_MAX_PATH];
|
|
std::va_list arglist;
|
|
char buf[1024];
|
|
|
|
va_start(arglist, fmt);
|
|
std::vsnprintf(buf, sizeof(buf), fmt, arglist);
|
|
va_end(arglist);
|
|
|
|
// Open file if not already open
|
|
if (Data_error_file == NULL) {
|
|
Data_error_file = fopen("datacheck.out", "wt");
|
|
|
|
if (Data_error_file == NULL)
|
|
return;
|
|
|
|
last_filename[0] = 0;
|
|
}
|
|
|
|
// If this is a new addon file, print the name
|
|
if (strcmp(last_filename, Addon_filename)) {
|
|
if (last_filename[0])
|
|
fprintf(Data_error_file, "\n\n");
|
|
fprintf(Data_error_file, "Errors in addon file <%s>:\n\n", Addon_filename);
|
|
strcpy(last_filename, Addon_filename);
|
|
}
|
|
|
|
// Print the message
|
|
fprintf(Data_error_file, " ");
|
|
fprintf(Data_error_file, "%s", buf);
|
|
}
|
|
}
|