Skip to content

[TOOLS] GenBIG - A CLI Tool for BIG Archives #535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions GeneralsMD/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ if(GENZH_ZEROHOUR_INSTALL_PREFIX AND NOT "${GENZH_ZEROHOUR_INSTALL_PREFIX}" STRE
install(FILES $<TARGET_PDB_FILE:z_debugwindow> DESTINATION "${GENZH_ZEROHOUR_INSTALL_PREFIX}" OPTIONAL)
endif()

if(TARGET z_genbig)
install(TARGETS z_genbig RUNTIME DESTINATION "${GENZH_ZEROHOUR_INSTALL_PREFIX}")
install(FILES $<TARGET_PDB_FILE:z_genbig> DESTINATION "${GENZH_ZEROHOUR_INSTALL_PREFIX}" OPTIONAL)
endif()

if(TARGET z_guiedit)
install(TARGETS z_guiedit RUNTIME DESTINATION "${GENZH_ZEROHOUR_INSTALL_PREFIX}")
install(FILES $<TARGET_PDB_FILE:z_guiedit> DESTINATION "${GENZH_ZEROHOUR_INSTALL_PREFIX}" OPTIONAL)
Expand Down
1 change: 1 addition & 0 deletions GeneralsMD/Code/Tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Build useful tool binaries.
if(GENZH_BUILD_ZEROHOUR_TOOLS)
add_subdirectory(DebugWindow)
add_subdirectory(GenBIG)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest call it BigArchiver or similar.

add_subdirectory(GUIEdit)
add_subdirectory(ImagePacker)
add_subdirectory(MapCacheBuilder)
Expand Down
32 changes: 32 additions & 0 deletions GeneralsMD/Code/Tools/GenBIG/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
set(GENBIG_SRC
"src/BIGFile.h"
"src/BIGFile.cpp"
"src/main.cpp"
)

add_executable(z_genbig WIN32)

target_sources(z_genbig PRIVATE ${GENBIG_SRC})

target_include_directories(z_genbig PRIVATE
src
)

target_link_libraries(z_genbig PRIVATE
benchmark
comctl32
dbghelplib
imm32
vfw32
winmm
z_gameengine
z_gameenginedevice
z_profile
z_wwvegas
zi_libraries_include
)

if(WIN32 OR "${CMAKE_SYSTEM}" MATCHES "Windows")
target_link_options(z_genbig PRIVATE /subsystem:console)
set_target_properties(z_genbig PROPERTIES OUTPUT_NAME GenBIG)
endif()
109 changes: 109 additions & 0 deletions GeneralsMD/Code/Tools/GenBIG/src/BIGFile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "BIGFile.h"
#include <iostream>
#include <fstream>
#include <cstring>
#include <filesystem>

BIGFile::BIGFile()
{
init();
}

BIGFile::~BIGFile()
{
if (m_archiveFile != nullptr) {
m_archiveFile->close();
delete m_archiveFile;
m_archiveFile = nullptr;
}
if (m_paths != nullptr) {
delete[] m_paths;
m_paths = nullptr;
}
if (m_outputDir != nullptr) {
delete[] m_outputDir;
m_outputDir = nullptr;
}
if (m_archiveName != nullptr) {
delete[] m_archiveName;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer to not use new/delete where so possible and use things like STL containers (std::string for strings) and smart pointers (std::unique_ptr, std::shared_ptr).

When interfacing with Engine, can also use AsciiString and UnicodeString.

m_archiveName = nullptr;
}
}

void BIGFile::init(void) {
DEBUG_ASSERTCRASH(TheLocalFileSystem != NULL, ("TheLocalFileSystem must be initialized before TheArchiveFileSystem."));
if (TheLocalFileSystem == NULL) {
return;
}
}

bool BIGFile::addToArchive() {
if (m_archiveName == nullptr || m_outputDir == nullptr || m_paths == nullptr) {
return false;
}

m_archiveFile = TheArchiveFileSystem->openArchiveFile(m_archiveName);

// logic to add files to the archive

m_archiveFile->close();
printf("Failed to Add file to archive.\n");
return false;
}

bool BIGFile::deleteFromArchive(const char *path) {
if (path == nullptr) {
return false;
}

// logic to delete file from the archive
// function to use: doesFileExist

printf("File not found in archive.\n");
return false;
}

bool BIGFile::listArchive() {
if (m_archiveName == nullptr) {
return false;
}
// todo m_archiveFileSystem = ?
m_archiveFile = m_archiveFileSystem->openArchiveFile(m_archiveName);
FilenameList filenameList;
m_archiveFile->getFileListInDirectory(AsciiString(""), AsciiString(""), AsciiString("*"), filenameList, TRUE);
for (const auto &filename : filenameList) {
std::cout << filename.str() << std::endl;
}
m_archiveFile->close();
return true;
}

bool BIGFile::extractArchive(const char *path) {
// logic to extract file from the archive
// function to use: openFile, addFile,
return false;
}

bool BIGFile::setArchiveFileName(const char *path) {
if (path == nullptr) {
return false;
}
// logic to handle relative paths
// function to use: loadIntoDirectoryTree,
m_archiveName = path;

return true;
}

bool BIGFile::setOutputDir(const char *path) {
return false;
}

bool BIGFile::setPaths(const char *path) {
// logic to handle relative paths of files to be added to/remove from the archive
return false;
}




54 changes: 54 additions & 0 deletions GeneralsMD/Code/Tools/GenBIG/src/BIGFile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// Created by User on 27/03/2025.
//

#ifndef GENZH_BIGFILE_H
#define GENZH_BIGFILE_H
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use #pragma once instead of header include guards.


#include "Common/ArchiveFile.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/file.h"
#include "Common/LocalFileSystem.h"
#include "Win32Device/Common/Win32BIGFile.h"
#include "Win32Device/Common/Win32BIGFileSystem.h"

class BIGFile : Win32BIGFileSystem
{
public:
BIGFile();
~BIGFile();

// Initialize the archive file system
void init( void ) override;

// Handle archive operations
bool addToArchive();
bool addDirectoryToArchive(const char* path);
bool deleteFromArchive(const char* name = NULL);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use nullptr for non-vs6 code.

bool listArchive();
bool extractArchive(const char* path = NULL);

// getters
ArchiveFile* getArchiveFile() const { return m_archiveFile; }
const char* getArchiveFileName() const { return m_archiveName; }
const char* getOutputDir() const { return m_outputDir; }
const char** getPaths() const { return m_paths; }
int getPathCount() const { return m_pathCount; }

// setters
bool setArchiveFileName(const char* path);
bool setOutputDir(const char* path);
bool setPaths(const char* path);

private:
Win32BIGFileSystem * m_archiveFileSystem; // or Win32BIGFileSystem?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks odd that this class derives from Win32BIGFileSystem, but also owns a pointer to Win32BIGFileSystem.

Before jumping into big implementations, I suggest to take a moment and think carefully about the design.

Upon a first look, we could try to have something like:

class Win32BIGFileRW : public Win32BIGFile

class Win32BIGFileSystemRW : public Win32BIGFileSystem

You would just need to find an elegant way to make Win32BIGFileSystem use Win32BIGFileRW as well, probably with a template.

This could allow making a new Read Write version of the archive and its system, while reusing what is already there for opening and reading archives.

But perhaps the design of the Win32BIGFile and Win32BIGFileSystem make it hard to fit in the write functionality nicely. In that case a different strategy may be better.

Please also be mindful about performance when implementing this.

ArchiveFile* m_archiveFile;
const char *m_archiveName;
const char *m_outputDir;
const char **m_paths;
int m_pathCount;
// list files to archive
std::vector<std::string> m_files;
};

#endif //GENZH_BIGFILE_H
146 changes: 146 additions & 0 deletions GeneralsMD/Code/Tools/GenBIG/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/**
* @file main.cpp
*
* @author DevGeniusCode
*
* @brief A CLI tool to handle BIG files for Command & Conquer Generals & Zero Hour
*
* @project GenBIG
*
* @date March 2025
*
* @version 1.0
*
* @copyright GenBIG 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
* 2 of the License, or (at your option) any later version.
* A full copy of the GNU General Public License can be found in
* LICENSE
*/


#include <iostream>
#include "BIGFile.h"

void printHelp()
{
printf("Usage: GenBIG [options] <path> <path> ...\n\n");

printf("Options:\n"
" -a <path> Add a file or directory to the archive\n"
" -d <name> Delete a file from the archive\n"
" -l List the contents of the archive\n"
" -x Extract the contents of the archive\n"
" -o <path> Output dir\n"
" -h Display this help message\n\n");

printf("Examples:"
" Example 1: Add a file to the archive\n"
" GenBIG -a file.txt -o archive.big\n\n"
" Example 2: Add a directory to the archive\n"
" GenBIG -a dir -o archive.big\n\n"
" Example 3: Add multiple files and directories to the archive\n"
" GenBIG -a file1.txt -a file2.txt -a dir1 -a dir2 -o archive.big\n\n"
" Example 4: Extract the contents of the archive\n"
" GenBIG -x archive.big -o dir\n\n"
" Example 5: Extract the contents of the archive from specific directory\n"
" GenBIG -x your/path/archive.big -o .\n\n"
" Example 6: Delete a file from an archive\n"
" GenBIG -d file.txt -o archive.big\n\n");
}

// GLOBALS ////////////////////////////////////////////////////////////////////
HINSTANCE ApplicationHInstance = NULL; ///< our application instance
HWND ApplicationHWnd = NULL; ///< our application window handle

const Char *g_strFile = "data\\Generals.str";
const Char *g_csfFile = "data\\%s\\Generals.csf";
const char *gAppPrefix = ""; /// So WB can have a different debug log file name.

// Main function to handle user input and options
int main(int argc, char *argv[]) {
printf("GenBIG - BIG Archive Tool By TheSuperHackers v1.0\n\n");

if (argc < 2) {
printHelp();
return 1;
}

BIGFile *bigFile = new BIGFile();

int optionError = 0;

for (int i = 1; i < argc; ++i) {
switch (argv[i][0]) {
case '-':
switch (argv[i][1]) {
case 'a': // Add file or directory to the archive
case 'd': // Delete file from archive
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we use cxxopts for command line, to standardize setups. You can see example how it is used in https://github.com/xezon/unassemblize

if (i + 1 < argc && argv[i + 1][0] != '-') {
bigFile->setPaths(argv[i + 1]);
i++; // Skip the next argument
} else {
printf("Error: Missing path\n");
optionError = 1;
}
break;
case 'l': // List the contents of the archive
case 'x': // Extract contents from the archive
if (i + 1 < argc && argv[i + 1][0] != '-') {
bigFile->setArchiveFileName(argv[i + 1]);
i++;
} else {
printf("Error: Missing archive name\n");
optionError = 1;
}
break;
case 'o': // Set output directory or BIG file name to create
if (i + 1 < argc && argv[i + 1][0] != '-') {
bigFile->setOutputDir(argv[i + 1]);
i++;
} else {
printf("Error: Missing output directory after -o\n");
optionError = 1;
}
break;
case 'h': // Display help message
printHelp();
return 0;

default:
printf("Error: Unknown option -%c\n", argv[i][1]);
optionError = 1;
}
break;

default:
printf("Error: Invalid argument %s\n", argv[i]);
optionError = 1;
}
if (optionError) {
break;
}
}

if (optionError || bigFile->getArchiveFileName() == NULL) {
return 1;
}

// Perform the appropriate action based on the options
if (strcmp(argv[1], "-a") == 0) {
bigFile->addToArchive();
}
else if (strcmp(argv[1], "-d") == 0) {
bigFile->deleteFromArchive();
}
else if (strcmp(argv[1], "-l") == 0) {
bigFile->listArchive();
}
else if (strcmp(argv[1], "-x") == 0) {
bigFile->extractArchive();
}

delete bigFile;
return 0;
}
Loading