1
0
mirror of synced 2024-11-23 22:40:58 +01:00

Additional console logging and minor cleanup

This commit is contained in:
samyuu 2021-05-27 09:22:21 +02:00
parent 1ed1a17d9a
commit ecfeea77b3
3 changed files with 75 additions and 42 deletions

View File

@ -1,11 +1,20 @@
#include "Types.h"
#include "Utilities.h"
namespace PeepoHappy
namespace TaikoSwitchDataTableDecryptor
{
namespace
{
// NOTE: Sucks for modders, makes sense for them to do it though...
constexpr size_t MaxDecompressedGameDataTableFileSize = 0x200000;
// NOTE: Extracted from "JP v1.4.3". I just hope it's at least the same for all versions, though not like it takes more than 5 minutes to find anyway...
constexpr std::array<u8, PeepoHappy::Crypto::Aes128KeySize> DataTableAesKey = { 0x57, 0x39, 0x73, 0x35, 0x38, 0x73, 0x68, 0x43, 0x54, 0x70, 0x76, 0x75, 0x6A, 0x6B, 0x4A, 0x74 };
}
bool HasValidGZipHeader(const u8* fileContent, size_t fileSize)
{
if (fileSize <= Crypto::AesKeySize)
if (constexpr size_t minHeaderSize = 9; fileSize <= minHeaderSize)
return false;
constexpr std::array<u8, 2> gzibMagic = { 0x1F, 0x8B };
@ -18,25 +27,31 @@ namespace PeepoHappy
bool DecompressAndWriteDataTableJsonFile(const u8* compressedData, size_t compressedDataSize, std::string_view jsonOutputFilePath)
{
auto decompressedBuffer = std::make_unique<u8[]>(IO::MaxDecompressedGameDataTableFileSize);
if (Compression::Inflate(compressedData, compressedDataSize, decompressedBuffer.get(), IO::MaxDecompressedGameDataTableFileSize))
auto decompressedBuffer = std::make_unique<u8[]>(MaxDecompressedGameDataTableFileSize);
if (!PeepoHappy::Compression::Inflate(compressedData, compressedDataSize, decompressedBuffer.get(), MaxDecompressedGameDataTableFileSize))
{
const size_t jsonLength = strnlen(reinterpret_cast<const char*>(decompressedBuffer.get()), IO::MaxDecompressedGameDataTableFileSize);
const auto jsonString = std::string_view(reinterpret_cast<const char*>(decompressedBuffer.get()), jsonLength);
if (IO::WriteEntireFile(jsonOutputFilePath, reinterpret_cast<const u8*>(jsonString.data()), jsonString.size()))
return true;
fprintf(stderr, "Failed to decompress input file");
return false;
}
return false;
const size_t jsonLength = strnlen(reinterpret_cast<const char*>(decompressedBuffer.get()), MaxDecompressedGameDataTableFileSize);
const auto jsonString = std::string_view(reinterpret_cast<const char*>(decompressedBuffer.get()), jsonLength);
if (!PeepoHappy::IO::WriteEntireFile(jsonOutputFilePath, reinterpret_cast<const u8*>(jsonString.data()), jsonString.size()))
{
fprintf(stderr, "Failed to write JSON output file");
return false;
}
return true;
}
int ReadAndWriteEncryptedAndOrCompressedBinToJsonFile(std::string_view encryptedAndOrCompressedInputFilePath, std::string_view jsonOutputFilePath)
{
const auto[fileContent, fileSize] = IO::ReadEntireFile(encryptedAndOrCompressedInputFilePath);
const auto[fileContent, fileSize] = PeepoHappy::IO::ReadEntireFile(encryptedAndOrCompressedInputFilePath);
if (fileSize <= 8)
{
fprintf(stderr, "Bad file? :WidePeepoSad:");
fprintf(stderr, "Unexpected end of file");
return EXIT_WIDEPEEPOSAD;
}
@ -47,14 +62,15 @@ namespace PeepoHappy
}
else
{
std::array<u8, Crypto::AesKeySize> iv = {};
std::array<u8, PeepoHappy::Crypto::Aes128KeySize> iv = {};
memcpy(iv.data(), fileContent.get(), iv.size());
const size_t fileSizeWithoutIV = (fileSize - iv.size());
const u8* fileContentWithoutIV = (fileContent.get() + iv.size());
auto decryptedBuffer = std::make_unique<u8[]>(fileSizeWithoutIV);
Crypto::DecryptAes128Cbc(fileContentWithoutIV, decryptedBuffer.get(), fileSizeWithoutIV, Crypto::DataTableAesKey, iv);
if (!PeepoHappy::Crypto::DecryptAes128Cbc(fileContentWithoutIV, decryptedBuffer.get(), fileSizeWithoutIV, DataTableAesKey, iv))
fprintf(stderr, "Failed to decrypt input file");
if (!DecompressAndWriteDataTableJsonFile(decryptedBuffer.get(), fileSizeWithoutIV, jsonOutputFilePath))
return EXIT_WIDEPEEPOSAD;
@ -65,43 +81,66 @@ namespace PeepoHappy
int ReadAndWriteJsonFileToCompressedBin(std::string_view jsonInputFilePath, std::string_view compressedOutputFilePath)
{
auto compressedBuffer = std::make_unique<u8[]>(IO::MaxDecompressedGameDataTableFileSize);
auto compressedBuffer = std::make_unique<u8[]>(MaxDecompressedGameDataTableFileSize);
const auto[fileContent, fileSize] = IO::ReadEntireFile(jsonInputFilePath);
const auto compressedSize = Compression::Deflate(fileContent.get(), fileSize, compressedBuffer.get(), IO::MaxDecompressedGameDataTableFileSize);
const auto[fileContent, fileSize] = PeepoHappy::IO::ReadEntireFile(jsonInputFilePath);
const auto compressedSize = PeepoHappy::Compression::Deflate(fileContent.get(), fileSize, compressedBuffer.get(), MaxDecompressedGameDataTableFileSize);
if (compressedSize < 0)
{
fprintf(stderr, "Failed to compress JSON file");
return EXIT_WIDEPEEPOSAD;
}
if (!IO::WriteEntireFile(compressedOutputFilePath, compressedBuffer.get(), compressedSize))
if (!PeepoHappy::IO::WriteEntireFile(compressedOutputFilePath, compressedBuffer.get(), compressedSize))
{
fprintf(stderr, "Failed to write compressed output file");
return EXIT_WIDEPEEPOSAD;
}
return EXIT_WIDEPEEPOHAPPY;
}
int EntryPoint()
{
const auto[argc, argv] = UTF8::GetCommandLineArguments();
const auto[argc, argv] = PeepoHappy::UTF8::GetCommandLineArguments();
if (argc <= 1)
{
fprintf(stderr, "Insufficient arguments :WidePeepoSad:\n");
printf("Description:\n");
printf(" A program to decrypt, decompress and recompress DataTable JSON files\n");
printf(" used by the Switch release of Taiko no Tatsujin (and possibly more)\n");
printf("\n");
printf("Usage:\n");
printf(" TaikoSwitchDataTableDecryptor.exe \"{input_datatable_file}.bin\"\n");
printf(" TaikoSwitchDataTableDecryptor.exe \"{input_datatable_file}.json\"\n");
printf("\n");
printf("Notes:\n");
printf(" Decompressed DataTable JSON input files mustn't be larger than ~2MB (0x200000 bytes)\n");
printf(" because of fixed size buffers used by the game during decompression.\n");
printf("\n");
printf("Credits:\n");
printf(" Programmed and reverse engineered by samyuu\n");
printf(" This program is licensed under the MIT License and makes use of the zlib library.\n");
printf(" The source code is available at " "https://github.com/samyuu/TaikoSwitchDataTableDecryptor" "\n");
printf("\n");
return EXIT_WIDEPEEPOSAD;
}
const auto inputPath = std::string_view(argv[1]);
if (IO::HasFileExtension(inputPath, ".bin"))
return ReadAndWriteEncryptedAndOrCompressedBinToJsonFile(inputPath, IO::ChangeFileExtension(inputPath, ".json"));
if (PeepoHappy::IO::HasFileExtension(inputPath, ".bin"))
return ReadAndWriteEncryptedAndOrCompressedBinToJsonFile(inputPath, PeepoHappy::IO::ChangeFileExtension(inputPath, ".json"));
if (IO::HasFileExtension(inputPath, ".json"))
return ReadAndWriteJsonFileToCompressedBin(inputPath, IO::ChangeFileExtension(inputPath, ".bin"));
// TODO: Actually.. it doesn't look like there are game encryption checks in place so this will have to re-encrypt too (?)
if (PeepoHappy::IO::HasFileExtension(inputPath, ".json"))
return ReadAndWriteJsonFileToCompressedBin(inputPath, PeepoHappy::IO::ChangeFileExtension(inputPath, ".bin"));
fprintf(stderr, "Unknown file extension\n");
fprintf(stderr, "Unexpected file extension\n");
return EXIT_WIDEPEEPOSAD;
}
}
int main()
{
return PeepoHappy::EntryPoint();
return TaikoSwitchDataTableDecryptor::EntryPoint();
}

View File

@ -172,7 +172,7 @@ namespace PeepoHappy
namespace Crypto
{
bool DecryptAes128Cbc(const u8* inEncryptedData, u8* outDecryptedData, size_t inOutDataSize, std::array<u8, AesKeySize> key, std::array<u8, AesKeySize> iv)
bool DecryptAes128Cbc(const u8* inEncryptedData, u8* outDecryptedData, size_t inOutDataSize, std::array<u8, Aes128KeySize> key, std::array<u8, Aes128KeySize> iv)
{
bool successful = false;
::NTSTATUS status = {};
@ -203,7 +203,7 @@ namespace PeepoHappy
}
else
{
fprintf(stderr, __FUNCTION__"(): BCryptDecrypt() failed with 0x%X", status);
fprintf(stderr, "BCryptDecrypt() failed with 0x%X", status);
}
if (symmetricKeyHandle)
@ -211,17 +211,17 @@ namespace PeepoHappy
}
else
{
fprintf(stderr, __FUNCTION__"(): BCryptGenerateSymmetricKey() failed with 0x%X", status);
fprintf(stderr, "BCryptGenerateSymmetricKey() failed with 0x%X", status);
}
}
else
{
fprintf(stderr, __FUNCTION__"(): BCryptGetProperty(BCRYPT_OBJECT_LENGTH) failed with 0x%X", status);
fprintf(stderr, "BCryptGetProperty(BCRYPT_OBJECT_LENGTH) failed with 0x%X", status);
}
}
else
{
fprintf(stderr, __FUNCTION__"(): BCryptSetProperty(BCRYPT_CHAINING_MODE) failed with 0x%X", status);
fprintf(stderr, "BCryptSetProperty(BCRYPT_CHAINING_MODE) failed with 0x%X", status);
}
if (algorithmHandle)
@ -229,7 +229,7 @@ namespace PeepoHappy
}
else
{
fprintf(stderr, __FUNCTION__"(): BCryptOpenAlgorithmProvider(BCRYPT_AES_ALGORITHM) failed with 0x%X", status);
fprintf(stderr, "BCryptOpenAlgorithmProvider(BCRYPT_AES_ALGORITHM) failed with 0x%X", status);
}
return successful;

View File

@ -1,6 +1,8 @@
#pragma once
#include "Types.h"
// NOTE: In case anyone is wondering... no, there is no particular reason for these names.
// I just like Peepo and it cheers me up after looking at code all day :WidePeepoHappy:
#define EXIT_WIDEPEEPOHAPPY EXIT_SUCCESS
#define EXIT_WIDEPEEPOSAD EXIT_FAILURE
@ -35,9 +37,6 @@ namespace PeepoHappy
namespace IO
{
// NOTE: Sucks for modders, makes sense for them to do it though...
constexpr size_t MaxDecompressedGameDataTableFileSize = 0x200000;
std::pair<std::unique_ptr<u8[]>, size_t> ReadEntireFile(std::string_view filePath);
bool WriteEntireFile(std::string_view filePath, const u8* fileContent, size_t fileSize);
@ -47,13 +46,8 @@ namespace PeepoHappy
namespace Crypto
{
constexpr size_t AesKeySize = 16;
// NOTE: Literally loaded directly into X8 right before calling nn::crypto::DecryptAes128Cbc()
// they couldn't even bother trying to "hide" it by adding a few pointer indirection or scrambling first :KEKL:
constexpr std::array<u8, AesKeySize> DataTableAesKey = { 0x57, 0x39, 0x73, 0x35, 0x38, 0x73, 0x68, 0x43, 0x54, 0x70, 0x76, 0x75, 0x6A, 0x6B, 0x4A, 0x74, };
bool DecryptAes128Cbc(const u8* inEncryptedData, u8* outDecryptedData, size_t inOutDataSize, std::array<u8, AesKeySize> key, std::array<u8, AesKeySize> iv);
constexpr size_t Aes128KeySize = 16;
bool DecryptAes128Cbc(const u8* inEncryptedData, u8* outDecryptedData, size_t inOutDataSize, std::array<u8, Aes128KeySize> key, std::array<u8, Aes128KeySize> iv);
}
namespace Compression