Merge pull request #22 from PikalaxALT/mod123_decry

This commit is contained in:
Thomas 2021-10-18 16:42:36 -04:00 committed by GitHub
commit 9207816fb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2718 additions and 1701 deletions

File diff suppressed because it is too large Load Diff

View File

@ -126,7 +126,7 @@ basefile=${MYDIR}/.bins/${baserom}${basestem}.sbin
[[ -f $basefile ]] || {
dd if="$baserom" of="$basefile" bs=1 skip="$fileoff" count="$size" 2>/dev/null
[[ $proc == armv5te ]] && {
_start_ModuleParams=$(python $MYDIR/find_module_params.py ${basefile})
_start_ModuleParams=$(getword "$baserom" $((fileoff+size+4)))
compstatend=$(getword "$basefile" $((_start_ModuleParams+20)))
[[ $compstatend != "0" ]] && {
$MYDIR/ntruncompbw $basefile $vma $compstatend || { rm -f $basefile; exit 1; }

View File

@ -0,0 +1,63 @@
#include "CryptoRc4.h"
u8 CryptoRC4Context::GetEncodedByte() {
i = (++i) & 0xFF;
u8 x = s[i];
j = (j + x) & 0xFF;
u8 y = s[j];
s[j] = x;
s[i] = y;
return s[(x + y) & 0xFF];
}
void CryptoRC4Context::Decrypt(u32 *start, u32 *end) {
// assert (!(size & 3));
u8 buffer[256];
for (int _i = 0; _i < 256; _i++) {
buffer[_i] = _i ^ 1;
}
for (; start < end; start++) {
u32 &word = *start;
switch (GetInsnType(word)) {
case 1:
case 2:
word = (((word & ~0xFF000000) - 0x1300) & ~0xFF000000) | ((word & 0xFF000000) ^ 0x01000000);
break;
case 3:
word = (((word & ~0xFF000000) - 0x4C2) & ~0xFF000000) | ((word & 0xFF000000) ^ 0x01000000);
break;
default:
u8 *bytes = (u8 *)&word;
bytes[0] ^= GetEncodedByte();
bytes[1] ^= GetEncodedByte();
bytes[2] = buffer[bytes[2]];
break;
}
}
}
void CryptoRC4Context::Encrypt(u32 *start, u32 *end) {
// assert (!(size & 3));
u8 buffer[256];
for (int _i = 0; _i < 256; _i++) {
buffer[_i] = _i ^ 1;
}
for (; start < end; start++) {
u32 &word = *start;
switch (GetInsnType(word)) {
case 1:
case 2:
word = (((word & ~0xFF000000) + 0x4C2) & ~0xFF000000) | ((word & 0xFF000000) ^ 0x01000000);
break;
case 3:
word = (((word & ~0xFF000000) + 0x1300) & ~0xFF000000) | ((word & 0xFF000000) ^ 0x01000000);
break;
default:
u8 *bytes = (u8 *)&word;
bytes[0] ^= GetEncodedByte();
bytes[1] ^= GetEncodedByte();
bytes[2] = buffer[bytes[2]];
break;
}
}
}

View File

@ -0,0 +1,52 @@
#ifndef GUARD_CRYPTORC4_H
#define GUARD_CRYPTORC4_H
#include "ntrtypes.h"
static inline int GetInsnType(u32 value) {
u8 highByte = (value >> 24) & 0xFF;
if ((highByte & 0xE) == 0xA) {
if ((highByte & 0xF0) == 0xF0)
return 1;
else if (highByte & 1)
return 2;
else
return 3;
}
return 0;
}
class CryptoRC4Context {
u32 i;
u32 j;
u8 s[256];
u8 GetEncodedByte();
public:
CryptoRC4Context() {
i = 0;
j = 0;
for (int _i = 0; _i < 256; _i++) {
s[_i] = _i;
}
}
CryptoRC4Context(const u8 *keys) : CryptoRC4Context() {
int key_i = 0;
int s_i = 0;
for (int s_j = 255; s_j >= 0; s_j--) {
u8 s_k = s[s_j];
s_i = (s_i + keys[key_i++] + s_k) & 0xFF;
u8 s_l = s[s_i];
if (key_i >= 16)
key_i = 0;
s[s_i] = s_k;
s[s_j] = s_l;
}
}
void Decrypt(u32 *start, u32 *end);
void Encrypt(u32 *start, u32 *end);
};
#endif //GUARD_CRYPTORC4_H

View File

@ -0,0 +1,126 @@
#include <cstring>
#include "Decrypt.h"
#include "CryptoRc4.h"
u32 Decryptor::FindDecryLvl2(u32 offset) {
offset = (offset + 3) & ~3; // round up
static const u8 pattern1[] = {
0xf0, 0x00, 0x2d, 0xe9, 0x0f, 0x00, 0x2d, 0xe9, 0xf0, 0x00, 0xbd, 0xe8, 0x60, 0x10, 0x9f, 0xe5,
0x50, 0x30, 0x8f, 0xe2, 0x00, 0xe0, 0x83, 0xe5, 0x4c, 0x20, 0x9f, 0xe5, 0x4c, 0x00, 0x9f, 0xe5,
}; // size=32
static const u8 pattern2[] = {
0x00, 0xc0, 0xa0, 0xe1, 0xf0, 0x00, 0x2d, 0xe9, 0x0f, 0x00, 0xbd, 0xe8,
0xf0, 0x00, 0xbd, 0xe8, 0x3c, 0xff, 0x2f, 0xe1, 0x10, 0x00, 0x2d, 0xe9, 0x00, 0x40, 0xa0, 0xe1,
0x2c, 0x10, 0x9f, 0xe5, 0x20, 0x20, 0x9f, 0xe5, 0x20, 0x00, 0x9f, 0xe5,
}; // size=40
static const u8 pattern3[] = {
0x18, 0x00, 0x8f, 0xe5, 0x04, 0x00, 0xa0, 0xe1, 0x10, 0x00, 0xbd, 0xe8, 0x04, 0xe0, 0x9f, 0xe5,
0x00, 0xf0, 0x8f, 0xe5, 0x1e, 0xff, 0x2f, 0xe1
}; // size=24
for (int i = offset; i < data.size() - sizeof(pattern1) - sizeof(pattern2) - sizeof(pattern3) - 8; i += 4) {
if (memcmp(&data[i], pattern1, sizeof(pattern1)) == 0
&& memcmp(&data[i + sizeof(pattern1) + 4], pattern2, sizeof(pattern2)) == 0
&& memcmp(&data[i + sizeof(pattern1) + sizeof(pattern2) + 8], pattern3, sizeof(pattern3)) == 0) {
return i;
}
}
return data.size();
}
u32 Decryptor::DoDecryptLvl2(u32 tableOffset) {
u32 *pool = (u32 *)&data[tableOffset + 104];
pool[1] -= info.start + info.size + 0x1300;
pool[2] -= info.start + info.size + 0x1300;
pool[3] -= 0x1300;
u32 param = pool[2];
u32 start = pool[3];
u32 size = pool[1];
u32 keys[4] = {
size ^ param,
size ^ ((param << 8) | (param >> 24)),
size ^ ((param << 16) | (param >> 16)),
size ^ ((param << 24) | (param >> 8)),
};
CryptoRC4Context buffer((const u8 *)keys);
buffer.Decrypt((u32 *) &data[start - info.start], (u32 *) &data[start + size - info.start]);
return tableOffset + 120;
}
void Decryptor::DecryptLvl2() {
u32 i = 0;
while ((i = FindDecryLvl2(i)) != data.size()) {
i = DoDecryptLvl2(i);
}
}
u32 Decryptor::DoDecryptLvl1(u32 tableOffset) {
FATEntry *table;
FATEntry *table_start = (FATEntry *)(&data[tableOffset]);
for (table = table_start; table->start != 0 && table->end != 0; table++) {
table->start -= 0x1300;
table->end -= info.start + info.size + 0x1300;
u32 start_offs = table->start - info.start;
u32 size = table->end & ~3;
for (int i = start_offs; i < start_offs + size; i += 4) {
u32 & word = (u32 &)data[i];
switch (GetInsnType(word)) {
case 1:
case 2:
word = (((word & ~0xFF000000) - 0x1300) & ~0xFF000000) | ((word & 0xFF000000) ^ 0x01000000);
break;
case 3:
word = (((word & ~0xFF000000) - 0x4C2) & ~0xFF000000) | ((word & 0xFF000000) ^ 0x01000000);
break;
default:
u8 *ptr = (u8 *)&word;
word = ((ptr[0] ^ 0x56) << 0) | ((ptr[1] ^ 0x65) << 8) | ((ptr[2] ^ 0x56) << 16) | ((ptr[3] ^ 0xF0) << 24);
break;
}
}
}
return (u8 *)table - (u8 *)table_start + tableOffset + sizeof(*table);
}
void Decryptor::DecryptLvl1() {
for (int i = info.sinit_start; i != info.sinit_end; i += 4) {
if (*(u32 *)&data[i - info.start] != 0) {
(void)DoDecryptLvl1(*(u32 *)&data[i - info.start] - info.start + 16);
}
}
}
void Decryptor::Decrypt() {
DecryptLvl1();
DecryptLvl2();
}
void Decryptor::Write(std::ofstream &outfile) {
outfile.write((char *)data.data(), data.size());
}
DecryptOptions::DecryptOptions(int argc, char ** argv) : Options(argc, argv) {
if (argc < 5) {
throw std::invalid_argument("missing required argument: " +
((std::string[]) {"", "mode", "baserom", "outfile", "ovy_id"})[argc]);
}
baserom = new NtrRom(argv[2], std::ios::binary);
outfile = std::ofstream(argv[3], std::ios::binary);
if (!outfile.good()) {
throw std::runtime_error(std::string("unable to open file '") + argv[3] + "' for reading");
}
// Translate module number
ovy_id = std::strtoul(argv[4], nullptr, 10);
}
DecryptOptions::~DecryptOptions() {
delete baserom;
}
int DecryptOptions::main() {
Decryptor decryptor(baserom, ovy_id);
decryptor.Decrypt();
decryptor.Write(outfile);
return 0;
}

View File

@ -0,0 +1,66 @@
#ifndef GUARD_DECRYPT_H
#define GUARD_DECRYPT_H
#include "NtrRom.h"
#include "Options.h"
struct DecryptPart2 {
u32 i;
u32 j;
u8 s[256];
const u8 *keys = nullptr;
DecryptPart2() {
i = 0;
j = 0;
for (int _i = 0; _i < 256; _i++) {
s[_i] = _i;
}
}
DecryptPart2(const u8 *_keys) : DecryptPart2() {
keys = _keys;
int r6 = 0;
int r7 = 0;
for (int _i = 255; _i >= 0; _i--) {
u8 r4 = s[_i];
r7 = (r7 + keys[r6++] + r4) & 0xFF;
u8 ip = s[r7];
if (r6 >= 16)
r6 = 0;
s[r7] = r4;
s[_i] = ip;
}
}
u8 GetEncodedByte();
void DoDecrypt(u32 *start, u32 *end);
};
class Decryptor : public NtrOverlay {
protected:
u32 FindDecryLvl2(u32 offset);
u32 DoDecryptLvl1(u32 tableOffset);
u32 DoDecryptLvl2(u32 tableOffset);
void DecryptLvl1();
void DecryptLvl2();
public:
Decryptor() = default;
Decryptor(FSOverlayInfo &_info, std::vector<u8> &_data) : NtrOverlay(_info, _data) {}
Decryptor(NtrOverlay &_overlay) : NtrOverlay(_overlay) {}
Decryptor(NtrRom *baserom, u32 ovy_id) : NtrOverlay(baserom, ovy_id) {}
void Decrypt();
void Write(std::ofstream &outfile);
};
struct DecryptOptions: public Options {
NtrRom *baserom;
std::ofstream outfile;
std::uint32_t ovy_id;
DecryptOptions(int argc, char ** argv);
~DecryptOptions();
int main();
};
int decrypt_main(DecryptOptions &options);
#endif //GUARD_DECRYPT_H

View File

@ -0,0 +1,124 @@
#include <iostream>
#include "Encrypt.h"
#include "CryptoRc4.h"
Encryptor::Encryptor(std::string &buildname, u32 ovy_id) {
std::filesystem::path table_path(buildname + "_table.sbin");
std::ifstream table(table_path, std::ios::binary);
if (!table.good()) {
throw std::runtime_error("unable to open " + buildname + "_table.sbin for reading");
}
table.seekg(ovy_id * sizeof(FSOverlayInfo));
table.read((char *)&info, sizeof(info));
std::ifstream defs(buildname + "_defs.sbin", std::ios::binary);
if (!defs.good()) {
throw std::runtime_error("unable to open " + buildname + "_defs.sbin for reading");
}
defs.seekg(16);
std::string filename;
for (int i = 0; i < ovy_id; i++) {
std::getline(defs, filename, '\0');
}
std::getline(defs, filename, '\0');
std::filesystem::path ovyfname = table_path.replace_filename(filename);
std::ifstream ovyfile(ovyfname, std::ios::binary | std::ios::ate);
if (!ovyfile.good()) {
throw std::runtime_error("unable to open " + ovyfname.string() + " for reading");
}
u32 size = ovyfile.tellg();
data.resize(size);
ovyfile.seekg(0);
ovyfile.read((char *)data.data(), size);
}
u32 Encryptor::DoEncryptLvl2(u32 tableOffset) {
u32 *pool = (u32 *)&data[tableOffset + 104];
u32 param = pool[2];
u32 start = pool[3];
u32 size = pool[1];
pool[1] += info.start + info.size + 0x1300;
pool[2] += info.start + info.size + 0x1300;
pool[3] += 0x1300;
u32 keys[4] = {
size ^ param,
size ^ ((param << 8) | (param >> 24)),
size ^ ((param << 16) | (param >> 16)),
size ^ ((param << 24) | (param >> 8)),
};
CryptoRC4Context buffer((const u8 *)keys);
buffer.Encrypt((u32 *) &data[start - info.start], (u32 *) &data[start + size - info.start]);
return tableOffset + 120;
}
void Encryptor::EncryptLvl2() {
u32 i = 0;
while ((i = FindDecryLvl2(i)) != data.size()) {
i = DoEncryptLvl2(i);
}
}
u32 Encryptor::DoEncryptLvl1(u32 tableOffset) {
FATEntry *table;
FATEntry *table_start = (FATEntry *)(&data[tableOffset]);
for (table = table_start; table->start != 0 && table->end != 0; table++) {
u32 start_offs = table->start - info.start;
u32 size = table->end & ~3;
table->start += 0x1300;
table->end += info.start + info.size + 0x1300;
for (int i = start_offs; i < start_offs + size; i += 4) {
u32 & word = (u32 &)data[i];
switch (GetInsnType(word)) {
case 1:
case 2:
word = (((word & ~0xFF000000) + 0x1300) & ~0xFF000000) | ((word & 0xFF000000) ^ 0x01000000);
break;
case 3:
word = (((word & ~0xFF000000) + 0x4C2) & ~0xFF000000) | ((word & 0xFF000000) ^ 0x01000000);
break;
default:
u8 *ptr = (u8 *)&word;
word = ((ptr[0] ^ 0x56) << 0) | ((ptr[1] ^ 0x65) << 8) | ((ptr[2] ^ 0x56) << 16) | ((ptr[3] ^ 0xF0) << 24);
break;
}
}
}
return (u8 *)table - (u8 *)table_start + tableOffset + sizeof(*table);
}
void Encryptor::EncryptLvl1() {
for (int i = info.sinit_start; i != info.sinit_end; i += 4) {
if (*(u32 *)&data[i - info.start] != 0) {
(void)DoEncryptLvl1(*(u32 *)&data[i - info.start] - info.start + 16);
}
}
}
void Encryptor::Encrypt() {
EncryptLvl2();
EncryptLvl1();
}
EncryptOptions::EncryptOptions(int argc, char ** argv) : Options(argc, argv) {
if (argc < 5) {
throw std::invalid_argument("missing required argument: " +
((std::string[]) {"", "mode", "buildname", "outfile", "ovy_id"})[argc]);
}
buildname = argv[2];
outfile = std::ofstream(argv[3], std::ios::binary);
if (!outfile.good()) {
throw std::runtime_error(std::string("unable to open file '") + argv[3] + "' for reading");
}
// Translate module number
ovy_id = std::strtoul(argv[4], nullptr, 10);
}
int EncryptOptions::main() {
Encryptor encryptor(buildname, ovy_id);
encryptor.Encrypt();
encryptor.Write(outfile);
return 0;
}

View File

@ -0,0 +1,33 @@
#ifndef GUARD_ENCRYPT_H
#define GUARD_ENCRYPT_H
#include <fstream>
#include <string>
#include <filesystem>
#include "ntrtypes.h"
#include "Decrypt.h"
class Encryptor : public Decryptor {
u32 FindEncryLvl2(u32 offset) { return FindDecryLvl2(offset); }
u32 DoEncryptLvl1(u32 tableOffset);
u32 DoEncryptLvl2(u32 tableOffset);
void EncryptLvl1();
void EncryptLvl2();
public:
Encryptor(std::string &buildname, u32 ovy_id);
Encryptor(FSOverlayInfo &_info, std::vector<u8> &_data) : Decryptor(_info, _data) {}
void Encrypt();
};
struct EncryptOptions : public Options {
std::string buildname;
std::ofstream outfile;
u32 ovy_id;
EncryptOptions(int argc, char ** argv);
~EncryptOptions() = default;
int main(void);
};
#endif //GUARD_ENCRYPT_H

View File

@ -0,0 +1,49 @@
DEBUG ?= 1
CC ?= gcc
CXX ?= g++
ifneq ($(DEBUG),1)
OPTFLAGS += -O3
DEFINES += -DNDEBUG
endif
CFLAGS := $(OPTFLAGS) $(DEFINES) -g
CXXFLAGS := $(OPTFLAGS) $(DEFINES) -g -std=c++17
LDFLAGS :=
DEPDIR := .deps
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
PROGRAM := mod123encry
CXXSRCS := mod123encry.cpp NtrRom.cpp Decrypt.cpp Encrypt.cpp CryptoRc4.cpp Overlay.cpp
CXXOBJS := $(CXXSRCS:%.cpp=%.o)
CSRCS := ntruncompbw.c
COBJS := $(CSRCS:%.c=%.o)
SRCS := $(CXXSRCS) $(CSRCS)
OBJS := $(CXXOBJS) $(COBJS)
.PHONY: all clean
all: $(PROGRAM)
@:
$(PROGRAM): $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $^
clean:
$(RM) $(PROGRAM) $(OBJS)
$(RM) -r $(DEPDIR)
%.o: %.cpp
%.o: %.cpp $(DEPDIR)/%.d | $(DEPDIR)
$(CXX) $(CXXFLAGS) $(DEPFLAGS) -c -o $@ $<
%.o: %.c
%.o: %.c $(DEPDIR)/%.d | $(DEPDIR)
$(CC) $(CFLAGS) $(DEPFLAGS) -c -o $@ $<
$(DEPDIR): ; @mkdir -p $@
DEPFILES := $(CXXSRCS:%.cpp=$(DEPDIR)/%.d) $(CSRCS:%.c=$(DEPDIR)/%.d)
$(DEPFILES):
include $(wildcard $(DEPFILES))

View File

@ -0,0 +1,82 @@
#include <algorithm>
#include <cstring>
#include <iostream>
#include "NtrRom.h"
#include "ntruncompbw.h"
NtrRom::NtrRom(const char *filename, std::ios::openmode mode) : handle(filename, mode | std::ios::ate) {
size_t romsize = handle.tellg();
raw = new u8[romsize];
handle.seekg(0);
handle.read((char *)raw, romsize);
const RomHeader *header = getHeader();
arm9_static = raw + header->main_rom_offset;
arm7_static = raw + header->sub_rom_offset;
// ARM9 might be compressed
u32 *_start_ModuleParams = (u32 *)(arm9_static + *(u32 *)(arm9_static + header->main_size + 4));
if (_start_ModuleParams[5] != 0) {
arm9_static = new u8[header->main_size];
memcpy(arm9_static, raw + header->main_rom_offset, header->main_size);
arm9_static_uncomp_size = MIi_UncompressBackwards((u8 **)&arm9_static, header->main_size);
}
// ARM7 static is never compressed
// Set FAT
AssignRomVector(fat, header->fat.offset, header->fat.length);
// Set FNT
fnt.raw = raw + header->fnt.offset;
AssignRomVector(fnt.directories, header->fnt.offset, sizeof(FNTHeader));
AssignRomVector(fnt.directories, header->fnt.offset, fnt.directories[0].parent * sizeof(FNTHeader));
fnt.paths = (char *)&*fnt.directories.cend();
// Set overlay tables
AssignRomVector(arm9_ovt, header->main_ovt.offset, header->main_ovt.length);
arm9_ovy = new u8*[arm9_ovt.size()];
for (int i = 0; i < arm9_ovt.size(); i++) {
if (arm9_ovt[i].flag & 1) {
arm9_ovy[i] = new u8[arm9_ovt[i].compsize];
memcpy(arm9_ovy[i], raw + fat[arm9_ovt[i].file_id].start, arm9_ovt[i].compsize);
MIi_UncompressBackwards((u8 **)&arm9_ovy[i], arm9_ovt[i].compsize);
} else {
arm9_ovy[i] = raw + fat[arm9_ovt[i].file_id].start;
}
}
AssignRomVector(arm7_ovt, header->main_ovt.offset, header->main_ovt.length);
arm7_ovy = new u8*[arm7_ovt.size()];
for (int i = 0; i < arm7_ovt.size(); i++) {
if (arm7_ovt[i].flag & 1) {
arm7_ovy[i] = new u8[arm7_ovt[i].compsize];
memcpy(arm7_ovy[i], raw + fat[arm7_ovt[i].file_id].start, arm7_ovt[i].compsize);
MIi_UncompressBackwards((u8 **)&arm7_ovy[i], arm7_ovt[i].compsize);
} else {
arm7_ovy[i] = raw + fat[arm7_ovt[i].file_id].start;
}
}
banner = (NtrBanner *)(raw + header->banner_offset);
}
NtrRom::~NtrRom() {
int i;
for (i = 0; i < arm9_ovt.size(); i++) {
if (arm9_ovt[i].flag & 1) {
delete[] arm9_ovy[i];
}
}
for (i = 0; i < arm7_ovt.size(); i++) {
if (arm7_ovt[i].flag & 1) {
delete[] arm7_ovy[i];
}
}
delete[] arm9_ovy;
delete[] arm7_ovy;
// explicit delete for static only if uncomped
if (arm9_static_uncomp_size != 0) {
delete[] arm9_static;
}
delete[] raw;
}

119
tools/mod123encry/NtrRom.h Normal file
View File

@ -0,0 +1,119 @@
#ifndef GUARD_NTRROM_H
#define GUARD_NTRROM_H
#include <fstream>
#include <vector>
#include "ntrtypes.h"
#include "Overlay.h"
struct CARDRomRegion {
u32 offset;
u32 length;
};
struct RomHeader {
char game_name[12];
u32 game_code;
u16 maker_code;
u8 product_id;
u8 device_type;
u8 device_size;
u8 reserved_A[9];
u8 game_version;
u8 property;
u32 main_rom_offset;
u32 main_entry_address;
u32 main_ram_address;
u32 main_size;
u32 sub_rom_offset;
u32 sub_entry_address;
u32 sub_ram_address;
u32 sub_size;
CARDRomRegion fnt;
CARDRomRegion fat;
CARDRomRegion main_ovt;
CARDRomRegion sub_ovt;
u8 rom_param_A[8];
u32 banner_offset;
u16 secure_crc;
u8 rom_param_B[2];
u32 main_autoload_done;
u32 sub_autoload_done;
u8 rom_param_C[8];
u32 rom_size;
u32 header_size;
u8 reserved_B[0x38];
u8 logo_data[0x9C];
u16 logo_crc;
u16 header_crc;
};
struct NtrBanner {
u16 reserved[16];
u16 pixels[0x100];
u16 palette[16];
char16_t titles[6][128];
};
struct FATEntry {
u32 start;
u32 end;
};
struct FNTHeader {
u32 start;
u16 index;
u16 parent;
};
struct FNT {
u8 *raw;
std::vector<FNTHeader> directories;
char *paths;
};
class NtrRom {
std::ifstream handle;
u8 * raw;
u8 * arm9_static;
u8 * arm7_static;
u32 arm9_static_uncomp_size = 0;
std::vector<FSOverlayInfo> arm9_ovt;
std::vector<FSOverlayInfo> arm7_ovt;
std::vector<FATEntry> fat;
FNT fnt;
u8 ** arm9_ovy;
u8 ** arm7_ovy;
NtrBanner *banner;
template <typename T> void AssignRomVector(std::vector<T> &dest, u32 address, u32 size) {
if (size % sizeof(T) != 0) {
throw std::runtime_error("requested size not a multiple of type size");
}
dest.assign((T *)(raw + address), (T *)(raw + address + size));
}
public:
NtrRom(const char * filename, std::ios::openmode mode = std::ios::in);
~NtrRom();
const RomHeader * getHeader() { return (const RomHeader *)(raw + 0); }
FSOverlayInfo &getOverlayInfo(u32 proc, u32 ovy_id) {
std::vector<FSOverlayInfo> &ovyi = (proc == 0) ? arm9_ovt : arm7_ovt;
return ovyi[ovy_id];
}
std::vector<u8> getOverlayData(u32 proc, u32 ovy_id) {
u8 *& data_raw = ((proc == 0) ? arm9_ovy : arm7_ovy)[ovy_id];
FSOverlayInfo &info = getOverlayInfo(proc, ovy_id);
return std::vector<u8>(data_raw, data_raw + info.size);
}
NtrOverlay getOverlay(u32 proc, u32 ovy_id) {
std::vector<FSOverlayInfo> &ovyi = (proc == 0) ? arm9_ovt : arm7_ovt;
u8 *& data_raw = ((proc == 0) ? arm9_ovy : arm7_ovy)[ovy_id];
FSOverlayInfo &info = ovyi[ovy_id];
std::vector<u8> data(data_raw, data_raw + info.size);
return {info, data};
}
};
#endif //GUARD_NTRROM_H

View File

@ -0,0 +1,30 @@
#ifndef GUARD_OPTIONS_H
#define GUARD_OPTIONS_H
#include <iostream>
#include <string>
#include <cstring>
#include "NtrRom.h"
enum ExecMode {
EXEC_DECRY = 0,
EXEC_ENCRY = 1,
};
struct Options {
Options(int argc, char ** argv) {};
virtual ~Options() = default;
virtual int main() = 0;
static ExecMode TranslateExecMode(const char * value) {
ExecMode mode;
if (strcmp(value, "decry") == 0) {
mode = EXEC_DECRY;
} else if (strcmp(value, "encry") == 0) {
mode = EXEC_ENCRY;
} else {
throw std::invalid_argument(std::string("invalid ExecMode value: expected 'encry' or 'decry'; got ") + value);
}
return mode;
}
};
#endif //GUARD_OPTIONS_H

View File

@ -0,0 +1,7 @@
#include "Overlay.h"
#include "NtrRom.h"
NtrOverlay::NtrOverlay(NtrRom *baserom, u32 ovy_id) {
info = baserom->getOverlayInfo(0, ovy_id);
data = baserom->getOverlayData(0, ovy_id);
}

View File

@ -0,0 +1,32 @@
#ifndef GUARD_OVERLAY_H
#define GUARD_OVERLAY_H
#include <vector>
#include "ntrtypes.h"
class NtrRom;
struct FSOverlayInfo {
u32 ovy_id;
u32 start;
u32 size;
u32 bssize;
u32 sinit_start;
u32 sinit_end;
u32 file_id;
u32 compsize : 24;
u32 flag : 8;
};
class NtrOverlay {
protected:
FSOverlayInfo info;
std::vector<u8> data;
public:
NtrOverlay() = default;
NtrOverlay(FSOverlayInfo &_info, std::vector<u8> &_data) : info(_info), data(_data) {}
NtrOverlay(NtrRom *baserom, u32 ovy_id);
};
#endif //GUARD_OVERLAY_H

View File

@ -0,0 +1,31 @@
#include <iostream>
#include <string>
#include "Decrypt.h"
#include "Encrypt.h"
int main(int argc, char ** argv) {
// Usage:
// mod123encry decry BASEROM OUTFILE OVY_ID
// mod123encry encry BUILDNAME OUTFILE OVY_ID
try {
if (argc < 2) {
throw std::invalid_argument("missing required argument: mode");
}
Options *options;
switch (Options::TranslateExecMode(argv[1])) {
case EXEC_DECRY:
options = new DecryptOptions(argc, argv);
break;
case EXEC_ENCRY:
options = new EncryptOptions(argc, argv);
break;
}
int ret = options->main();
delete options;
return ret;
} catch (std::exception &e) {
std::cerr << "An exception has occurred: " << e.what() << std::endl;
return 1;
}
}

View File

@ -0,0 +1,24 @@
#ifndef GUARD_NTRTYPES_H
#define GUARD_NTRTYPES_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
typedef s32 BOOL;
#ifdef __cplusplus
}
#endif
#endif //GUARD_NTRTYPES_H

View File

@ -0,0 +1,51 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "ntruncompbw.h"
static inline uint32_t READ32(const unsigned char * ptr)
{
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
}
uint32_t MIi_UncompressBackwards(unsigned char ** out_p, size_t compsize)
{
unsigned char * out = *out_p;
// Read the pointer to the end of the compressed image
uint8_t * endptr = out + compsize - 8;
uint32_t size = READ32(endptr);
uint32_t offset = READ32(endptr + 4);
out = realloc(out, compsize + offset);
if (out == NULL)
return -1u;
endptr = out + compsize;
uint8_t * dest_p = endptr + offset;
uint8_t * end = endptr - ((uint8_t)(size >> 24));
uint8_t * start = endptr - (size & ~0xFF000000);
while (dest_p > end && end > start) {
uint8_t r5 = *--end;
for (int i = 0; i < 8; i++) {
if ((r5 & 0x80) == 0)
*--dest_p = *--end;
else {
int ip = *--end;
int r7 = *--end;
r7 = ((r7 | (ip << 8)) & ~0xF000) + 2;
ip += 0x20;
while (ip >= 0) {
dest_p[-1] = dest_p[r7];
dest_p--;
ip -= 0x10;
}
}
if (end <= start)
break;
r5 <<= 1;
}
}
*out_p = out;
return compsize + offset;
}

View File

@ -0,0 +1,14 @@
#ifndef GUARD_NTRUNCOMPBW_H
#define GUARD_NTRUNCOMPBW_H
#ifdef __cplusplus
extern "C" {
#endif
uint32_t MIi_UncompressBackwards(unsigned char ** out_p, size_t compsize);
#ifdef __cplusplus
}
#endif
#endif //GUARD_NTRUNCOMPBW_H

View File

@ -33,6 +33,19 @@ NM=arm-none-eabi-nm
OBJCOPY=arm-none-eabi-objcopy
STEM="$1"
[[ -z "$STEM" ]] && { echo "usage: $0 [-h] STEM"; exit 1; }
[[ $STEM == "-h" ]] && {
echo "usage: $0 [-h] STEM"
echo ""
echo "STEM Prefix to the output static sbin (from running"
echo " mwldarm in a nitro build). For example, if you"
echo " output build/diamond.us/main.sbin, STEM would"
echo " be \"build/diamond.us/main\"."
echo ""
echo "-h Print this message and exit"
exit 0
}
assertFile $STEM.sbin
assertFile $STEM.nef
assertFile ${STEM}_defs.sbin
@ -62,9 +75,10 @@ autoload_start=$(($(getword ${STEM}.sbin $((ptr+8)))-static_load))
# Truncate the static module and dump
static_size=$autoload_start
dd if=${STEM}.sbin of=$(basename $STEM).sbin bs=1 count=${static_size} 2>/dev/null
flags="$flags --update-section $(basename $STEM)=$(basename $STEM).sbin"
to_clean=$(basename $STEM)
static_sbin=$(mktemp --suffix=sbin)
dd if=${STEM}.sbin of=$static_sbin bs=1 count=${static_size} 2>/dev/null
flags="$flags --update-section $(basename $STEM)=$static_sbin"
to_clean=$static_sbin
# Dump autoloads
# The output of `NM -n $STEM.nef` is assumed to be sorted in the order in
@ -72,11 +86,12 @@ to_clean=$(basename $STEM)
# Autoload table is struct { u32 load; u32 size; u32 bsssize; } table[];
while read -r name; do
aload_text_size=$(getword ${STEM}.sbin $((autoload_table_start+4)))
dd if=${STEM}.sbin of=$name.sbin bs=1 skip=$autoload_start count=$aload_text_size 2>/dev/null
aload_sbin=$(mktemp --suffix=sbin)
dd if=${STEM}.sbin of=$aload_sbin bs=1 skip=$autoload_start count=$aload_text_size 2>/dev/null
((autoload_start+=aload_text_size))
((autoload_table_start+=12))
flags="$flags --update-section $name=$name.sbin"
to_clean="$to_clean $name.sbin"
flags="$flags --update-section $name=$aload_sbin"
to_clean="$to_clean $aload_sbin"
done < <($NM -n $STEM.nef | grep -E "SDK_AUTOLOAD_\w+_START" | grep -vE "_(TEXT|BSS|DATA|ARENA|SINIT|ETABLE)_" | cut -d' ' -f3 | cut -d'_' -f3- | sed 's/_START//g')
# Compile the elf