-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
276 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
#include "indicator.h" | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
// INDicator <exec-file> <new-wad-file-to-use> <platform> <version> | ||
if (argc != 5) | ||
{ | ||
printUsageString(); | ||
return -1; | ||
} | ||
|
||
std::string executable = std::string(argv[1]); | ||
std::string chosenWad = std::string(argv[2]); | ||
std::string platform = std::string(argv[3]); | ||
std::string version = std::string(argv[4]); | ||
std::string explainStr = "PC executable "; | ||
bool isPSX = false; | ||
bool isM2K = false; | ||
|
||
if (validatePlatformArg(platform) && validateVersionArg(version)) | ||
{ | ||
if (0 == PLAT_PSX_STR.compare(platform)) | ||
{ | ||
isPSX = true; | ||
explainStr = "PSX executable "; | ||
} | ||
|
||
if (0 == VERS_M2K_STR.compare(version)) | ||
{ | ||
isM2K = true; | ||
} | ||
|
||
makeBackup(executable); | ||
replaceBytesInExec(executable, chosenWad, isPSX, isM2K); | ||
std::cout << "INDicator: " << explainStr << "\"" << executable << "\" now using \"" << chosenWad << "\" as the loaded wad file." << std::endl; | ||
} | ||
|
||
else | ||
{ | ||
printUsageString(); | ||
return -1; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
/** | ||
* @brief Convert Platform argument to lowercase and validate that it is either PC or PSX | ||
* | ||
* @param arg to compare | ||
* @return true if platform argument is either pc or psx, | ||
* @return false if platform argument is anything else | ||
*/ | ||
bool validatePlatformArg(std::string arg) | ||
{ | ||
toLowerCase(arg); | ||
return 0 == PLAT_PSX_STR.compare(arg) || 0 == PLAT_PC_STR.compare(arg); | ||
} | ||
|
||
/** | ||
* @brief Convert Version argument to lowercase and validate that it is either MTVMG or M2K | ||
* | ||
* @param arg to compare | ||
* @return true if version argument is either mtvmg or m2k, | ||
* @return false if version argument is anything else | ||
*/ | ||
bool validateVersionArg(std::string arg) | ||
{ | ||
toLowerCase(arg); | ||
return 0 == VERS_M2K_STR.compare(arg) || 0 == VERS_MTVMG_STR.compare(arg); | ||
} | ||
|
||
/** | ||
* @brief Print Usage String | ||
* | ||
*/ | ||
void printUsageString() | ||
{ | ||
std::cout << USAGE_STR << std::endl; | ||
} | ||
|
||
/** | ||
* @brief Convert string to lowercase | ||
* | ||
* @param s string to convert | ||
*/ | ||
void toLowerCase(std::string s) | ||
{ | ||
std::transform(s.begin(), s.end(), s.begin(), ::tolower); | ||
} | ||
|
||
/** | ||
* @brief Cut the tail end of a string if it exceeds the max character length, the final character at index of 'maxSize - 1' will be a null char | ||
* | ||
* @param s string to process | ||
* @param maxSize maximum length of the string (including extension), anything beyond this size will be cut | ||
* @param extension file extension to keep at the end of the truncated string | ||
* @return Truncated string | ||
*/ | ||
std::string truncateString(std::string s, int maxSize, const char *extension) | ||
{ | ||
// First, strip the path in case we have one as we only want the filename | ||
std::string strippedStr = s; | ||
int index = strippedStr.find_last_of("/\\"); | ||
strippedStr = s.substr(index + 1); | ||
// Now remove any extension the file might have, we're gonna put it back on after. This just prevents 'ANDY.WAD.wad' | ||
int extIndex = strippedStr.find_last_of("."); | ||
strippedStr = strippedStr.substr(0, extIndex); | ||
|
||
// Now perform the truncating if need be | ||
int sizeData = maxSize - strlen(extension) - 1; // -1 to allow for NULL terminator to be within range when calling std::string.c_str() | ||
std::string truncatedStr = strippedStr.substr(0, sizeData) + extension; | ||
|
||
if (strippedStr.length() + strlen(extension) > maxSize) | ||
{ | ||
std::cout << "INDicator: " << extension << " file reference in the executable has been shortened to \"" << truncatedStr << "\". Please rename the actual file when moving the file(s) to the target location." << std::endl; | ||
} | ||
return truncatedStr; | ||
} | ||
|
||
/** | ||
* @brief Pad a std::string with characters if size > string length and return it is a C string | ||
* | ||
* @param s to pad | ||
* @param pad character (as std::string) | ||
* @param size of string, if input string is shorter than this, padding occurs | ||
* @return padded C string | ||
*/ | ||
const char *makePaddedCString(std::string s, std::string pad, int size) | ||
{ | ||
if (s.length() >= size) | ||
{ | ||
return s.c_str(); | ||
} | ||
else | ||
{ | ||
std::string pads; | ||
int sizeDiff = size - s.length(); | ||
for (int i = 0; i < sizeDiff; ++i) | ||
{ | ||
pads.append(pad); | ||
} | ||
return s.append(pads).c_str(); | ||
} | ||
} | ||
|
||
/** | ||
* @brief Write bytes to file at position offset, size will determine how many bytes are written. If data is < size, data is null-padded. | ||
* | ||
* @param offset location in file to write bytes to | ||
* @param file to write bytes to | ||
* @param size of data to write | ||
* @param data to write at location in file | ||
*/ | ||
void writeBytes(int offset, std::string file, int size, std::string data) | ||
{ | ||
std::fstream stream(file.c_str(), std::ios_base::binary | std::ios_base::out | std::ios_base::in); | ||
stream.seekp(offset, std::ios_base::beg); | ||
stream.write(makePaddedCString(data, "\0", size), size); | ||
stream.flush(); | ||
stream.close(); | ||
} | ||
|
||
/** | ||
* @brief Backup executable as <executable-file>.bak | ||
* | ||
* @param executable to backup | ||
*/ | ||
void makeBackup(std::string executable) | ||
{ | ||
std::ifstream src(executable, std::ios::binary); | ||
std::ofstream dst(executable + ".bak", std::ios::binary); | ||
dst << src.rdbuf(); | ||
std::cout << "INDicator: Backing up \"" << executable << "\" as \"" << executable + ".bak" | ||
<< "\"" << std::endl; | ||
} | ||
|
||
/** | ||
* @brief Replace bytes with the wad filename at the specific offset based on whether the PC or PSX version | ||
* | ||
* @param executable to perform the replacement on | ||
* @param wad filename to inject | ||
* @param isPSX True if this is a PSX executable, False if PC | ||
* @param isM2K True if this is a Music 2000 executable, False if MTV Music Generator | ||
* (offsets are different so we need to differentiate between the two) | ||
*/ | ||
void replaceBytesInExec(std::string executable, std::string wad, bool isPSX, bool isM2K) | ||
{ | ||
std::string wadFname; | ||
std::string indFname; | ||
std::string nullPad = "\0"; | ||
std::fstream execF(executable.c_str(), std::ios_base::binary | std::ios_base::out | std::ios_base::in); | ||
|
||
if (isPSX) | ||
{ | ||
wadFname = truncateString(wad, PSX_WAD_IND_REF_SIZE_BYTES, ".wad"); | ||
indFname = truncateString(wad, PSX_WAD_IND_REF_SIZE_BYTES, ".ind"); | ||
|
||
// Both MTV Music Generator and Music 2000 have the same offsets for the wad/ind files, | ||
// so we don't need to check version for PSX | ||
writeBytes(PSX_WAD_BYTE_OFFSET_1, executable, PSX_WAD_IND_REF_SIZE_BYTES, wadFname); | ||
writeBytes(PSX_WAD_BYTE_OFFSET_2, executable, PSX_WAD_IND_REF_SIZE_BYTES, wadFname); | ||
writeBytes(PSX_IND_BYTE_OFFSET_1, executable, PSX_WAD_IND_REF_SIZE_BYTES, indFname); | ||
writeBytes(PSX_IND_BYTE_OFFSET_2, executable, PSX_WAD_IND_REF_SIZE_BYTES, indFname); | ||
} | ||
// PC | ||
else | ||
{ | ||
wadFname = truncateString(wad, PC_WAD_IND_REF_SIZE_BYTES, ".wad"); | ||
indFname = truncateString(wad, PC_WAD_IND_REF_SIZE_BYTES, ".ind"); | ||
|
||
if (isM2K) | ||
{ | ||
writeBytes(PC_M2K_WAD_BYTE_OFFSET_1, executable, PC_WAD_IND_REF_SIZE_BYTES, wadFname); | ||
writeBytes(PC_M2K_WAD_BYTE_OFFSET_2, executable, PC_WAD_IND_REF_SIZE_BYTES, wadFname); | ||
writeBytes(PC_M2K_WAD_BYTE_OFFSET_3, executable, PC_WAD_IND_REF_SIZE_BYTES, wadFname); | ||
writeBytes(PC_M2K_IND_BYTE_OFFSET_1, executable, PC_WAD_IND_REF_SIZE_BYTES, indFname); | ||
writeBytes(PC_M2K_IND_BYTE_OFFSET_2, executable, PC_WAD_IND_REF_SIZE_BYTES, indFname); | ||
} | ||
else | ||
{ | ||
writeBytes(PC_MTVMG_WAD_BYTE_OFFSET_1, executable, PC_WAD_IND_REF_SIZE_BYTES, wadFname); | ||
writeBytes(PC_MTVMG_WAD_BYTE_OFFSET_2, executable, PC_WAD_IND_REF_SIZE_BYTES, wadFname); | ||
writeBytes(PC_MTVMG_WAD_BYTE_OFFSET_3, executable, PC_WAD_IND_REF_SIZE_BYTES, wadFname); | ||
writeBytes(PC_MTVMG_IND_BYTE_OFFSET_1, executable, PC_WAD_IND_REF_SIZE_BYTES, indFname); | ||
writeBytes(PC_MTVMG_IND_BYTE_OFFSET_2, executable, PC_WAD_IND_REF_SIZE_BYTES, indFname); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
#pragma once | ||
#ifndef INDICATOR_H | ||
#define INDICATOR_H | ||
#include <string.h> | ||
#include <iostream> | ||
#include <string.h> | ||
#include <fstream> | ||
#include <cctype> | ||
#include <algorithm> | ||
|
||
void printUsageString(); | ||
void toLowerCase(std::string); | ||
void replaceBytesInExec(std::string, std::string, bool, bool); | ||
void writeBytes(int, std::string, int, std::string); | ||
void makeBackup(std::string); | ||
bool validatePlatformArg(std::string); | ||
bool validateVersionArg(std::string); | ||
const char *makePaddedCString(std::string, std::string, int); | ||
std::string truncateString(std::string, int, const char *); | ||
|
||
const int PSX_WAD_BYTE_OFFSET_1 = 0x00000918; | ||
const int PSX_WAD_BYTE_OFFSET_2 = 0x00000CF8; | ||
const int PSX_IND_BYTE_OFFSET_1 = 0x00000924; | ||
const int PSX_IND_BYTE_OFFSET_2 = 0x00000D04; | ||
|
||
const int PC_M2K_WAD_BYTE_OFFSET_1 = 0x00133FAC; | ||
const int PC_M2K_WAD_BYTE_OFFSET_2 = 0x0013CDDC; | ||
const int PC_M2K_WAD_BYTE_OFFSET_3 = 0x0010528D; | ||
const int PC_M2K_IND_BYTE_OFFSET_1 = 0x00133F9C; | ||
const int PC_M2K_IND_BYTE_OFFSET_2 = 0x0013CDCC; | ||
|
||
const int PC_MTVMG_WAD_BYTE_OFFSET_1 = 0x00133FC4; | ||
const int PC_MTVMG_WAD_BYTE_OFFSET_2 = 0x0013CDF4; | ||
const int PC_MTVMG_WAD_BYTE_OFFSET_3 = 0x001052BD; | ||
const int PC_MTVMG_IND_BYTE_OFFSET_1 = 0x00133FB4; | ||
const int PC_MTVMG_IND_BYTE_OFFSET_2 = 0x0013CDE4; | ||
|
||
const int PSX_WAD_IND_REF_SIZE_BYTES = 12; | ||
const int PC_WAD_IND_REF_SIZE_BYTES = 16; | ||
|
||
const std::string USAGE_STR = "INDicator: Replace the reference to wad/ind file in a M2K or MTV Music Generator executable\n\tUsage: indicator <exec-file> <new-wad-file-to-use> <platform - pc | psx> <version - m2k | mtvmg>"; | ||
const std::string PLAT_PSX_STR = "psx"; | ||
const std::string PLAT_PC_STR = "pc"; | ||
const std::string VERS_MTVMG_STR = "mtvmg"; | ||
const std::string VERS_M2K_STR = "m2k"; | ||
|
||
#endif /* INDICATOR_H */ |