ssn-installer/src/main.c

132 lines
4.1 KiB
C
Raw Normal View History

2018-07-17 14:05:22 +02:00
#include <errno.h>
2018-07-24 19:56:21 +02:00
#include <stdbool.h>
#include <stdio.h>
2018-07-24 19:56:21 +02:00
#include <stdlib.h>
2018-07-17 14:05:22 +02:00
#include <string.h>
2018-07-31 13:18:18 +02:00
#include <unistd.h>
2018-07-17 14:05:22 +02:00
//Import our code
#include "decrypt.h"
2018-07-17 17:19:13 +02:00
#include "fileReader.h"
2018-07-31 13:18:18 +02:00
#include "inflate.h"
2018-07-24 18:10:07 +02:00
#include "utils/min.h"
2018-07-17 14:05:22 +02:00
#define BUFFER_SIZE 32UL * 1024UL //512 KiB
2018-07-24 19:56:21 +02:00
#define ENCRYPTION_HEADER_LENGTH 12UL
2018-07-25 02:34:08 +02:00
#define LOCAL_FILE_HEADER_MAGIC (uint32_t)0x04034b50
2018-07-24 19:56:21 +02:00
uint16_t getUint16(uint8_t* buffer) {
2018-07-24 19:56:21 +02:00
return (uint16_t)buffer[0] | \
(uint16_t)buffer[1] << 8;
}
uint32_t getUint32(uint8_t* buffer) {
2018-07-24 19:56:21 +02:00
return (uint32_t)buffer[0] | \
(uint32_t)buffer[1] << 8 | \
(uint32_t)buffer[2] << 16 | \
(uint32_t)buffer[3] << 24;
}
2018-07-17 14:05:22 +02:00
int main(int argc, unsigned char *argv[]) {
2018-07-24 19:56:21 +02:00
if (argc != 4 && argc != 7) {
2018-07-25 02:34:08 +02:00
fprintf(stderr, "Wrong number of arguments. Usage: patcher-installer <disk_name> <disk_offset> <file_size> [<key0> <key1> <key2>]");
2018-07-24 19:56:21 +02:00
exit(1);
}
2018-07-24 18:10:07 +02:00
//TODO: verify argv and assign it to variables
2018-07-24 19:56:21 +02:00
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 key1 = atoi(argv[5]);
uint32_t key2 = atoi(argv[6]);
2018-07-25 02:34:08 +02:00
//Initialize decryption (pass decryption keys)
2018-07-24 19:56:21 +02:00
initDecryptor(key0, key1, key2);
}
//-------------------------------------------------
2018-07-17 14:05:22 +02:00
uint8_t* compressedChunk = malloc(BUFFER_SIZE);
2018-07-31 13:18:18 +02:00
if (compressedChunk == NULL) {
fprintf(stderr, "Could not allocate %lu bytes for compressed buffer.\n", BUFFER_SIZE);
exit(1);
}
//memset(compressedChunk, (uint8_t)0, BUFFER_SIZE);
2018-07-31 13:18:18 +02:00
uint8_t* uncompressedChunk = malloc(BUFFER_SIZE);
2018-07-31 13:18:18 +02:00
if (uncompressedChunk == NULL) {
fprintf(stderr, "Could not allocate %lu bytes for uncompressed buffer.\n", BUFFER_SIZE);
exit(1);
}
memset(uncompressedChunk, (uint8_t)0, BUFFER_SIZE);
2018-07-31 13:18:18 +02:00
//-------------------------------------------------
2018-07-25 02:34:08 +02:00
//Initialize file reader
2018-07-24 18:17:15 +02:00
initFileReader(archiveName, archiveOffset);
2018-07-17 15:05:57 +02:00
2018-07-24 19:56:21 +02:00
//Skip local file header (30 bytes + additional length)
2018-07-31 13:18:18 +02:00
getBytes(compressedChunk, 30UL);
2018-07-24 19:56:21 +02:00
//Check that header is correct
2018-07-31 13:18:18 +02:00
const uint32_t magic = getUint32(compressedChunk);
2018-07-25 02:34:08 +02:00
if (magic != LOCAL_FILE_HEADER_MAGIC) {
fprintf(stderr, "Wrong magic in local file header, expected %#010x but found %#010x.", LOCAL_FILE_HEADER_MAGIC, magic);
2018-07-24 19:56:21 +02:00
exit(1);
}
//Read additional length
2018-07-31 13:18:18 +02:00
const unsigned long additionalLength = getUint16(compressedChunk + 26) + getUint16(compressedChunk + 28);
getBytes(compressedChunk, additionalLength);
2018-07-17 15:05:57 +02:00
2018-07-24 19:56:21 +02:00
//If file is encrypted, skip 12-byte encryption header
if (isEncrypted) {
2018-07-31 13:18:18 +02:00
getBytes(compressedChunk, ENCRYPTION_HEADER_LENGTH);
decrypt(compressedChunk, ENCRYPTION_HEADER_LENGTH);
2018-07-24 19:56:21 +02:00
}
2018-07-17 15:05:57 +02:00
2018-07-24 19:56:21 +02:00
//-------------------------------------------------
2018-07-17 15:05:57 +02:00
2018-07-31 13:18:18 +02:00
struct InflateOutput inflateResult;
inflateInit(compressedChunk, uncompressedChunk, BUFFER_SIZE);
2018-07-24 18:10:07 +02:00
//Read actual file
2018-07-24 18:17:15 +02:00
unsigned long remainingBytes = fileLength;
2018-07-31 13:18:18 +02:00
bool needToRead = true;
bool hasReachedEnd = false;
2018-07-31 13:18:18 +02:00
unsigned long chunkSize;
while (remainingBytes > 0 || !hasReachedEnd) {
2018-07-31 13:18:18 +02:00
if (needToRead) {
chunkSize = min(BUFFER_SIZE, remainingBytes);
//memset(compressedChunk, 0, chunkSize);
2018-07-31 13:18:18 +02:00
getBytes(compressedChunk, chunkSize);
remainingBytes -= chunkSize;
//Decrypt file if it is encrypted
if (isEncrypted) {
//TODO: For highest performance, we need to move if condition outside of while loop
decrypt(compressedChunk, chunkSize);
}
2018-07-24 19:56:21 +02:00
}
2018-07-24 18:10:07 +02:00
//Decompress file
2018-07-31 13:18:18 +02:00
//bytes are contained in uncompressedChunk from [0, inflateResult.numBytesWrittenToOutput - 1]
inflateResult = inflateInflate(needToRead ? chunkSize : 0, remainingBytes > 0);
needToRead = inflateResult.needMoreInput;
hasReachedEnd = inflateResult.hasReachedEnd;
2018-07-31 13:18:18 +02:00
//important: we must not modify uncompressedChunk since miniz may use it as dictionary and read from it during the next invocation of inflateInflate()
2018-07-31 13:18:18 +02:00
write(1, uncompressedChunk, inflateResult.numBytesWrittenToOutput);
2018-07-24 18:10:07 +02:00
//Optionally perform xdelta3
//TODO
2018-07-24 19:56:21 +02:00
2018-07-24 18:10:07 +02:00
}
2018-07-17 15:05:57 +02:00
2018-07-31 13:18:18 +02:00
//release memory
free(compressedChunk);
free(uncompressedChunk);
2018-07-17 14:05:22 +02:00
return 0;
}