✨ Enable use of command line options
This commit is contained in:
parent
313f65609f
commit
43614e1498
6 changed files with 137 additions and 20 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -3,7 +3,8 @@
|
||||||
"filereader.h": "c",
|
"filereader.h": "c",
|
||||||
"decrypt.h": "c",
|
"decrypt.h": "c",
|
||||||
"stdint.h": "c",
|
"stdint.h": "c",
|
||||||
"decryptutilities.h": "c"
|
"decryptutilities.h": "c",
|
||||||
|
"fileutilities.h": "c"
|
||||||
},
|
},
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"init",
|
"init",
|
||||||
|
|
14
README.md
14
README.md
|
@ -1,5 +1,19 @@
|
||||||
This tool installs a patch, assuming the patch files are already downloaded, and if not patching from -1, that the previous patch is already installed.
|
This tool installs a patch, assuming the patch files are already downloaded, and if not patching from -1, that the previous patch is already installed.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
To compile:
|
||||||
|
|
||||||
|
```
|
||||||
|
./build.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
To use:
|
||||||
|
|
||||||
|
```
|
||||||
|
./patcher-installer --disk /tmp/patcher/cdn-patch.swtor.com/patch/assets_swtor_main/assets_swtor_main_-1to0/assets_swtor_main_-1to0.z08 --offset 267071107 --size 165738618 --keys 3393608425 2820264972 3930508185 > ../swtor_main_gfx_1.tor
|
||||||
|
```
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
## Miniz <[https://github.com/richgel999/miniz](https://github.com/richgel999/miniz)>
|
## Miniz <[https://github.com/richgel999/miniz](https://github.com/richgel999/miniz)>
|
||||||
|
|
|
@ -31,7 +31,7 @@ void initFileReader(char path[], unsigned long offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Reads the given amount of bytes from the file and returns them. Automatically opens next file if EOF is reached.
|
//Reads the given amount of bytes from the file and writes them into the given buffer. Automatically opens next file if EOF is reached.
|
||||||
void getBytes(uint8_t* buffer, unsigned long numBytes) {
|
void getBytes(uint8_t* buffer, unsigned long numBytes) {
|
||||||
char* bufferPosition = buffer;
|
char* bufferPosition = buffer;
|
||||||
unsigned long remainingBytes = numBytes;
|
unsigned long remainingBytes = numBytes;
|
||||||
|
@ -40,8 +40,14 @@ void getBytes(uint8_t* buffer, unsigned long numBytes) {
|
||||||
while (remainingBytes > 0) {
|
while (remainingBytes > 0) {
|
||||||
//Read as many bytes as we can from the current file
|
//Read as many bytes as we can from the current file
|
||||||
const unsigned long availableBytes = min(remainingBytes, file.size - file.offset);
|
const unsigned long availableBytes = min(remainingBytes, file.size - file.offset);
|
||||||
|
|
||||||
|
//Read bytes into buffer if we have a valid buffer, otherwise skip bytes
|
||||||
|
if (buffer) {
|
||||||
readBytesIntoBuffer(bufferPosition, availableBytes);
|
readBytesIntoBuffer(bufferPosition, availableBytes);
|
||||||
bufferPosition += availableBytes;
|
bufferPosition += availableBytes;
|
||||||
|
} else {
|
||||||
|
skipBytes(availableBytes);
|
||||||
|
}
|
||||||
remainingBytes -= availableBytes;
|
remainingBytes -= availableBytes;
|
||||||
|
|
||||||
//If we've reached end of file, close file and open next file
|
//If we've reached end of file, close file and open next file
|
||||||
|
|
111
src/main.c
111
src/main.c
|
@ -1,4 +1,5 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <getopt.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -26,24 +27,98 @@ uint32_t getUint32(uint8_t* buffer) {
|
||||||
(uint32_t)buffer[3] << 24;
|
(uint32_t)buffer[3] << 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, unsigned char *argv[]) {
|
struct arguments {
|
||||||
if (argc != 4 && argc != 7) {
|
/** Path to the disk file that contains the start of the file we want to extract.
|
||||||
fprintf(stderr, "Wrong number of arguments. Usage: patcher-installer <disk_name> <disk_offset> <file_size> [<key0> <key1> <key2>]");
|
* The file may stretch across multiple disks though.
|
||||||
|
*/
|
||||||
|
char* diskName;
|
||||||
|
/** Offset into the disk where the file starts. */
|
||||||
|
unsigned long diskOffset;
|
||||||
|
/** Size of the file stored in the disk. */
|
||||||
|
unsigned long fileSize;
|
||||||
|
//Decryption keys
|
||||||
|
bool isEncrypted;
|
||||||
|
uint32_t key0;
|
||||||
|
uint32_t key1;
|
||||||
|
uint32_t key2;
|
||||||
|
//TODO: For xdelta3, the location of the old file
|
||||||
|
char* prevFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct option long_options[] = {
|
||||||
|
{"disk", required_argument, 0, 'd'},
|
||||||
|
{"offset", required_argument, 0, 'o'},
|
||||||
|
{"size", required_argument, 0, 's'},
|
||||||
|
{"keys", required_argument, 0, 'k'},
|
||||||
|
{"prev", required_argument, 0, 'p'},
|
||||||
|
};
|
||||||
|
|
||||||
|
//Stores current state from command line arguments, initialized to zero
|
||||||
|
struct arguments state = {};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
//Parse command line arguments
|
||||||
|
|
||||||
|
int requiredOptions = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
//in this variable, getopt_long stores the current position in the command line args array
|
||||||
|
int option_index = 0;
|
||||||
|
|
||||||
|
int curOption = getopt_long(argc, argv, "d:o:s:k:", long_options, &option_index);
|
||||||
|
|
||||||
|
//end of command line arguments reached
|
||||||
|
if (curOption == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (curOption) {
|
||||||
|
case 'd': //disk name
|
||||||
|
state.diskName = optarg;
|
||||||
|
requiredOptions |= 1;
|
||||||
|
break;
|
||||||
|
case 'o': //offset
|
||||||
|
state.diskOffset = atol(optarg);
|
||||||
|
requiredOptions |= 2;
|
||||||
|
break;
|
||||||
|
case 's': //size
|
||||||
|
state.fileSize = atol(optarg);
|
||||||
|
requiredOptions |= 4;
|
||||||
|
break;
|
||||||
|
case 'k': { //decryption keys
|
||||||
|
//TODO: parse from optarg
|
||||||
|
uint32_t key0 = atoi(argv[8]);
|
||||||
|
uint32_t key1 = atoi(argv[9]);
|
||||||
|
uint32_t key2 = atoi(argv[10]);
|
||||||
|
//Initialize decryption (pass decryption keys)
|
||||||
|
initDecryptor(key0, key1, key2);
|
||||||
|
state.isEncrypted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown option '%c'.", (char)curOption);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
//TODO: verify argv and assign it to variables
|
}
|
||||||
char* archiveName = argv[1];
|
|
||||||
const unsigned long archiveOffset = atol(argv[2]);
|
|
||||||
const unsigned long fileLength = atol(argv[3]);
|
|
||||||
|
|
||||||
const bool isEncrypted = argc == 7;
|
if (requiredOptions != 7) {
|
||||||
if (isEncrypted) {
|
fprintf(stderr, "Missing arguments, received %i.", requiredOptions);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: verify argv and assign it to variables
|
||||||
|
//char* archiveName = argv[1];
|
||||||
|
//const unsigned long archiveOffset = atol(argv[2]);
|
||||||
|
//const unsigned long fileLength = atol(argv[3]);
|
||||||
|
|
||||||
|
//const bool isEncrypted = argc == 7;
|
||||||
|
/*if (isEncrypted) {
|
||||||
uint32_t key0 = atoi(argv[4]);
|
uint32_t key0 = atoi(argv[4]);
|
||||||
uint32_t key1 = atoi(argv[5]);
|
uint32_t key1 = atoi(argv[5]);
|
||||||
uint32_t key2 = atoi(argv[6]);
|
uint32_t key2 = atoi(argv[6]);
|
||||||
//Initialize decryption (pass decryption keys)
|
//Initialize decryption (pass decryption keys)
|
||||||
initDecryptor(key0, key1, key2);
|
initDecryptor(key0, key1, key2);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
|
@ -63,7 +138,7 @@ int main(int argc, unsigned char *argv[]) {
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
//Initialize file reader
|
//Initialize file reader
|
||||||
initFileReader(archiveName, archiveOffset);
|
initFileReader(state.diskName, state.diskOffset);
|
||||||
|
|
||||||
//Skip local file header (30 bytes + additional length)
|
//Skip local file header (30 bytes + additional length)
|
||||||
getBytes(compressedChunk, 30UL);
|
getBytes(compressedChunk, 30UL);
|
||||||
|
@ -75,10 +150,12 @@ int main(int argc, unsigned char *argv[]) {
|
||||||
}
|
}
|
||||||
//Read additional length
|
//Read additional length
|
||||||
const unsigned long additionalLength = getUint16(compressedChunk + 26) + getUint16(compressedChunk + 28);
|
const unsigned long additionalLength = getUint16(compressedChunk + 26) + getUint16(compressedChunk + 28);
|
||||||
getBytes(compressedChunk, additionalLength);
|
if (additionalLength > 0UL) {
|
||||||
|
getBytes(NULL, additionalLength);
|
||||||
|
}
|
||||||
|
|
||||||
//If file is encrypted, skip 12-byte encryption header
|
//If file is encrypted, skip 12-byte encryption header
|
||||||
if (isEncrypted) {
|
if (state.isEncrypted) {
|
||||||
getBytes(compressedChunk, ENCRYPTION_HEADER_LENGTH);
|
getBytes(compressedChunk, ENCRYPTION_HEADER_LENGTH);
|
||||||
decrypt(compressedChunk, ENCRYPTION_HEADER_LENGTH);
|
decrypt(compressedChunk, ENCRYPTION_HEADER_LENGTH);
|
||||||
}
|
}
|
||||||
|
@ -89,7 +166,7 @@ int main(int argc, unsigned char *argv[]) {
|
||||||
inflateInit(compressedChunk, uncompressedChunk, BUFFER_SIZE);
|
inflateInit(compressedChunk, uncompressedChunk, BUFFER_SIZE);
|
||||||
|
|
||||||
//Read actual file
|
//Read actual file
|
||||||
unsigned long remainingBytes = fileLength;
|
unsigned long remainingBytes = state.fileSize;
|
||||||
bool needToRead = true;
|
bool needToRead = true;
|
||||||
bool hasReachedEnd = false;
|
bool hasReachedEnd = false;
|
||||||
unsigned long chunkSize;
|
unsigned long chunkSize;
|
||||||
|
@ -101,7 +178,7 @@ int main(int argc, unsigned char *argv[]) {
|
||||||
remainingBytes -= chunkSize;
|
remainingBytes -= chunkSize;
|
||||||
|
|
||||||
//Decrypt file if it is encrypted
|
//Decrypt file if it is encrypted
|
||||||
if (isEncrypted) {
|
if (state.isEncrypted) {
|
||||||
//TODO: For highest performance, we need to move if condition outside of while loop
|
//TODO: For highest performance, we need to move if condition outside of while loop
|
||||||
decrypt(compressedChunk, chunkSize);
|
decrypt(compressedChunk, chunkSize);
|
||||||
}
|
}
|
||||||
|
@ -115,6 +192,7 @@ int main(int argc, unsigned char *argv[]) {
|
||||||
|
|
||||||
//important: we must not modify uncompressedChunk since miniz may use it as dictionary and read from it during the next invocation of inflateInflate()
|
//important: we must not modify uncompressedChunk since miniz may use it as dictionary and read from it during the next invocation of inflateInflate()
|
||||||
|
|
||||||
|
//Write to stdout
|
||||||
write(1, uncompressedChunk + uncompressedPosition, inflateResult.numBytesWrittenToOutput);
|
write(1, uncompressedChunk + uncompressedPosition, inflateResult.numBytesWrittenToOutput);
|
||||||
uncompressedPosition += inflateResult.numBytesWrittenToOutput;
|
uncompressedPosition += inflateResult.numBytesWrittenToOutput;
|
||||||
while (uncompressedPosition >= BUFFER_SIZE) {
|
while (uncompressedPosition >= BUFFER_SIZE) {
|
||||||
|
@ -122,8 +200,9 @@ int main(int argc, unsigned char *argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Optionally perform xdelta3
|
//Optionally perform xdelta3
|
||||||
|
if (state.prevFile) {
|
||||||
//TODO
|
//TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//release memory
|
//release memory
|
||||||
|
|
|
@ -19,6 +19,19 @@ void readBytesIntoBuffer(uint8_t* buffer, long numBytes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Skips the given amount of bytes in the file
|
||||||
|
void skipBytes(long numBytes) {
|
||||||
|
const int result = fseek(file.filePointer, numBytes, SEEK_CUR);
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
|
fprintf(stderr, "Could not skip %lu bytes from file: %s\n", numBytes, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
} else {
|
||||||
|
file.offset += numBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Closes the currently opened file and opens the next file at its beginning.
|
//Closes the currently opened file and opens the next file at its beginning.
|
||||||
void openNextFile() {
|
void openNextFile() {
|
||||||
const int closeResult = fclose(file.filePointer);
|
const int closeResult = fclose(file.filePointer);
|
||||||
|
|
|
@ -17,5 +17,9 @@ struct FILE_INFO file;
|
||||||
void readBytesIntoBuffer(uint8_t* buffer, long numBytes);
|
void readBytesIntoBuffer(uint8_t* buffer, long numBytes);
|
||||||
|
|
||||||
|
|
||||||
|
//Skips the given amount of bytes in the file
|
||||||
|
void skipBytes(long numBytes);
|
||||||
|
|
||||||
|
|
||||||
//Closes the currently opened file and opens the next file at its beginning.
|
//Closes the currently opened file and opens the next file at its beginning.
|
||||||
void openNextFile();
|
void openNextFile();
|
||||||
|
|
Loading…
Reference in a new issue