/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** 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 .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//////// Win32BIGFileSystem.h ///////////////////////////
// Bryan Cleveland, August 2002
/////////////////////////////////////////////////////////////
#include
#include "Common/AudioAffect.h"
#include "Common/ArchiveFile.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/File.h"
#include "Common/GameAudio.h"
#include "Common/GameMemory.h"
#include "Common/LocalFileSystem.h"
#include "Win32Device/Common/Win32BIGFile.h"
#include "Win32Device/Common/Win32BIGFileSystem.h"
#include "Common/registry.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
static const char *BIGFileIdentifier = "BIGF";
Win32BIGFileSystem::Win32BIGFileSystem() : ArchiveFileSystem() {
}
Win32BIGFileSystem::~Win32BIGFileSystem() {
}
void Win32BIGFileSystem::init() {
DEBUG_ASSERTCRASH(TheLocalFileSystem != NULL, ("TheLocalFileSystem must be initialized before TheArchiveFileSystem."));
if (TheLocalFileSystem == NULL) {
return;
}
loadBigFilesFromDirectory("", "*.big");
// load original Generals assets
AsciiString installPath;
GetStringFromGeneralsRegistry("", "InstallPath", installPath );
//@todo this will need to be ramped up to a crash for release
#ifndef _INTERNAL
// had to make this non-internal only, otherwise we can't autobuild
// GeneralsZH...
DEBUG_ASSERTCRASH(installPath != "", ("Be 1337! Go install Generals!"));
#endif
if (installPath!="")
loadBigFilesFromDirectory(installPath, "*.big");
}
void Win32BIGFileSystem::reset() {
}
void Win32BIGFileSystem::update() {
}
void Win32BIGFileSystem::postProcessLoad() {
}
ArchiveFile * Win32BIGFileSystem::openArchiveFile(const Char *filename) {
File *fp = TheLocalFileSystem->openFile(filename, File::READ | File::BINARY);
AsciiString archiveFileName;
archiveFileName = filename;
archiveFileName.toLower();
Int archiveFileSize = 0;
Int numLittleFiles = 0;
ArchiveFile *archiveFile = NEW Win32BIGFile;
DEBUG_LOG(("Win32BIGFileSystem::openArchiveFile - opening BIG file %s\n", filename));
if (fp == NULL) {
DEBUG_CRASH(("Could not open archive file %s for parsing", filename));
return NULL;
}
AsciiString asciibuf;
char buffer[_MAX_PATH];
fp->read(buffer, 4); // read the "BIG" at the beginning of the file.
buffer[4] = 0;
if (strcmp(buffer, BIGFileIdentifier) != 0) {
DEBUG_CRASH(("Error reading BIG file identifier in file %s", filename));
fp->close();
fp = NULL;
return NULL;
}
// read in the file size.
fp->read(&archiveFileSize, 4);
DEBUG_LOG(("Win32BIGFileSystem::openArchiveFile - size of archive file is %d bytes\n", archiveFileSize));
// char t;
// read in the number of files contained in this BIG file.
// change the order of the bytes cause the file size is in reverse byte order for some reason.
fp->read(&numLittleFiles, 4);
numLittleFiles = ntohl(numLittleFiles);
DEBUG_LOG(("Win32BIGFileSystem::openArchiveFile - %d are contained in archive\n", numLittleFiles));
// for (Int i = 0; i < 2; ++i) {
// t = buffer[i];
// buffer[i] = buffer[(4-i)-1];
// buffer[(4-i)-1] = t;
// }
// seek to the beginning of the directory listing.
fp->seek(0x10, File::START);
// read in each directory listing.
ArchivedFileInfo *fileInfo = NEW ArchivedFileInfo;
for (Int i = 0; i < numLittleFiles; ++i) {
Int filesize = 0;
Int fileOffset = 0;
fp->read(&fileOffset, 4);
fp->read(&filesize, 4);
filesize = ntohl(filesize);
fileOffset = ntohl(fileOffset);
fileInfo->m_archiveFilename = archiveFileName;
fileInfo->m_offset = fileOffset;
fileInfo->m_size = filesize;
// read in the path name of the file.
Int pathIndex = -1;
do {
++pathIndex;
fp->read(buffer + pathIndex, 1);
} while (buffer[pathIndex] != 0);
Int filenameIndex = pathIndex;
while ((buffer[filenameIndex] != '\\') && (buffer[filenameIndex] != '/') && (filenameIndex >= 0)) {
--filenameIndex;
}
fileInfo->m_filename = (char *)(buffer + filenameIndex + 1);
fileInfo->m_filename.toLower();
buffer[filenameIndex + 1] = 0;
AsciiString path;
path = buffer;
AsciiString debugpath;
debugpath = path;
debugpath.concat(fileInfo->m_filename);
// DEBUG_LOG(("Win32BIGFileSystem::openArchiveFile - adding file %s to archive file %s, file number %d\n", debugpath.str(), fileInfo->m_archiveFilename.str(), i));
archiveFile->addFile(path, fileInfo);
}
archiveFile->attachFile(fp);
delete fileInfo;
fileInfo = NULL;
// leave fp open as the archive file will be using it.
return archiveFile;
}
void Win32BIGFileSystem::closeArchiveFile(const Char *filename) {
// Need to close the specified big file
ArchiveFileMap::iterator it = m_archiveFileMap.find(filename);
if (it == m_archiveFileMap.end()) {
return;
}
if (stricmp(filename, MUSIC_BIG) == 0) {
// Stop the current audio
TheAudio->stopAudio(AudioAffect_Music);
// No need to turn off other audio, as the lookups will just fail.
}
DEBUG_ASSERTCRASH(stricmp(filename, MUSIC_BIG) == 0, ("Attempting to close Archive file '%s', need to add code to handle its shutdown correctly.", filename));
// may need to do some other processing here first.
delete (it->second);
m_archiveFileMap.erase(it);
}
void Win32BIGFileSystem::closeAllArchiveFiles() {
}
void Win32BIGFileSystem::closeAllFiles() {
}
Bool Win32BIGFileSystem::loadBigFilesFromDirectory(AsciiString dir, AsciiString fileMask, Bool overwrite) {
FilenameList filenameList;
TheLocalFileSystem->getFileListInDirectory(dir, AsciiString(""), fileMask, filenameList, TRUE);
Bool actuallyAdded = FALSE;
FilenameListIter it = filenameList.begin();
while (it != filenameList.end()) {
ArchiveFile *archiveFile = openArchiveFile((*it).str());
if (archiveFile != NULL) {
DEBUG_LOG(("Win32BIGFileSystem::loadBigFilesFromDirectory - loading %s into the directory tree.\n", (*it).str()));
loadIntoDirectoryTree(archiveFile, *it, overwrite);
m_archiveFileMap[(*it)] = archiveFile;
DEBUG_LOG(("Win32BIGFileSystem::loadBigFilesFromDirectory - %s inserted into the archive file map.\n", (*it).str()));
actuallyAdded = TRUE;
}
it++;
}
return actuallyAdded;
}