#pragma once

// This file contains the structure of ITCM on the ARM9, as initialized by boot9.

#include "common.h"
#include "memmap.h"


// Structure of the decrypted version of the OTP.  Part of this is in ITCM.
// https://www.3dbrew.org/wiki/OTP_Registers#Plaintext_OTP
typedef struct _Otp {
    // 00: Magic value OTP_MAGIC (little-endian).
    u32 magic;
    // 04: DeviceId, mostly used for TWL stuff.
    u32 deviceId;
    // 08: Fallback keyY for movable.sed.
    u8 fallbackKeyY[16];
    // 18: CtCert flags.  When >= 5, ctcertExponent is big-endian?  Always 05 in systems I've seen.
    u8 ctcertFlags;
    // 19: 00 = retail, nonzero (always 02?) = dev
    u8 ctcertIssuer;
    // 1A-1F: CtCert timestamp - can be used as timestamp of SoC.  Year is minus 1900.
    u8 timestampYear;
    u8 timestampMonth;
    u8 timestampDay;
    u8 timestampHour;
    u8 timestampMinute;
    u8 timestampSecond;
    // 20: CtCert ECDSA exponent.  Big-endian if ctcertFlags >= 5?
    u32 ctcertExponent;
    // 24: CtCert ECDSA private key, in big-endian.  First two bytes always zero?
    u8 ctcertPrivK[32];
    // 44: CtCert ECDSA signature, in big-endian.  Proves CtCert was made by Nintendo.
    u8 ctcertSignature[60];
    // 80: Zero.
    u8 zero[16];
    // 90: Random(?) data used for generation of console-specific keys.
    u8 random[0x50];
    // E0: SHA-256 hash of the rest of OTP.
    u8 hash[256 / 8];
} __attribute__((__packed__)) Otp;

// Value of Otp::magic
#define OTP_MAGIC 0xDEADB00F
// Value to add to Otp::timestampYear to get actual year.
#define OTP_YEAR_BIAS 1900

// Sanity checking.
STATIC_ASSERT(offsetof(Otp, magic) == 0x00);
STATIC_ASSERT(offsetof(Otp, deviceId) == 0x04);
STATIC_ASSERT(offsetof(Otp, fallbackKeyY) == 0x08);
STATIC_ASSERT(offsetof(Otp, ctcertFlags) == 0x18);
STATIC_ASSERT(offsetof(Otp, ctcertIssuer) == 0x19);
STATIC_ASSERT(offsetof(Otp, timestampYear) == 0x1A);
STATIC_ASSERT(offsetof(Otp, timestampMonth) == 0x1B);
STATIC_ASSERT(offsetof(Otp, timestampDay) == 0x1C);
STATIC_ASSERT(offsetof(Otp, timestampHour) == 0x1D);
STATIC_ASSERT(offsetof(Otp, timestampMinute) == 0x1E);
STATIC_ASSERT(offsetof(Otp, timestampSecond) == 0x1F);
STATIC_ASSERT(offsetof(Otp, ctcertExponent) == 0x20);
STATIC_ASSERT(offsetof(Otp, ctcertPrivK) == 0x24);
STATIC_ASSERT(offsetof(Otp, ctcertSignature) == 0x44);
STATIC_ASSERT(offsetof(Otp, zero) == 0x80);
STATIC_ASSERT(offsetof(Otp, random) == 0x90);
STATIC_ASSERT(offsetof(Otp, hash) == 0xE0);
STATIC_ASSERT(sizeof(Otp) == 256);


// Structure of an RSA private key that boot9 puts into ITCM.
// These keys were never used.
typedef struct _Arm9ItcmRsaPrivateKey {
    // 000: RSA modulus.
    u8 modulus[256];
    // 100: RSA private exponent.
    u8 privateExponent[256];
} __attribute__((__packed__)) Arm9ItcmRsaPrivateKey;

STATIC_ASSERT(sizeof(Arm9ItcmRsaPrivateKey) == 512);


// Structure of the data in ARM9 ITCM, filled in by boot9.
// https://www.3dbrew.org/wiki/OTP_Registers#Plaintext_OTP
typedef struct _Arm9Itcm {
    // 0000: Uninitialized / available.
    u8 uninitializedBefore[0x3700];
    // 3700: Copied code from boot9 to disable boot9.
    u8 boot9DisableCode[0x100];
    // 3800: Decrypted OTP.  boot9 zeros last 0x70 bytes (Otp::random and Otp::hash).
    Otp otp;
    // 3900: Copy of NAND MBR, which includes the NCSD header.
    u8 ncsd[0x200];
    // 3B00: Decrypted FIRM header for the FIRM that was loaded by boot9.
    u8 decryptedFirmHeader[0x200];
    // 3D00: RSA modulus for keyslots 0-3 as left by boot9 (big-endian).
    u8 rsaKeyslotModulus[4][0x100];
    // 4100: RSA private keys for who knows what; nothing ever used them.
    Arm9ItcmRsaPrivateKey rsaPrivateKeys[4];
    // 4900: RSA public keys (moduli) for things Nintendo signs.
    u8 rsaModulusCartNCSD[0x100];
    u8 rsaModulusAccessDesc[0x100];
    u8 rsaModulusUnused2[0x100];
    u8 rsaModulusUnused3[0x100];
    // 4D00: Unknown keys; unused.
    u8 unknownKeys[8][16];
    // 4D80: NAND CID
    u8 nandCid[0x64];
    // 4DE4: Padding after NAND CID; uninitialized.
    u8 uninitializedCid[0x1C];
    // 4E00: Unused data before TWL keys.
    u8 uninitializedMiddle[0x200];
    // 5000: RSA modulus for TWL System Menu.
    u8 rsaModulusTwlSystemMenu[0x80];
    // 5080: RSA modulus for TWL Wi-Fi firmware and DSi Sound.
    u8 rsaModulusTwlSound[0x80];
    // 5100: RSA modulus for TWL system applications.
    u8 rsaModulusTwlSystemApps[0x80];
    // 5180: RSA modulus for TWL normal applications.
    u8 rsaModulusTwlNormalApps[0x80];
    // 5200: TWL ???
    u8 twlUnknown1[0x10];
    // 5210: TWL keyY for per-console ES blocks.
    u8 twlKeyYUniqueES[0x10];
    // 5220: TWL keyY for fixed-key ES blocks.
    u8 twlKeyYFixedES[0x10];
    // 5230: TWL ???
    u8 twlUnknown2[0xD0];
    // 5300: TWL common key.
    u8 twlCommonKey[0x10];
    // 5310: TWL ???
    u8 twlUnknown3[0x40];
    // 5350: TWL normal key for keyslot 0x02.
    u8 twlKeyslot02NormalKey[0x10];
    // 5360: TWL ???
    u8 twlUnknown4[0x20];
    // 5380: TWL keyX for keyslot 0x00.
    u8 twlKeyslot00KeyX[0x10];
    // 5390: TWL ???
    u8 twlUnknown5[8];
    // 5398: TWL "Tad" crypto keyX.
    u8 twlTadKeyX[0x10];
    // 53A8: TWL middle two words of keyslot 0x03 keyX.
    u8 twlKeyslot03KeyXMiddle[8];
    // 53B0: TWL ???
    u8 twlUnknown6[12];
    // 53BC: TWL keyY for keyslot 0x01.  Truncated (last word becomes E1A00005).
    u8 twlKeyslot01KeyY[12];
    // 53C8: TWL keyY for NAND crypto on retail TWL.
    u8 twlNANDKeyY[0x10];
    // 53D8: TWL ???
    u8 twlUnknown7[8];
    // 53E0: TWL Blowfish cart key.
    u8 twlBlowfishCartKey[0x1048];
    // 6428: NTR Blowfish cart key.
    u8 ntrBlowfishCartKey[0x1048];
    // 7470: End of the line.
    u8 uninitializedEnd1[0x790];
    // 7C00: ...But wait, this buffer is used for the FIRM header during firmlaunch on >= 9.5.0.
    u8 firmlaunchHeader[0x100];
    // 7D00: Back to our regularly-scheduled emptiness.
    u8 uninitializedEnd2[0x300];
} __attribute__((__packed__)) Arm9Itcm;

// Sanity checking.
STATIC_ASSERT(offsetof(Arm9Itcm, otp) == 0x3800);
STATIC_ASSERT(offsetof(Arm9Itcm, rsaModulusCartNCSD) == 0x4900);
STATIC_ASSERT(offsetof(Arm9Itcm, rsaModulusAccessDesc) == 0x4A00);
STATIC_ASSERT(offsetof(Arm9Itcm, twlNANDKeyY) == 0x53C8);
STATIC_ASSERT(offsetof(Arm9Itcm, twlBlowfishCartKey) == 0x53E0);
STATIC_ASSERT(offsetof(Arm9Itcm, ntrBlowfishCartKey) == 0x6428);
STATIC_ASSERT(sizeof(Arm9Itcm) == 0x8000);


// Macro for accessing ITCM.
#define ARM9_ITCM ((Arm9Itcm*) __ITCM_ADDR)
