mirror of
https://github.com/d0k3/GodMode9.git
synced 2026-05-31 06:46:56 +00:00
Compare commits
No commits in common. "master" and "v2.2.2" have entirely different histories.
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@ -8,12 +8,7 @@ jobs:
|
|||||||
container: devkitpro/devkitarm
|
container: devkitpro/devkitarm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v1
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup environment
|
|
||||||
run: git config --global safe.directory '*'
|
|
||||||
|
|
||||||
- name: Fix apt sources
|
- name: Fix apt sources
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/crowdin-commit.yml
vendored
2
.github/workflows/crowdin-commit.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
|||||||
- name: Install tools
|
- name: Install tools
|
||||||
run: |
|
run: |
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install python3 python3-pip p7zip-full -y
|
apt-get install python3 python3-pip -y
|
||||||
python3 -m pip install --upgrade pip setuptools --break-system-packages
|
python3 -m pip install --upgrade pip setuptools --break-system-packages
|
||||||
python3 -m pip install cryptography git+https://github.com/TuxSH/firmtool.git --break-system-packages
|
python3 -m pip install cryptography git+https://github.com/TuxSH/firmtool.git --break-system-packages
|
||||||
|
|
||||||
|
|||||||
@ -40,8 +40,6 @@ extern "C" {
|
|||||||
#define AES_CNT_FLUSH_WRITE 0x00000400u
|
#define AES_CNT_FLUSH_WRITE 0x00000400u
|
||||||
|
|
||||||
#define AES_CNT_CTRNAND_MODE (AES_CTR_MODE | AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN)
|
#define AES_CNT_CTRNAND_MODE (AES_CTR_MODE | AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN)
|
||||||
// gamecard save crypto and NAND crypto use the same options for AES-CTR.
|
|
||||||
#define AES_CNT_CART_SAVE_MODE AES_CNT_CTRNAND_MODE
|
|
||||||
#define AES_CNT_TWLNAND_MODE AES_CTR_MODE
|
#define AES_CNT_TWLNAND_MODE AES_CTR_MODE
|
||||||
#define AES_CNT_TITLEKEY_DECRYPT_MODE (AES_CBC_DECRYPT_MODE | AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN)
|
#define AES_CNT_TITLEKEY_DECRYPT_MODE (AES_CBC_DECRYPT_MODE | AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN)
|
||||||
#define AES_CNT_TITLEKEY_ENCRYPT_MODE (AES_CBC_ENCRYPT_MODE | AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN)
|
#define AES_CNT_TITLEKEY_ENCRYPT_MODE (AES_CBC_ENCRYPT_MODE | AES_CNT_INPUT_ORDER | AES_CNT_OUTPUT_ORDER | AES_CNT_INPUT_ENDIAN | AES_CNT_OUTPUT_ENDIAN)
|
||||||
|
|||||||
@ -288,17 +288,15 @@ static u32 RemoveBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_of
|
|||||||
if (BDRIWrite(fht_offset + hash_bucket * sizeof(u32), sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK)
|
if (BDRIWrite(fht_offset + hash_bucket * sizeof(u32), sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK)
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
u32 prev_hash_index = 0;
|
|
||||||
do {
|
do {
|
||||||
if (index_hash == 0) // This shouldn't happen if the entry was properly added
|
if (index_hash == 0) // This shouldn't happen if the entry was properly added
|
||||||
break;
|
break;
|
||||||
|
|
||||||
prev_hash_index = index_hash;
|
|
||||||
if (BDRIRead(fet_offset + index_hash * sizeof(TdbFileEntry) + 0x28, sizeof(u32), &index_hash) != FR_OK)
|
if (BDRIRead(fet_offset + index_hash * sizeof(TdbFileEntry) + 0x28, sizeof(u32), &index_hash) != FR_OK)
|
||||||
return 1;
|
return 1;
|
||||||
} while (index_hash != index);
|
} while (index_hash != index);
|
||||||
|
|
||||||
if ((prev_hash_index != 0) && BDRIWrite(fet_offset + prev_hash_index * sizeof(TdbFileEntry) + 0x28, sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK)
|
if ((index_hash != 0) && BDRIWrite(fet_offset + index_hash * sizeof(TdbFileEntry) + 0x28, sizeof(u32), &(file_entry.hash_bucket_next_index)) != FR_OK)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,26 +322,16 @@ static u32 RemoveBDRIEntry(const BDRIFsHeader* fs_header, const u32 fs_header_of
|
|||||||
return 1;
|
return 1;
|
||||||
} while (getfatindex(fat_entry[1]) != 0);
|
} while (getfatindex(fat_entry[1]) != 0);
|
||||||
|
|
||||||
// Bug fix: use buildfatuv to explicitly clear Bit 31 (the multi-block flag).
|
fat_entry[1] |= next_free_index;
|
||||||
// If the tail of the freed chain is a multi-block node start, Bit 31 is already
|
|
||||||
// set in fat_entry[1]. A plain |= would keep it set, corrupting the free list.
|
|
||||||
fat_entry[1] = buildfatuv(next_free_index, false);
|
|
||||||
|
|
||||||
if (BDRIWrite(fat_offset + fat_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
|
if ((BDRIWrite(fat_offset + fat_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK) ||
|
||||||
|
(BDRIRead(fat_offset + next_free_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// Bug fix: guard against next_free_index == 0 (freed entry was the last free
|
fat_entry[0] = buildfatuv(fat_index, false);
|
||||||
// block). Without this guard, FAT[0] (the free-list sentinel) gets corrupted
|
|
||||||
// by a stray back-pointer write.
|
|
||||||
if (next_free_index != 0) {
|
|
||||||
if (BDRIRead(fat_offset + next_free_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
fat_entry[0] = buildfatuv(fat_index, false);
|
if (BDRIWrite(fat_offset + next_free_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
|
||||||
|
return 1;
|
||||||
if (BDRIWrite(fat_offset + next_free_index * FAT_ENTRY_SIZE, FAT_ENTRY_SIZE, fat_entry) != FR_OK)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,7 +61,7 @@ u32 BuildCiaCert(u8* ciacert) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const char* const retail_issuers[] = {"Root-CA00000003", "Root-CA00000003-XS0000000c", "Root-CA00000003-CP0000000b"};
|
static const char* const retail_issuers[] = {"Root-CA00000003", "Root-CA00000003-XS0000000c", "Root-CA00000003-CP0000000b"};
|
||||||
static const char* const dev_issuers[] = {"Root-CA00000004", "Root-CA00000004-CP0000000a", "Root-CA00000004-XS00000009"};
|
static const char* const dev_issuers[] = {"Root-CA00000004", "Root-CA00000004-XS00000009", "Root-CA00000004-CP0000000a"};
|
||||||
|
|
||||||
size_t size = CIA_CERT_SIZE;
|
size_t size = CIA_CERT_SIZE;
|
||||||
if (BuildRawCertBundleFromCertDb(ciacert, &size, !IS_DEVKIT ? retail_issuers : dev_issuers, 3) ||
|
if (BuildRawCertBundleFromCertDb(ciacert, &size, !IS_DEVKIT ? retail_issuers : dev_issuers, 3) ||
|
||||||
|
|||||||
@ -33,8 +33,7 @@ typedef struct {
|
|||||||
u8 sector_zero_offset[0x4];
|
u8 sector_zero_offset[0x4];
|
||||||
u8 partition_flags[8];
|
u8 partition_flags[8];
|
||||||
u8 partitionId_table[8][8];
|
u8 partitionId_table[8][8];
|
||||||
u8 reserved[0x2F];
|
u8 reserved[0x30];
|
||||||
u8 extra_save_keysel;
|
|
||||||
} PACKED_STRUCT NcsdHeader;
|
} PACKED_STRUCT NcsdHeader;
|
||||||
|
|
||||||
u32 ValidateNcsdHeader(NcsdHeader* header);
|
u32 ValidateNcsdHeader(NcsdHeader* header);
|
||||||
|
|||||||
@ -80,9 +80,8 @@ u32 GetCartInfoString(char* info, size_t info_size, CartData* cdata) {
|
|||||||
"Product Code : %.10s\n"
|
"Product Code : %.10s\n"
|
||||||
"Revision : %lu\n"
|
"Revision : %lu\n"
|
||||||
"Cart ID : %08lX\n"
|
"Cart ID : %08lX\n"
|
||||||
"Cart ID2 : %08lX\n"
|
|
||||||
"Platform : %s\n",
|
"Platform : %s\n",
|
||||||
ncsd->mediaId, ncch->productcode, cdata_i->rom_version, cdata_i->cart_id, cdata_i->cart_id2,
|
ncsd->mediaId, ncch->productcode, cdata_i->rom_version, cdata_i->cart_id,
|
||||||
(ncch->flags[4] == 0x2) ? "N3DS" : "O3DS");
|
(ncch->flags[4] == 0x2) ? "N3DS" : "O3DS");
|
||||||
} else if (cdata->cart_type & CART_NTR) {
|
} else if (cdata->cart_type & CART_NTR) {
|
||||||
CartDataNtrTwl* cdata_i = (CartDataNtrTwl*)cdata;
|
CartDataNtrTwl* cdata_i = (CartDataNtrTwl*)cdata;
|
||||||
@ -221,10 +220,9 @@ u32 InitCartRead(CartData* cdata) {
|
|||||||
memcpy(priv_header + 0x44, &(cdata->cart_id2), 4);
|
memcpy(priv_header + 0x44, &(cdata->cart_id2), 4);
|
||||||
memset(priv_header + 0x48, 0xFF, 8);
|
memset(priv_header + 0x48, 0xFF, 8);
|
||||||
|
|
||||||
bool is_card2 = cdata->cart_id & 0x8000000;
|
// save data
|
||||||
u32 card2_offset = getle32(cdata->header + 0x200);
|
u32 card2_offset = getle32(cdata->header + 0x200);
|
||||||
|
if (card2_offset != 0xFFFFFFFF) {
|
||||||
if (is_card2 && card2_offset != 0xFFFFFFFF) {
|
|
||||||
cdata->save_type = CARD_SAVE_CARD2;
|
cdata->save_type = CARD_SAVE_CARD2;
|
||||||
cdata->save_size = GetCtrCartSaveSize(cdata);
|
cdata->save_size = GetCtrCartSaveSize(cdata);
|
||||||
// Sanity checks
|
// Sanity checks
|
||||||
@ -435,11 +433,11 @@ u32 ReadCartPrivateHeader(void* buffer, u64 offset, u64 count, CartData* cdata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
u32 ReadCartInfo(u8* buffer, u64 offset, u64 count, CartData* cdata) {
|
u32 ReadCartInfo(u8* buffer, u64 offset, u64 count, CartData* cdata) {
|
||||||
char info[301];
|
char info[256];
|
||||||
u32 len;
|
u32 len;
|
||||||
|
|
||||||
GetCartInfoString(info, sizeof(info), cdata);
|
GetCartInfoString(info, sizeof(info), cdata);
|
||||||
len = strnlen(info, 300);
|
len = strnlen(info, 255);
|
||||||
|
|
||||||
if (offset >= len) return 0;
|
if (offset >= len) return 0;
|
||||||
if (offset + count > len) count = len - offset;
|
if (offset + count > len) count = len - offset;
|
||||||
|
|||||||
@ -211,12 +211,10 @@ void Cart_Secure_Init(u32 *buf, u32 *out)
|
|||||||
CTR_SendCommand(C5_cmd, 0, 1, 0x100002C, NULL);
|
CTR_SendCommand(C5_cmd, 0, 1, 0x100002C, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process9 (or at least the latest version of it, in NATIVE_FIRM v32673) does not use this in its init routine.
|
for (int i = 0; i < 5; ++i) {
|
||||||
// leaving this in adds an unnecessary 2.4-2.6s of init time.
|
CTR_SendCommand(A2_cmd, 4, 1, 0x701002C, &test);
|
||||||
//for (int i = 0; i < 5; ++i) {
|
ARM_WaitCycles(0xF0000 * 8);
|
||||||
// CTR_SendCommand(A2_cmd, 4, 1, 0x701002C, &test);
|
}
|
||||||
// ARM_WaitCycles(0xF0000 * 8);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cart_Dummy(void) {
|
void Cart_Dummy(void) {
|
||||||
|
|||||||
@ -1,490 +0,0 @@
|
|||||||
#include "aes.h"
|
|
||||||
#include "gamecart.h"
|
|
||||||
#include "ncch.h"
|
|
||||||
#include "ncsd.h"
|
|
||||||
#include "crc16.h"
|
|
||||||
#include "sha.h"
|
|
||||||
#include "types.h"
|
|
||||||
#include "unittype.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
typedef struct SaveBlockmapHeader {
|
|
||||||
u32 flushcount; // how many times the blockmap was entirely rewritten
|
|
||||||
u32 common_min_remap_count; // every physical sector in the flash has been rewritten at least `common_min_remap_count` times
|
|
||||||
// that is, the total remap count for physical sector x representing virtual sector n = blockmap[n].remap_count + common_min_remap_count
|
|
||||||
} SaveBlockmapHeader;
|
|
||||||
|
|
||||||
typedef struct SaveBlockmapEntryV1 {
|
|
||||||
struct {
|
|
||||||
u8 phys_sec : 7; // physical sector representing this virtual sector
|
|
||||||
u8 used : 1; // whether or not this virtual sector is in use
|
|
||||||
};
|
|
||||||
|
|
||||||
u8 remap_count; // amount of times phys_sec has been rewritten
|
|
||||||
|
|
||||||
u8 checksums[8]; // checksum values (if applicable) for each 0x200 sized sector in the full 0x1000-sized physical flash sector this structure covers
|
|
||||||
} SaveBlockmapEntryV1;
|
|
||||||
|
|
||||||
typedef struct SaveBlockmapEntryV2 {
|
|
||||||
struct {
|
|
||||||
u8 remap_count : 7; // amount of times phys_sec has been rewritten
|
|
||||||
u8 used : 1; // whether or not this virtual sector is in use
|
|
||||||
};
|
|
||||||
u8 phys_sec; // physical sector representing this virtual sector
|
|
||||||
} SaveBlockmapEntryV2;
|
|
||||||
|
|
||||||
typedef struct SaveBlockmapEntry {
|
|
||||||
u8 remap_count; // how many times phys_sec was remapped (written to)
|
|
||||||
u8 used; // whether or not this virtual sector is used
|
|
||||||
u8 phys_sec; // the physical sector this virtual sector is mapped to
|
|
||||||
u8 checksums[8]; // checksum values (if applicable) for each 0x200 sized sector in the full 0x1000-sized physical flash sector this structure covers
|
|
||||||
} SaveBlockmapEntry;
|
|
||||||
|
|
||||||
typedef struct SaveJournalEntryData
|
|
||||||
{
|
|
||||||
u8 src_virt_sec; // virtual sector that is being remapped
|
|
||||||
u8 dst_virt_sec; // virtual sector whose corresponding physical sector was used to remap src_virt_sec to
|
|
||||||
u8 dst_phys_sec; // the physical sector of dst_virt_sec the data of src_virt_sec now resides in
|
|
||||||
u8 src_phys_sec; // the previous physical sector where the data in src_virt_sec resided in before the remapping
|
|
||||||
u8 new_dst_remap_count; // the new remap count (including this remap write) of the destination physical sector
|
|
||||||
u8 src_remap_count; // the remap count of the source physical sector (unchanged)
|
|
||||||
u8 new_dst_checksums[8]; // checksums for the data that was written to the destination physical sector, pending to be written to blockmap
|
|
||||||
} SaveJournalEntryData;
|
|
||||||
|
|
||||||
typedef struct SaveJournalEntry
|
|
||||||
{
|
|
||||||
SaveJournalEntryData main;
|
|
||||||
SaveJournalEntryData backup; // compared to main to check for integrity before applying to blockmap
|
|
||||||
u32 pad;
|
|
||||||
} SaveJournalEntry;
|
|
||||||
|
|
||||||
typedef enum CardSaveWearLevelingType {
|
|
||||||
CARD_SAVE_WEAR_LEVELING_V1 = 10,
|
|
||||||
CARD_SAVE_WEAR_LEVELING_V2 = 2,
|
|
||||||
|
|
||||||
CARD_SAVE_WEAR_LEVELING_NONE = -1,
|
|
||||||
} CardSaveWearLevelingType;
|
|
||||||
|
|
||||||
typedef enum CardSaveCryptoType {
|
|
||||||
CARD_SAVE_CRYPTO_V0 = 0,
|
|
||||||
CARD_SAVE_CRYPTO_V1 = 1,
|
|
||||||
CARD_SAVE_CRYPTO_V1_N3DS = 2,
|
|
||||||
CARD_SAVE_CRYPTO_V2 = 3,
|
|
||||||
|
|
||||||
CARD_SAVE_CRYPTO_INVALID = 0x7FFFFFFF,
|
|
||||||
} CardSaveCryptoType;
|
|
||||||
|
|
||||||
typedef enum CardSaveCryptoKeyslot {
|
|
||||||
CARD_SAVE_CMAC_KEYSLOT_O3DS = 0x33,
|
|
||||||
CARD_SAVE_CMAC_KEYSLOT_N3DS = 0x19,
|
|
||||||
CARD_SAVE_CRYPTO_KEYSLOT_O3DS = 0x37,
|
|
||||||
CARD_SAVE_CRYPTO_KEYSLOT_N3DS = 0x1A,
|
|
||||||
} CardSaveCryptoKeyslot;
|
|
||||||
|
|
||||||
typedef struct CartSaveContext {
|
|
||||||
// wear leveling
|
|
||||||
|
|
||||||
/* min. 87, max 118 journal entries */
|
|
||||||
SaveJournalEntry journal[118];
|
|
||||||
/* max case is header + 127 v1 blockmap entries + crc16 */
|
|
||||||
u8 blockmap[sizeof(SaveBlockmapHeader) + 127 * sizeof(SaveBlockmapEntryV1) + 2];
|
|
||||||
|
|
||||||
CardSaveWearLevelingType type;
|
|
||||||
u32 num_journal_entries; // number of journal entries
|
|
||||||
u32 num_blockmap_sectors; // number of sectors covered by the blockmap (nsectors - 1)
|
|
||||||
u32 blockmap_size; // size of the blockmap, in bytes
|
|
||||||
u32 logical_size; // actual number of available data sectors for the DISA save image (nsectors - 2)
|
|
||||||
|
|
||||||
// crypto
|
|
||||||
|
|
||||||
u32 crypto_type;
|
|
||||||
bool repeating_ctr;
|
|
||||||
|
|
||||||
} CartSaveContext;
|
|
||||||
|
|
||||||
static CartSaveContext savectx;
|
|
||||||
|
|
||||||
// wear leveling
|
|
||||||
|
|
||||||
static void SetBlockmapEntry(u32 index, SaveBlockmapEntry *in_entry) {
|
|
||||||
u8 *base_ptr = &savectx.blockmap[sizeof(SaveBlockmapHeader)];
|
|
||||||
if (savectx.type == CARD_SAVE_WEAR_LEVELING_V2) {
|
|
||||||
SaveBlockmapEntryV2 *entry = &((SaveBlockmapEntryV2 *)base_ptr)[index];
|
|
||||||
entry->phys_sec = in_entry->phys_sec;
|
|
||||||
entry->remap_count = in_entry->remap_count;
|
|
||||||
entry->used = in_entry->used;
|
|
||||||
} else {
|
|
||||||
SaveBlockmapEntryV1 *entry = &((SaveBlockmapEntryV1 *)base_ptr)[index];
|
|
||||||
entry->phys_sec = in_entry->phys_sec;
|
|
||||||
entry->remap_count = in_entry->remap_count;
|
|
||||||
entry->used = in_entry->used;
|
|
||||||
memcpy(entry->checksums, in_entry->checksums, sizeof(entry->checksums));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static SaveBlockmapEntry GetBlockmapEntry(u32 index) {
|
|
||||||
u8 *base_ptr = &savectx.blockmap[sizeof(SaveBlockmapHeader)];
|
|
||||||
if (savectx.type == CARD_SAVE_WEAR_LEVELING_V2) {
|
|
||||||
SaveBlockmapEntryV2 *entries = (SaveBlockmapEntryV2 *)base_ptr;
|
|
||||||
return (SaveBlockmapEntry) { .checksums = { 0 }, .phys_sec = entries[index].phys_sec, .remap_count = entries[index].remap_count, .used = entries[index].used };
|
|
||||||
} else {
|
|
||||||
SaveBlockmapEntryV1 *entries = (SaveBlockmapEntryV1 *)base_ptr;
|
|
||||||
SaveBlockmapEntry out = (SaveBlockmapEntry) { .phys_sec = entries[index].phys_sec, .remap_count = entries[index].remap_count, .used = entries[index].used };
|
|
||||||
memcpy(out.checksums, entries[index].checksums, sizeof(out.checksums));
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ApplyJournalEntryToBlockmap(SaveJournalEntry *entry) {
|
|
||||||
SaveBlockmapEntry ent = {
|
|
||||||
.used = 1,
|
|
||||||
.phys_sec = entry->main.dst_phys_sec,
|
|
||||||
.remap_count = entry->main.new_dst_remap_count
|
|
||||||
};
|
|
||||||
memcpy(ent.checksums, entry->main.new_dst_checksums, sizeof(ent.checksums));
|
|
||||||
|
|
||||||
// remap the virtual sector to the new physical sector
|
|
||||||
SetBlockmapEntry(entry->main.src_virt_sec, &ent);
|
|
||||||
|
|
||||||
// unless an actual remap didn't occur, we must mark (the physical sector
|
|
||||||
// whose virtual sector number we used for remapping this virtual sector to)
|
|
||||||
// as free and ready to use for another remap
|
|
||||||
if (entry->main.dst_phys_sec != entry->main.src_phys_sec) {
|
|
||||||
ent.used = 0;
|
|
||||||
ent.phys_sec = entry->main.src_phys_sec;
|
|
||||||
ent.remap_count = entry->main.src_remap_count;
|
|
||||||
memset(ent.checksums, 0, sizeof(ent.checksums));
|
|
||||||
SetBlockmapEntry(entry->main.dst_virt_sec, &ent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 InitSaveWearLeveling(CartData *cdata, u32 header_offset) {
|
|
||||||
// CARD2 does not implement wear leveling for the writable portion of the "ROM"
|
|
||||||
if (cdata->cart_id & 0x8000000) {
|
|
||||||
savectx.type = CARD_SAVE_WEAR_LEVELING_NONE;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPI flash savedata implements wear leveling
|
|
||||||
u32 num_physical_sectors = cdata->save_size / 0x1000;
|
|
||||||
// for some reason, the blockmap also covers the sector for the backup blockmap + journal header,
|
|
||||||
// despite the logical size calculation (see below) removing this sector from the available logical space
|
|
||||||
savectx.num_blockmap_sectors = num_physical_sectors - 1;
|
|
||||||
// the blockmap + journal header (combined size 0x1000) exists twice, the first being the main one and the second being the "backup" one.
|
|
||||||
// therefore, the actual number of usable data sectors for save data is num_sectors - 2.
|
|
||||||
savectx.logical_size = (num_physical_sectors - 2) * 0x1000;
|
|
||||||
|
|
||||||
if (num_physical_sectors > 128) {
|
|
||||||
// V2, header + blockmap_entries_v2[511] + crc
|
|
||||||
savectx.blockmap_size = 0x400; // fixed size
|
|
||||||
savectx.type = CARD_SAVE_WEAR_LEVELING_V2;
|
|
||||||
} else {
|
|
||||||
// V1, header + blockmap_entries_v1[n] + crc
|
|
||||||
savectx.blockmap_size = sizeof(SaveBlockmapHeader) + savectx.num_blockmap_sectors * sizeof(SaveBlockmapEntryV1) + sizeof(u16);
|
|
||||||
savectx.type = CARD_SAVE_WEAR_LEVELING_V1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add CMAC verification ability for DISA
|
|
||||||
|
|
||||||
if (ReadCartSave(savectx.blockmap, header_offset, savectx.blockmap_size, cdata) != 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
u32 journal_size = 0x1000 - savectx.blockmap_size;
|
|
||||||
savectx.num_journal_entries = journal_size / sizeof(SaveJournalEntry);
|
|
||||||
|
|
||||||
if (ReadCartSave((u8 *)savectx.journal, header_offset + savectx.blockmap_size, journal_size, cdata) != 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
u8 *crcLoc = &savectx.blockmap[savectx.blockmap_size - 2];
|
|
||||||
u16 expected_bmap_crc = crcLoc[0] | crcLoc[1] << 8;
|
|
||||||
u16 calc_bmap_crc = crc16_quick(savectx.blockmap, savectx.blockmap_size - 2);
|
|
||||||
if (expected_bmap_crc != calc_bmap_crc) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 journal_idx = 0;
|
|
||||||
|
|
||||||
for (; journal_idx < savectx.num_journal_entries; journal_idx++) {
|
|
||||||
SaveJournalEntry *ent = &savectx.journal[journal_idx];
|
|
||||||
|
|
||||||
// journal entry should point to a valid physical sector
|
|
||||||
if (ent->main.dst_phys_sec) {
|
|
||||||
// reached the end of the journal
|
|
||||||
if (ent->main.src_virt_sec >= savectx.num_blockmap_sectors)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ApplyJournalEntryToBlockmap(ent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 blank[sizeof(SaveJournalEntry)];
|
|
||||||
memset(blank, 0xFF, sizeof(blank));
|
|
||||||
u32 num_bad_journal_entries = 0;
|
|
||||||
for (; journal_idx < savectx.num_journal_entries; journal_idx++) {
|
|
||||||
num_bad_journal_entries += memcmp((u8 *)&savectx.journal[journal_idx], blank, sizeof(SaveJournalEntry)) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num_bad_journal_entries) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// crypto
|
|
||||||
|
|
||||||
static u32 InitCtrCardSaveCryptoKey(CartData *cdata) {
|
|
||||||
NcsdHeader *ncsd = (NcsdHeader *) (void *) cdata->header;
|
|
||||||
// save data crypto (if supported)
|
|
||||||
u32 save_media_old = ncsd->partition_flags[7];
|
|
||||||
u32 save_media_new = ncsd->partition_flags[3];
|
|
||||||
u8 save_crypto_keysel_base = ncsd->partition_flags[1];
|
|
||||||
u8 save_crypto_keysel_extra = ncsd->extra_save_keysel;
|
|
||||||
s32 save_crypto_keysel = 0;
|
|
||||||
bool is_card2 = cdata->cart_id & 0x8000000;
|
|
||||||
|
|
||||||
bool supported_save_crypto =
|
|
||||||
(save_media_old == 0 && save_media_new == 0) || /* SPI save flash (very old carts) */
|
|
||||||
(save_media_old == 1) || /* old SPI flash */
|
|
||||||
(save_media_old == 0 && save_media_new == 1) || /* newer SPI flash */
|
|
||||||
is_card2 /* CARD2 */;
|
|
||||||
|
|
||||||
// skip everything if there isn't save data or its crypto is not supported
|
|
||||||
if (cdata->save_type == CARD_SAVE_NONE || !supported_save_crypto) {
|
|
||||||
savectx.crypto_type = CARD_SAVE_CRYPTO_INVALID;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (save_media_old == 0 && save_media_new == 0 && !is_card2)
|
|
||||||
savectx.repeating_ctr = true;
|
|
||||||
|
|
||||||
if (is_card2)
|
|
||||||
save_crypto_keysel = save_crypto_keysel_base + save_crypto_keysel_extra;
|
|
||||||
else if (save_media_old != 0)
|
|
||||||
save_crypto_keysel = 0;
|
|
||||||
else if (save_media_new != 0)
|
|
||||||
save_crypto_keysel = save_crypto_keysel_base + save_crypto_keysel_extra;
|
|
||||||
else
|
|
||||||
save_crypto_keysel = -1;
|
|
||||||
|
|
||||||
save_crypto_keysel += 1;
|
|
||||||
|
|
||||||
u8 save_key_y[16] = { 0 };
|
|
||||||
|
|
||||||
struct {
|
|
||||||
NcchHeader ncch;
|
|
||||||
NcchExtHeader exthdr;
|
|
||||||
} hdrs;
|
|
||||||
|
|
||||||
ReadCartBytes(&hdrs, ncsd->partitions[0].offset * NCSD_MEDIA_UNIT, sizeof(hdrs), cdata, false);
|
|
||||||
DecryptNcch(&hdrs.exthdr, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), &hdrs.ncch, NULL);
|
|
||||||
u8 *cart_unique_id = cdata->header + 0x4000;
|
|
||||||
|
|
||||||
switch (save_crypto_keysel) {
|
|
||||||
case 0: // "backup security version" -1
|
|
||||||
memcpy(save_key_y, hdrs.exthdr.signature, 8);
|
|
||||||
memcpy(&save_key_y[8], &cdata->cart_id, 4);
|
|
||||||
memcpy(&save_key_y[12], &cdata->cart_id2, 4);
|
|
||||||
savectx.crypto_type = CARD_SAVE_CRYPTO_V0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: // "backup security version" 0
|
|
||||||
case 11: // "backup security version" 10
|
|
||||||
{
|
|
||||||
// version 10 (11) is not supported on O3DS
|
|
||||||
if (IS_O3DS && save_crypto_keysel == 11) {
|
|
||||||
savectx.crypto_type = CARD_SAVE_CRYPTO_INVALID;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 tmpbuf[0x48];
|
|
||||||
u8 hash[0x20];
|
|
||||||
memcpy(tmpbuf, hdrs.exthdr.signature, 8);
|
|
||||||
memcpy(&tmpbuf[8], cart_unique_id, 0x40);
|
|
||||||
|
|
||||||
sha_quick(hash, tmpbuf, 0x48, SHA256_MODE);
|
|
||||||
memcpy(save_key_y, hash, 16);
|
|
||||||
savectx.crypto_type = save_crypto_keysel == 11 ? CARD_SAVE_CRYPTO_V1_N3DS : CARD_SAVE_CRYPTO_V1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 2: // "backup security version" 1
|
|
||||||
{
|
|
||||||
static bool save60KeyYSetup = false;
|
|
||||||
|
|
||||||
if (!save60KeyYSetup) {
|
|
||||||
static const u8 save60KeyY[16] = { 0xC3, 0x69, 0xBA, 0xA2, 0x1E, 0x18, 0x8A, 0x88, 0xA9, 0xAA, 0x94, 0xE5, 0x50, 0x6A, 0x9F, 0x16 };
|
|
||||||
setup_aeskeyY(0x2F, save60KeyY);
|
|
||||||
save60KeyYSetup = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 hash[0x20];
|
|
||||||
u8 tmpbuf[0x70];
|
|
||||||
|
|
||||||
u32 in_ncch_offset = hdrs.ncch.offset_exefs * NCCH_MEDIA_UNIT + 0x1E0;
|
|
||||||
ReadCartBytes(&hash, ncsd->partitions[0].offset * NCSD_MEDIA_UNIT + in_ncch_offset, sizeof(hash), cdata, false);
|
|
||||||
DecryptNcch(hash, in_ncch_offset, sizeof(hash), &hdrs.ncch, NULL);
|
|
||||||
|
|
||||||
memcpy(tmpbuf, hdrs.exthdr.signature, 8);
|
|
||||||
memcpy(&tmpbuf[8], cart_unique_id, 0x40);
|
|
||||||
memcpy(&tmpbuf[0x48], &hdrs.ncch.programId, 8);
|
|
||||||
memcpy(&tmpbuf[0x50], hash, 0x20);
|
|
||||||
sha_quick(hash, tmpbuf, 0x70, SHA256_MODE);
|
|
||||||
|
|
||||||
use_aeskey(0x2F);
|
|
||||||
aes_cmac(hash, save_key_y, 2);
|
|
||||||
savectx.crypto_type = CARD_SAVE_CRYPTO_V2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
savectx.crypto_type = CARD_SAVE_CRYPTO_INVALID;
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 cmac_keyslot = savectx.crypto_type == CARD_SAVE_CRYPTO_V1_N3DS ? CARD_SAVE_CMAC_KEYSLOT_N3DS : CARD_SAVE_CMAC_KEYSLOT_O3DS;
|
|
||||||
u32 crypto_keyslot = savectx.crypto_type == CARD_SAVE_CRYPTO_V1_N3DS ? CARD_SAVE_CRYPTO_KEYSLOT_N3DS : CARD_SAVE_CRYPTO_KEYSLOT_O3DS;
|
|
||||||
|
|
||||||
setup_aeskeyY(cmac_keyslot, save_key_y); // savedata CMAC key
|
|
||||||
setup_aeskeyY(crypto_keyslot, save_key_y); // savedata crypto key
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 InitCtrCardSave(CartData *cdata) {
|
|
||||||
// the wear leveling header exists twice:
|
|
||||||
// the one at 0x0 is the main one
|
|
||||||
// the one at 0x1000 is used as a failsafe if the one above is corrupt
|
|
||||||
if (InitSaveWearLeveling(cdata, 0) != 0) {
|
|
||||||
memset(&savectx, 0, sizeof(savectx));
|
|
||||||
if (InitSaveWearLeveling(cdata, 0x1000) != 0)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (InitCtrCardSaveCryptoKey(cdata) != 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 ReadDecryptedCard1Save(u8 *buffer, u64 offset, u64 count, CartData* cdata) {
|
|
||||||
if (offset >= savectx.logical_size) return 1;
|
|
||||||
if (offset + count > savectx.logical_size) return 1;
|
|
||||||
|
|
||||||
u32 first_sector = offset / 0x1000;
|
|
||||||
u32 last_sector = (offset + count - 1) / 0x1000;
|
|
||||||
u32 outbuf_offset = 0;
|
|
||||||
|
|
||||||
SaveBlockmapEntry ent;
|
|
||||||
u8 sectorbuf[0x1000];
|
|
||||||
u8 ctr[16];
|
|
||||||
memset(ctr, 0, sizeof(ctr));
|
|
||||||
|
|
||||||
u32 crypto_keyslot = savectx.crypto_type == CARD_SAVE_CRYPTO_V1_N3DS ? CARD_SAVE_CRYPTO_KEYSLOT_N3DS : CARD_SAVE_CRYPTO_KEYSLOT_O3DS;
|
|
||||||
use_aeskey(crypto_keyslot);
|
|
||||||
|
|
||||||
// the minimum one can read from the flash is 4K sectors anyway, and blockmap scatters it, so we do it sector by sector
|
|
||||||
for (u32 cur_sector = first_sector; cur_sector < last_sector + 1; cur_sector++) {
|
|
||||||
ent = GetBlockmapEntry(cur_sector);
|
|
||||||
|
|
||||||
// where in this sector we need to start reading data from
|
|
||||||
u32 in_sector_start = cur_sector == first_sector ? offset % 0x1000 : 0;
|
|
||||||
// how much data we can read in this sector assuming the start offset above
|
|
||||||
u32 in_sector_size = 0x1000 - in_sector_start;
|
|
||||||
// how much data we actually need to read from the sector
|
|
||||||
u32 chunksize = min(count, in_sector_size);
|
|
||||||
|
|
||||||
if (!ent.used) {
|
|
||||||
memset(§orbuf, 0xFF, sizeof(sectorbuf));
|
|
||||||
} else {
|
|
||||||
if (ReadCartSave(sectorbuf, ent.phys_sec * 0x1000, sizeof(sectorbuf), cdata) != 0)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savectx.repeating_ctr) {
|
|
||||||
for (u32 cursect = 0; cursect < 8; cursect++) {
|
|
||||||
memset(ctr, 0, sizeof(ctr));
|
|
||||||
ctr_decrypt_byte(§orbuf[0x200 * cursect], §orbuf[0x200 * cursect], 0x200, 0, AES_CNT_CART_SAVE_MODE, ctr);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctr_decrypt_byte(sectorbuf, sectorbuf, sizeof(sectorbuf), cur_sector * 0x1000, AES_CNT_CART_SAVE_MODE, ctr);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&buffer[outbuf_offset], §orbuf[in_sector_start], chunksize);
|
|
||||||
outbuf_offset += chunksize;
|
|
||||||
count -= chunksize;
|
|
||||||
offset += chunksize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 ReadDecryptedCard2Save(u8 *buffer, u64 offset, u64 count, CartData* cdata) {
|
|
||||||
if (offset >= cdata->save_size) return 1;
|
|
||||||
if (offset + count > cdata->save_size) count = cdata->save_size - offset;
|
|
||||||
|
|
||||||
u32 first_sector = offset / 0x200;
|
|
||||||
u32 outbuf_offset = 0;
|
|
||||||
|
|
||||||
u8 ctr[16];
|
|
||||||
memset(ctr, 0, sizeof(ctr));
|
|
||||||
|
|
||||||
u32 crypto_keyslot = savectx.crypto_type == CARD_SAVE_CRYPTO_V1_N3DS ? CARD_SAVE_CRYPTO_KEYSLOT_N3DS : CARD_SAVE_CRYPTO_KEYSLOT_O3DS;
|
|
||||||
use_aeskey(crypto_keyslot);
|
|
||||||
|
|
||||||
u8 sector_tmp[0x200];
|
|
||||||
|
|
||||||
// handle misalignment at the start
|
|
||||||
u32 in_first_sector_offset = offset % 0x200;
|
|
||||||
if (in_first_sector_offset) {
|
|
||||||
u32 sector_remain = 0x200 - in_first_sector_offset;
|
|
||||||
u32 misalignsize = min(sector_remain, count);
|
|
||||||
|
|
||||||
if (ReadCartSave(sector_tmp, first_sector * 0x200, sizeof(sector_tmp), cdata) != 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
ctr_decrypt_byte(sector_tmp, sector_tmp, sizeof(sector_tmp), first_sector * 0x200, AES_CNT_CART_SAVE_MODE, ctr);
|
|
||||||
memcpy(buffer, §or_tmp[in_first_sector_offset], misalignsize);
|
|
||||||
outbuf_offset += misalignsize;
|
|
||||||
offset += misalignsize;
|
|
||||||
count -= misalignsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// offset is now aligned, size may still not be, though
|
|
||||||
u32 aligned_size = count & ~(0x200-1);
|
|
||||||
if (aligned_size) {
|
|
||||||
if (ReadCartSave(&buffer[outbuf_offset], offset, aligned_size, cdata) != 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
ctr_decrypt_byte(&buffer[outbuf_offset], &buffer[outbuf_offset], aligned_size, offset, AES_CNT_CART_SAVE_MODE, ctr);
|
|
||||||
|
|
||||||
count -= aligned_size;
|
|
||||||
offset += aligned_size;
|
|
||||||
outbuf_offset += aligned_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// only ending misalignment remains, if applicable
|
|
||||||
if (count) {
|
|
||||||
if (ReadCartSave(sector_tmp, offset, sizeof(sector_tmp), cdata) != 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
ctr_decrypt_byte(sector_tmp, sector_tmp, sizeof(sector_tmp), offset, AES_CNT_CART_SAVE_MODE, ctr);
|
|
||||||
|
|
||||||
memcpy(&buffer[outbuf_offset], §or_tmp, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 ReadDecryptedCtrCardSave(u8* buffer, u64 offset, u64 count, CartData* cdata) {
|
|
||||||
switch (cdata->save_type) {
|
|
||||||
case CARD_SAVE_SPI:
|
|
||||||
return ReadDecryptedCard1Save(buffer, offset, count, cdata);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CARD_SAVE_CARD2:
|
|
||||||
return ReadDecryptedCard2Save(buffer, offset, count, cdata);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "gamecart.h"
|
|
||||||
|
|
||||||
u32 InitCtrCardSave(CartData *);
|
|
||||||
u32 ReadDecryptedCtrCardSave(u8* buffer, u64 offset, u64 count, CartData* cdata);
|
|
||||||
@ -3079,13 +3079,8 @@ u32 GodMode(int entrypoint) {
|
|||||||
if (!InitVCartDrive() && (pad_state & CART_INSERT) &&
|
if (!InitVCartDrive() && (pad_state & CART_INSERT) &&
|
||||||
(curr_drvtype & DRV_CART)) // reinit virtual cart drive
|
(curr_drvtype & DRV_CART)) // reinit virtual cart drive
|
||||||
ShowPrompt(false, "%s", STR_CART_INIT_FAILED);
|
ShowPrompt(false, "%s", STR_CART_INIT_FAILED);
|
||||||
// unmount any gamecart image if the gamecart was removed
|
if (!(*current_path) || (curr_drvtype & DRV_CART))
|
||||||
if (pad_state & CART_EJECT && *GetMountPath() && DriveType(GetMountPath()) & DRV_CART) {
|
|
||||||
InitImgFS(NULL);
|
|
||||||
}
|
|
||||||
if (!(*current_path) || (curr_drvtype & DRV_CART)) {
|
|
||||||
GetDirContents(current_dir, current_path); // refresh dir contents
|
GetDirContents(current_dir, current_path); // refresh dir contents
|
||||||
}
|
|
||||||
} else if (pad_state & SD_INSERT) {
|
} else if (pad_state & SD_INSERT) {
|
||||||
while (!InitSDCardFS() && ShowPrompt(true, "%s", STR_INITIALIZING_SD_FAILED_RETRY));
|
while (!InitSDCardFS() && ShowPrompt(true, "%s", STR_INITIALIZING_SD_FAILED_RETRY));
|
||||||
ClearScreenF(true, true, COLOR_STD_BG);
|
ClearScreenF(true, true, COLOR_STD_BG);
|
||||||
|
|||||||
@ -24,24 +24,24 @@
|
|||||||
u32 GetCbcBlocks(FIL* file, void* buffer, u64 offset, u32 count, u8* titlekey, u8* forced_iv) {
|
u32 GetCbcBlocks(FIL* file, void* buffer, u64 offset, u32 count, u8* titlekey, u8* forced_iv) {
|
||||||
u8 iv[16] __attribute__((aligned(4)));
|
u8 iv[16] __attribute__((aligned(4)));
|
||||||
UINT btr;
|
UINT btr;
|
||||||
|
|
||||||
// sanity, get IV
|
// sanity, get IV
|
||||||
if ((count % 0x10) || (offset % 0x10)) return 1; // not possible
|
if ((count % 0x10) || (offset % 0x10)) return 1; // not possible
|
||||||
if (forced_iv) memcpy(iv, forced_iv, 0x10);
|
if (forced_iv) memcpy(iv, forced_iv, 0x10);
|
||||||
else if ((offset < 0x10) || (fvx_lseek(file, offset - 0x10) != FR_OK) ||
|
else if ((offset < 0x10) || (fvx_lseek(file, offset - 0x10) != FR_OK) ||
|
||||||
(fvx_read(file, iv, 0x10, &btr) != FR_OK) || (btr != 0x10))
|
(fvx_read(file, iv, 0x10, &btr) != FR_OK) || (btr != 0x10))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// load data
|
// load data
|
||||||
if ((fvx_lseek(file, offset) != FR_OK) ||
|
if ((fvx_lseek(file, offset) != FR_OK) ||
|
||||||
(fvx_read(file, buffer, count, &btr) != FR_OK) || (btr != count))
|
(fvx_read(file, buffer, count, &btr) != FR_OK) || (btr != count))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// decrypt
|
// decrypt
|
||||||
if (titlekey) {
|
if (titlekey) {
|
||||||
setup_aeskey(0x11, titlekey);
|
setup_aeskey(0x11, titlekey);
|
||||||
use_aeskey(0x11);
|
use_aeskey(0x11);
|
||||||
cbc_decrypt(buffer, buffer, count / 0x10, AES_CNT_TITLEKEY_DECRYPT_MODE, iv);
|
cbc_decrypt(buffer, buffer, count / 0x10, AES_CNT_TITLEKEY_DECRYPT_MODE, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -231,7 +231,7 @@ u32 LoadTmdFile(TitleMetaData* tmd, const char* path) {
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// second part (read full size)
|
// second part (read full size)
|
||||||
if (ValidateTmd(tmd) == 0) {
|
if (ValidateTmd(tmd) == 0) {
|
||||||
if (fvx_qread(path, tmd, 0, TMD_SIZE_N(getbe16(tmd->content_count)), NULL) != FR_OK)
|
if (fvx_qread(path, tmd, 0, TMD_SIZE_N(getbe16(tmd->content_count)), NULL) != FR_OK)
|
||||||
return 1;
|
return 1;
|
||||||
} else if ((ValidateTwlTmd(tmd) == 0) && (getbe16(tmd->content_count) == 1)) {
|
} else if ((ValidateTwlTmd(tmd) == 0) && (getbe16(tmd->content_count) == 1)) {
|
||||||
@ -310,7 +310,7 @@ u32 LoadTicketForTitleId(Ticket** ticket, const u64 title_id) {
|
|||||||
char* path_bak = NULL;
|
char* path_bak = NULL;
|
||||||
strncpy(path_store, GetMountPath(), 256);
|
strncpy(path_store, GetMountPath(), 256);
|
||||||
if (*path_store) path_bak = path_store;
|
if (*path_store) path_bak = path_store;
|
||||||
|
|
||||||
// path to ticket.db
|
// path to ticket.db
|
||||||
char path_ticketdb[32];
|
char path_ticketdb[32];
|
||||||
char drv = *path_store;
|
char drv = *path_store;
|
||||||
@ -330,7 +330,7 @@ u32 LoadTicketForTitleId(Ticket** ticket, const u64 title_id) {
|
|||||||
|
|
||||||
u32 GetTmdContentPath(char* path_content, const char* path_tmd) {
|
u32 GetTmdContentPath(char* path_content, const char* path_tmd) {
|
||||||
// path_content should be 256 bytes in size!
|
// path_content should be 256 bytes in size!
|
||||||
|
|
||||||
// get path to TMD first content
|
// get path to TMD first content
|
||||||
static const u8 dlc_tid_high[] = { DLC_TID_HIGH };
|
static const u8 dlc_tid_high[] = { DLC_TID_HIGH };
|
||||||
|
|
||||||
@ -904,7 +904,7 @@ u32 VerifyTadFile(const char* path) {
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// verify contents
|
// verify contents
|
||||||
u32 content_start = sizeof(TadStub);
|
u32 content_start = sizeof(TadStub);
|
||||||
for (u32 i = 0; i < TAD_NUM_CONTENT; i++) {
|
for (u32 i = 0; i < TAD_NUM_CONTENT; i++) {
|
||||||
u8 hash[32];
|
u8 hash[32];
|
||||||
u32 len = align(hdr->content_size[i], 0x10);
|
u32 len = align(hdr->content_size[i], 0x10);
|
||||||
@ -1046,143 +1046,6 @@ u32 VerifyTicketFile(const char* path) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 LoadNcchFromTmdFile(const char *path, NcchHeader *ncch, NcchExtHeader *extheader) {
|
|
||||||
char pathstr[UTF_BUFFER_BYTESIZE(32)];
|
|
||||||
TruncateString(pathstr, path, 32, 8);
|
|
||||||
|
|
||||||
// content path
|
|
||||||
char path_content[256];
|
|
||||||
char *name_content; // points to content file name (%08lx)
|
|
||||||
strncpy(path_content, path, 256);
|
|
||||||
path_content[255] = '\0';
|
|
||||||
name_content = strrchr(path_content, '/');
|
|
||||||
if (!name_content) return 1;
|
|
||||||
name_content++;
|
|
||||||
|
|
||||||
TitleMetaData *tmd = (TitleMetaData*) malloc(TMD_SIZE_MAX);
|
|
||||||
TmdContentChunk *content_list = (TmdContentChunk*) (tmd + 1);
|
|
||||||
if ((LoadTmdFile(tmd, path) != 0) || (VerifyTmd(tmd) != 0)) {
|
|
||||||
ShowPrompt(false, "%s\n%s", pathstr, STR_ERROR_TMD_PROBABLY_CORRUPTED);
|
|
||||||
free(tmd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 titlekey[0x10] = { 0xFF };
|
|
||||||
Ticket* ticket = NULL;
|
|
||||||
if (!((LoadCdnTicketFile(&ticket, path) == 0) ||
|
|
||||||
((ticket = (Ticket*)malloc(TICKET_COMMON_SIZE), ticket != NULL) &&
|
|
||||||
(BuildFakeTicket(ticket, tmd->title_id) == 0) &&
|
|
||||||
(FindTitleKey(ticket, tmd->title_id) == 0))) ||
|
|
||||||
(GetTitleKey(titlekey, ticket) != 0)) {
|
|
||||||
ShowPrompt(false, "%s\n%s", pathstr, STR_ERROR_CDN_TITLEKEY_NOT_FOUND);
|
|
||||||
free(ticket);
|
|
||||||
free(tmd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
free(ticket);
|
|
||||||
|
|
||||||
u32 content_count = getbe16(tmd->content_count);
|
|
||||||
u32 res = 0;
|
|
||||||
bool found = false;
|
|
||||||
TmdContentChunk chunk = { 0 };
|
|
||||||
for (u32 i = 0; !res && i < content_count && (i < TMD_MAX_CONTENTS); i++) {
|
|
||||||
TmdContentChunk *tmp = &(content_list[i]);
|
|
||||||
if (getbe16(tmp->index) == 0) {
|
|
||||||
found = true;
|
|
||||||
memcpy(&chunk, tmp, sizeof(TmdContentChunk));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(tmd);
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// path to content with index 0
|
|
||||||
snprintf(name_content, 256 - (name_content - path_content), "%08lx", getbe32(chunk.id));
|
|
||||||
bool encrypted = getbe16(chunk.type) & 0x1;
|
|
||||||
u8 ctr[16];
|
|
||||||
|
|
||||||
GetTmdCtr(ctr, &chunk);
|
|
||||||
|
|
||||||
if (fvx_qread(path_content, ncch, 0, sizeof(NcchHeader), NULL) != FR_OK ||
|
|
||||||
(extheader && (fvx_qread(path_content, extheader, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), NULL) != FR_OK)))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (encrypted) {
|
|
||||||
DecryptCiaContentSequential(ncch, sizeof(NcchHeader), ctr, titlekey);
|
|
||||||
if (extheader)
|
|
||||||
DecryptCiaContentSequential(extheader, sizeof(NcchExtHeader), ctr, titlekey);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ValidateNcchHeader(ncch) != 0 ||
|
|
||||||
(extheader && (DecryptNcch(extheader, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), ncch, NULL) != 0)))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 LoadNcchFromGameFile(const char* path, NcchHeader* ncch, NcchExtHeader* extheader) {
|
|
||||||
u64 filetype = IdentifyFileType(path);
|
|
||||||
|
|
||||||
if (filetype & GAME_NCCH) {
|
|
||||||
if ((fvx_qread(path, ncch, 0, sizeof(NcchHeader), NULL) == FR_OK) &&
|
|
||||||
(ValidateNcchHeader(ncch) == 0) &&
|
|
||||||
(!extheader || ((fvx_qread(path, extheader, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), NULL) == FR_OK) &&
|
|
||||||
DecryptNcch(extheader, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), ncch, NULL) == 0))) return 0;
|
|
||||||
} else if (filetype & GAME_NCSD) {
|
|
||||||
if ((fvx_qread(path, ncch, NCSD_CNT0_OFFSET, sizeof(NcchHeader), NULL) == FR_OK) &&
|
|
||||||
(ValidateNcchHeader(ncch) == 0) &&
|
|
||||||
(!extheader || ((fvx_qread(path, extheader, NCSD_CINFO_OFFSET + NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), NULL) == 0) &&
|
|
||||||
DecryptNcch(extheader, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), ncch, NULL) == 0))) return 0;
|
|
||||||
} else if (filetype & GAME_CDNTMD) {
|
|
||||||
return LoadNcchFromTmdFile(path, ncch, extheader);
|
|
||||||
} else if (filetype & GAME_CIA) {
|
|
||||||
CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub));
|
|
||||||
CiaInfo info;
|
|
||||||
|
|
||||||
// load CIA stub from path
|
|
||||||
if ((LoadCiaStub(cia, path) != 0) ||
|
|
||||||
(GetCiaInfo(&info, &(cia->header)) != 0)) {
|
|
||||||
free(cia);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// decrypt / load NCCH header from first CIA content
|
|
||||||
u32 ret = 1;
|
|
||||||
if (getbe16(cia->tmd.content_count)) {
|
|
||||||
TmdContentChunk* chunk = cia->content_list;
|
|
||||||
if ((getbe64(chunk->size) < sizeof(NcchHeader)) ||
|
|
||||||
(fvx_qread(path, ncch, info.offset_content, sizeof(NcchHeader), NULL) != FR_OK) ||
|
|
||||||
(extheader && (fvx_qread(path, extheader, info.offset_content + NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), NULL) != FR_OK))) {
|
|
||||||
free(cia);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (getbe16(chunk->type) & 0x1) { // decrypt first content header
|
|
||||||
u8 titlekey[16];
|
|
||||||
u8 ctr[16];
|
|
||||||
GetTmdCtr(ctr, chunk);
|
|
||||||
if (GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0) {
|
|
||||||
free(cia);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
DecryptCiaContentSequential((void*) ncch, sizeof(NcchHeader), ctr, titlekey);
|
|
||||||
if (extheader)
|
|
||||||
DecryptCiaContentSequential((void*) extheader, sizeof(NcchExtHeader), ctr, titlekey);
|
|
||||||
}
|
|
||||||
if (ValidateNcchHeader(ncch) == 0 &&
|
|
||||||
(!extheader || DecryptNcch(extheader, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), ncch, NULL) == 0)) ret = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(cia);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 VerifyGameFile(const char* path, bool sig_check) {
|
u32 VerifyGameFile(const char* path, bool sig_check) {
|
||||||
u64 filetype = IdentifyFileType(path);
|
u64 filetype = IdentifyFileType(path);
|
||||||
if (filetype & GAME_CIA)
|
if (filetype & GAME_CIA)
|
||||||
@ -1646,28 +1509,7 @@ u32 CryptGameFile(const char* path, bool inplace, bool encrypt, bool restore) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetTwlInstallDataDrive(char *drv, bool to_emunand) {
|
u32 GetInstallDataDrive(char* drv, u64 tid64, bool to_emunand) {
|
||||||
drv[0] = to_emunand ? '5' : '2';
|
|
||||||
drv[1] = ':';
|
|
||||||
drv[2] = '\0';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetSdInstallDataDrive(char *drv, bool to_emunand) {
|
|
||||||
drv[0] = to_emunand ? 'B' : 'A';
|
|
||||||
drv[1] = ':';
|
|
||||||
drv[2] = '\0';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetNandInstallDataDrive(char *drv, bool to_emunand) {
|
|
||||||
drv[0] = to_emunand ? '4' : '1';
|
|
||||||
drv[1] = ':';
|
|
||||||
drv[2] = '\0';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetInstallDataDriveGeneric(char* drv, u64 tid64, bool to_emunand) {
|
|
||||||
// check the title id
|
// check the title id
|
||||||
bool to_twl = ((tid64 >> 32) & 0x8000);
|
bool to_twl = ((tid64 >> 32) & 0x8000);
|
||||||
bool to_sd = (!to_twl && !((tid64 >> 32) & 0x10));
|
bool to_sd = (!to_twl && !((tid64 >> 32) & 0x10));
|
||||||
@ -1675,8 +1517,14 @@ u32 GetInstallDataDriveGeneric(char* drv, u64 tid64, bool to_emunand) {
|
|||||||
// sanity
|
// sanity
|
||||||
if (!tid64) return 1;
|
if (!tid64) return 1;
|
||||||
|
|
||||||
return to_twl ? GetTwlInstallDataDrive(drv, to_emunand) :
|
// determine the correct drive
|
||||||
to_sd ? GetSdInstallDataDrive(drv, to_emunand) : GetNandInstallDataDrive(drv, to_emunand);
|
drv[0] = to_emunand ?
|
||||||
|
(to_twl ? '5' : to_sd ? 'B' : '4') :
|
||||||
|
(to_twl ? '2' : to_sd ? 'A' : '1');
|
||||||
|
drv[1] = ':';
|
||||||
|
drv[2] = '\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetInstallDbsPath(char* path, const char* drv, const char* str) {
|
u32 GetInstallDbsPath(char* path, const char* drv, const char* str) {
|
||||||
@ -1780,9 +1628,14 @@ u32 CreateSaveData(const char* drv, u64 tid64, const char* name, u32 save_size,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 UninstallGameData(const char *drv, u64 tid64, bool remove_tie, bool remove_ticket, bool remove_save) {
|
u32 UninstallGameData(u64 tid64, bool remove_tie, bool remove_ticket, bool remove_save, bool from_emunand) {
|
||||||
|
char drv[3];
|
||||||
|
|
||||||
// check permissions for SysNAND (this includes everything we need)
|
// check permissions for SysNAND (this includes everything we need)
|
||||||
if (!CheckWritePermissions(drv)) return 1;
|
if (!CheckWritePermissions(from_emunand ? "4:" : "1:")) return 1;
|
||||||
|
|
||||||
|
// determine the drive
|
||||||
|
if (GetInstallDataDrive(drv, tid64, from_emunand) != 0) return 1;
|
||||||
|
|
||||||
// remove data path
|
// remove data path
|
||||||
char path_data[256];
|
char path_data[256];
|
||||||
@ -1841,7 +1694,7 @@ u32 UninstallGameDataTie(const char* path, bool remove_tie, bool remove_ticket,
|
|||||||
u64 tid64;
|
u64 tid64;
|
||||||
|
|
||||||
const char* mntpath = GetMountPath();
|
const char* mntpath = GetMountPath();
|
||||||
if (!mntpath || !*mntpath) return 1;
|
if (!mntpath) return 1;
|
||||||
|
|
||||||
// title.db from emunand?
|
// title.db from emunand?
|
||||||
if ((strncasecmp(mntpath, "B:/dbs/title.db", 16) == 0) ||
|
if ((strncasecmp(mntpath, "B:/dbs/title.db", 16) == 0) ||
|
||||||
@ -1852,18 +1705,7 @@ u32 UninstallGameDataTie(const char* path, bool remove_tie, bool remove_ticket,
|
|||||||
if (sscanf(path, "T:/%016llx", &tid64) != 1)
|
if (sscanf(path, "T:/%016llx", &tid64) != 1)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// tid here is in the 00048000... format if it's a TWL title
|
return UninstallGameData(tid64, remove_tie, remove_ticket, remove_save, from_emunand);
|
||||||
bool is_twl = ((tid64 >> 32) & 0x8000) == 0x8000;
|
|
||||||
char drv[3] = { 0xFF, ':', '\0' };
|
|
||||||
|
|
||||||
// in all cases except for TWL titles, the uninstall drive is the same as the drive
|
|
||||||
// containing the .db file that is mounted.
|
|
||||||
if (is_twl)
|
|
||||||
drv[0] = from_emunand ? '5' : '2';
|
|
||||||
else
|
|
||||||
drv[0] = mntpath[0];
|
|
||||||
|
|
||||||
return UninstallGameData(drv, tid64, remove_tie, remove_ticket, remove_save);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 LoadEncryptedIconFromCiaTmd(const char* path, void* output, void* hdr, bool cia_meta) {
|
u32 LoadEncryptedIconFromCiaTmd(const char* path, void* output, void* hdr, bool cia_meta) {
|
||||||
@ -1881,7 +1723,7 @@ u32 LoadEncryptedIconFromCiaTmd(const char* path, void* output, void* hdr, bool
|
|||||||
|
|
||||||
void* data = (void*) malloc(max(sizeof(CiaStub), 0x1000));
|
void* data = (void*) malloc(max(sizeof(CiaStub), 0x1000));
|
||||||
if (!data) return 0;
|
if (!data) return 0;
|
||||||
|
|
||||||
// get content path/offset and TMD data
|
// get content path/offset and TMD data
|
||||||
TitleMetaData* tmd = (TitleMetaData*) data;
|
TitleMetaData* tmd = (TitleMetaData*) data;
|
||||||
Ticket* ticket = NULL;
|
Ticket* ticket = NULL;
|
||||||
@ -1902,7 +1744,7 @@ u32 LoadEncryptedIconFromCiaTmd(const char* path, void* output, void* hdr, bool
|
|||||||
free(data);
|
free(data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// title_id, titlekey & iv
|
// title_id, titlekey & iv
|
||||||
TmdContentChunk* chunk = (TmdContentChunk*) (tmd+1);
|
TmdContentChunk* chunk = (TmdContentChunk*) (tmd+1);
|
||||||
GetTmdCtr(iv, chunk);
|
GetTmdCtr(iv, chunk);
|
||||||
@ -1915,7 +1757,7 @@ u32 LoadEncryptedIconFromCiaTmd(const char* path, void* output, void* hdr, bool
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load first block of data
|
// load first block of data
|
||||||
FIL file;
|
FIL file;
|
||||||
if (fvx_open(&file, path_cnt, FA_READ | FA_OPEN_EXISTING) != FR_OK) {
|
if (fvx_open(&file, path_cnt, FA_READ | FA_OPEN_EXISTING) != FR_OK) {
|
||||||
@ -1927,7 +1769,7 @@ u32 LoadEncryptedIconFromCiaTmd(const char* path, void* output, void* hdr, bool
|
|||||||
free(data);
|
free(data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find out what it is and proceed
|
// find out what it is and proceed
|
||||||
u32 ret = 0;
|
u32 ret = 0;
|
||||||
if (!cia_meta && ValidateTwlHeader((TwlHeader*) data) == 0) {
|
if (!cia_meta && ValidateTwlHeader((TwlHeader*) data) == 0) {
|
||||||
@ -1945,7 +1787,7 @@ u32 LoadEncryptedIconFromCiaTmd(const char* path, void* output, void* hdr, bool
|
|||||||
if (cia_meta) {
|
if (cia_meta) {
|
||||||
CiaMeta* meta = (CiaMeta*) output;
|
CiaMeta* meta = (CiaMeta*) output;
|
||||||
NcchExtHeader* exthdr = (NcchExtHeader*) (void*) (((u8*)data) + NCCH_EXTHDR_OFFSET);
|
NcchExtHeader* exthdr = (NcchExtHeader*) (void*) (((u8*)data) + NCCH_EXTHDR_OFFSET);
|
||||||
if (!ncch->size_exthdr ||
|
if (!ncch->size_exthdr ||
|
||||||
(DecryptNcch(exthdr, NCCH_EXTHDR_OFFSET, NCCH_EXTHDR_SIZE, ncch, NULL) != 0) ||
|
(DecryptNcch(exthdr, NCCH_EXTHDR_OFFSET, NCCH_EXTHDR_SIZE, ncch, NULL) != 0) ||
|
||||||
(BuildCiaMeta(meta, exthdr, NULL) != 0)) {
|
(BuildCiaMeta(meta, exthdr, NULL) != 0)) {
|
||||||
fvx_close(&file);
|
fvx_close(&file);
|
||||||
@ -2351,7 +2193,7 @@ u32 BuildCiaLegitTicket(Ticket* ticket, u8* title_id, const char* path_cnt, bool
|
|||||||
bool src_emunand = ((*path_cnt == 'B') || (*path_cnt == '4'));
|
bool src_emunand = ((*path_cnt == 'B') || (*path_cnt == '4'));
|
||||||
|
|
||||||
if (BuildFakeTicket(ticket, title_id) != 0)
|
if (BuildFakeTicket(ticket, title_id) != 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (force_legit) {
|
if (force_legit) {
|
||||||
Ticket* ticket_tmp = NULL;
|
Ticket* ticket_tmp = NULL;
|
||||||
@ -2426,7 +2268,7 @@ u32 BuildCiaFromTadFile(const char* path_tad, const char* path_dest, bool force_
|
|||||||
if ((fvx_qread(path_tad, &tad, 0, sizeof(TadStub), NULL) != FR_OK) ||
|
if ((fvx_qread(path_tad, &tad, 0, sizeof(TadStub), NULL) != FR_OK) ||
|
||||||
(VerifyTadStub(&tad) != 0) || (hdr->content_size[0] < TMD_SIZE_N(1)))
|
(VerifyTadStub(&tad) != 0) || (hdr->content_size[0] < TMD_SIZE_N(1)))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// build the CIA stub
|
// build the CIA stub
|
||||||
CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub));
|
CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub));
|
||||||
if (!cia) return 1;
|
if (!cia) return 1;
|
||||||
@ -2443,7 +2285,7 @@ u32 BuildCiaFromTadFile(const char* path_tad, const char* path_dest, bool force_
|
|||||||
free(cia);
|
free(cia);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract info from TMD
|
// extract info from TMD
|
||||||
u32 content_count = getbe16(tmd->content_count);
|
u32 content_count = getbe16(tmd->content_count);
|
||||||
u8* title_id = tmd->title_id;
|
u8* title_id = tmd->title_id;
|
||||||
@ -2451,7 +2293,7 @@ u32 BuildCiaFromTadFile(const char* path_tad, const char* path_dest, bool force_
|
|||||||
free(cia);
|
free(cia);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for legit TMD
|
// check for legit TMD
|
||||||
if (force_legit && ((ValidateTmdSignature(tmd) != 0) || VerifyTmd(tmd) != 0)) {
|
if (force_legit && ((ValidateTmdSignature(tmd) != 0) || VerifyTmd(tmd) != 0)) {
|
||||||
ShowPrompt(false, STR_ID_N_TMD_IN_TAD_NOT_LEGIT, getbe64(title_id));
|
ShowPrompt(false, STR_ID_N_TMD_IN_TAD_NOT_LEGIT, getbe64(title_id));
|
||||||
@ -2478,7 +2320,7 @@ u32 BuildCiaFromTadFile(const char* path_tad, const char* path_dest, bool force_
|
|||||||
free(cia);
|
free(cia);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to find a titlekey
|
// attempt to find a titlekey
|
||||||
u8 titlekey[16] = { 0xFF };
|
u8 titlekey[16] = { 0xFF };
|
||||||
FindTitleKey((Ticket*) ticket, title_id);
|
FindTitleKey((Ticket*) ticket, title_id);
|
||||||
@ -2948,7 +2790,7 @@ u32 GetGameFileTitleVersion(const char* path) {
|
|||||||
if (cia && LoadCiaStub(cia, path) == 0)
|
if (cia && LoadCiaStub(cia, path) == 0)
|
||||||
version = getbe16(cia->tmd.title_version);
|
version = getbe16(cia->tmd.title_version);
|
||||||
if (cia) free(cia);
|
if (cia) free(cia);
|
||||||
}
|
}
|
||||||
|
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
@ -3027,51 +2869,14 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetInstallDataDriveForGameFile(const char *path, u64 filetype, u64 titleid, char *drv, bool to_emunand) {
|
|
||||||
// CTR filetypes
|
|
||||||
if (filetype & (GAME_CIA | GAME_NCSD | GAME_NCCH | GAME_CDNTMD)) {
|
|
||||||
if ((filetype & GAME_CIA) && (titleid >> 32) & 0x8000) {
|
|
||||||
// only CIAs may be either CTR or TWL titles
|
|
||||||
GetTwlInstallDataDrive(drv, to_emunand);
|
|
||||||
} else if ((titleid >> 32) == 0x0004008C || (filetype & GAME_NCSD)) {
|
|
||||||
// DLC, although it does not contain a CXI, must be installed to SD always
|
|
||||||
// gamecarts, and backups thereof, are also always installed to SD
|
|
||||||
GetSdInstallDataDrive(drv, to_emunand);
|
|
||||||
} else {
|
|
||||||
// definitely a CTR title
|
|
||||||
NcchHeader ncch;
|
|
||||||
NcchExtHeader extheader;
|
|
||||||
|
|
||||||
if (LoadNcchFromGameFile(path, &ncch, &extheader) != 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (!NCCH_IS_CXI(&ncch)) {
|
|
||||||
// for CFA, we don't have much to go off of. but CFAs can just be separated by the system title bit,
|
|
||||||
// as most CFAs are either part of system titles (NAND) or instruction manuals (SD).
|
|
||||||
if (GetInstallDataDriveGeneric(drv, titleid, to_emunand) != 0)
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
// for files that are CXIs or files whose first content is a CXI, we can check the exheader for the SD bit
|
|
||||||
if (extheader.flag & BIT(1))
|
|
||||||
GetSdInstallDataDrive(drv, to_emunand);
|
|
||||||
else
|
|
||||||
GetNandInstallDataDrive(drv, to_emunand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // TWL file types (GAME_NDS, GAME_TWLTMD)
|
|
||||||
GetTwlInstallDataDrive(drv, to_emunand);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 InstallGameFile(const char* path, bool to_emunand) {
|
u32 InstallGameFile(const char* path, bool to_emunand) {
|
||||||
char drv[3];
|
char drv[3];
|
||||||
u64 filetype = IdentifyFileType(path);
|
u64 filetype = IdentifyFileType(path);
|
||||||
u64 tid64 = GetGameFileTitleId(path);
|
|
||||||
u32 ret = 0;
|
u32 ret = 0;
|
||||||
|
|
||||||
if (GetInstallDataDriveForGameFile(path, filetype, tid64, drv, to_emunand) != 0)
|
// find out the destination
|
||||||
|
u64 tid64 = GetGameFileTitleId(path);
|
||||||
|
if (GetInstallDataDrive(drv, tid64, to_emunand) != 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// check dbs
|
// check dbs
|
||||||
@ -3088,7 +2893,7 @@ u32 InstallGameFile(const char* path, bool to_emunand) {
|
|||||||
|
|
||||||
// cleanup content folder before starting install
|
// cleanup content folder before starting install
|
||||||
ShowProgress(0, 0, path);
|
ShowProgress(0, 0, path);
|
||||||
UninstallGameData(drv, tid64, false, false, false);
|
UninstallGameData(tid64, false, false, false, to_emunand);
|
||||||
|
|
||||||
// install game file
|
// install game file
|
||||||
if (filetype & GAME_CIA)
|
if (filetype & GAME_CIA)
|
||||||
@ -3104,7 +2909,7 @@ u32 InstallGameFile(const char* path, bool to_emunand) {
|
|||||||
else ret = 1;
|
else ret = 1;
|
||||||
|
|
||||||
// cleanup on failed installs, but leave ticket and save untouched
|
// cleanup on failed installs, but leave ticket and save untouched
|
||||||
if (ret != 0) UninstallGameData(drv, tid64, true, false, false);
|
if (ret != 0) UninstallGameData(tid64, true, false, false, to_emunand);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -3263,7 +3068,7 @@ u32 DumpTicketForGameFile(const char* path, bool force_legit) {
|
|||||||
free(ticket);
|
free(ticket);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// build output name
|
// build output name
|
||||||
char dest[256];
|
char dest[256];
|
||||||
snprintf(dest, sizeof(dest), OUTPUT_PATH "/");
|
snprintf(dest, sizeof(dest), OUTPUT_PATH "/");
|
||||||
@ -3326,7 +3131,7 @@ u32 DumpCxiSrlFromGameFile(const char* path) {
|
|||||||
return 1;
|
return 1;
|
||||||
path_tmd = path_data;
|
path_tmd = path_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DumpCxiSrlFromTmdFile(path_tmd);
|
return DumpCxiSrlFromTmdFile(path_tmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3463,7 +3268,7 @@ u32 CompressCode(const char* path, const char* path_out) {
|
|||||||
u64 GetAnyFileTrimmedSize(const char* path) {
|
u64 GetAnyFileTrimmedSize(const char* path) {
|
||||||
u64 fsize = 0;
|
u64 fsize = 0;
|
||||||
u64 trimsize = 0;
|
u64 trimsize = 0;
|
||||||
u8 pad_byte = 0x7F;
|
u8 pad_byte = 0x7F;
|
||||||
FIL fp;
|
FIL fp;
|
||||||
UINT br;
|
UINT br;
|
||||||
|
|
||||||
@ -3708,7 +3513,7 @@ u32 ShowGameCheckerInfo(const char* path) {
|
|||||||
content_found = content_count = getbe16(tmd->content_count);
|
content_found = content_count = getbe16(tmd->content_count);
|
||||||
sd_title = (*path_tmd == 'A') || (*path_tmd == 'B');
|
sd_title = (*path_tmd == 'A') || (*path_tmd == 'B');
|
||||||
}
|
}
|
||||||
|
|
||||||
// load ticket
|
// load ticket
|
||||||
u64 tid64 = tmd ? getbe64(tmd->title_id) : 0;
|
u64 tid64 = tmd ? getbe64(tmd->title_id) : 0;
|
||||||
if (LoadTicketForTitleId(&ticket, tid64) != 0)
|
if (LoadTicketForTitleId(&ticket, tid64) != 0)
|
||||||
@ -3747,7 +3552,7 @@ u32 ShowGameCheckerInfo(const char* path) {
|
|||||||
memcpy(tmd, &(cia->tmd), cia->header.size_tmd);
|
memcpy(tmd, &(cia->tmd), cia->header.size_tmd);
|
||||||
ticket = (Ticket*) malloc(cia->header.size_ticket);
|
ticket = (Ticket*) malloc(cia->header.size_ticket);
|
||||||
if (ticket) memcpy(ticket, &(cia->ticket), cia->header.size_ticket);
|
if (ticket) memcpy(ticket, &(cia->ticket), cia->header.size_ticket);
|
||||||
|
|
||||||
free(cia);
|
free(cia);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4049,7 +3854,7 @@ u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump) {
|
|||||||
u32 num_entries = 0;
|
u32 num_entries = 0;
|
||||||
u8* title_ids = NULL;
|
u8* title_ids = NULL;
|
||||||
|
|
||||||
if (!InitImgFS(path_in) ||
|
if (!InitImgFS(path_in) ||
|
||||||
!(num_entries = GetNumTickets(PART_PATH)) ||
|
!(num_entries = GetNumTickets(PART_PATH)) ||
|
||||||
!(title_ids = (u8*) malloc(num_entries * 8)) ||
|
!(title_ids = (u8*) malloc(num_entries * 8)) ||
|
||||||
(ListTicketTitleIDs(PART_PATH, title_ids, num_entries) != 0)) {
|
(ListTicketTitleIDs(PART_PATH, title_ids, num_entries) != 0)) {
|
||||||
@ -4066,7 +3871,7 @@ u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump) {
|
|||||||
AddTicketToInfo(tik_info, ticket, dec); // ignore result
|
AddTicketToInfo(tik_info, ticket, dec); // ignore result
|
||||||
free(ticket);
|
free(ticket);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(title_ids);
|
free(title_ids);
|
||||||
InitImgFS(NULL);
|
InitImgFS(NULL);
|
||||||
} else if (filetype & BIN_TIKDB) {
|
} else if (filetype & BIN_TIKDB) {
|
||||||
@ -4203,6 +4008,54 @@ u32 BuildSeedInfo(const char* path, bool dump) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 LoadNcchFromGameFile(const char* path, NcchHeader* ncch) {
|
||||||
|
u64 filetype = IdentifyFileType(path);
|
||||||
|
|
||||||
|
if (filetype & GAME_NCCH) {
|
||||||
|
if ((fvx_qread(path, ncch, 0, sizeof(NcchHeader), NULL) == FR_OK) &&
|
||||||
|
(ValidateNcchHeader(ncch) == 0)) return 0;
|
||||||
|
} else if (filetype & GAME_NCSD) {
|
||||||
|
if ((fvx_qread(path, ncch, NCSD_CNT0_OFFSET, sizeof(NcchHeader), NULL) == FR_OK) &&
|
||||||
|
(ValidateNcchHeader(ncch) == 0)) return 0;
|
||||||
|
} else if (filetype & GAME_CIA) {
|
||||||
|
CiaStub* cia = (CiaStub*) malloc(sizeof(CiaStub));
|
||||||
|
CiaInfo info;
|
||||||
|
|
||||||
|
// load CIA stub from path
|
||||||
|
if ((LoadCiaStub(cia, path) != 0) ||
|
||||||
|
(GetCiaInfo(&info, &(cia->header)) != 0)) {
|
||||||
|
free(cia);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrypt / load NCCH header from first CIA content
|
||||||
|
u32 ret = 1;
|
||||||
|
if (getbe16(cia->tmd.content_count)) {
|
||||||
|
TmdContentChunk* chunk = cia->content_list;
|
||||||
|
if ((getbe64(chunk->size) < sizeof(NcchHeader)) ||
|
||||||
|
(fvx_qread(path, ncch, info.offset_content, sizeof(NcchHeader), NULL) != FR_OK)) {
|
||||||
|
free(cia);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (getbe16(chunk->type) & 0x1) { // decrypt first content header
|
||||||
|
u8 titlekey[16];
|
||||||
|
u8 ctr[16];
|
||||||
|
GetTmdCtr(ctr, chunk);
|
||||||
|
if (GetTitleKey(titlekey, (Ticket*)&(cia->ticket)) != 0) {
|
||||||
|
free(cia);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
DecryptCiaContentSequential((void*) ncch, sizeof(NcchHeader), ctr, titlekey);
|
||||||
|
}
|
||||||
|
if (ValidateNcchHeader(ncch) == 0) ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(cia);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
u32 GetGoodName(char* name, const char* path, bool quick) {
|
u32 GetGoodName(char* name, const char* path, bool quick) {
|
||||||
// name should be 128+1 byte
|
// name should be 128+1 byte
|
||||||
@ -4264,7 +4117,7 @@ u32 GetGoodName(char* name, const char* path, bool quick) {
|
|||||||
if (LoadTwlMetaData(path_donor, (TwlHeader*) header,
|
if (LoadTwlMetaData(path_donor, (TwlHeader*) header,
|
||||||
quick ? NULL : (TwlIconData*) icon) != 0) type_donor = 0;
|
quick ? NULL : (TwlIconData*) icon) != 0) type_donor = 0;
|
||||||
} else if (type_donor & (GAME_NCSD|GAME_NCCH)) { // CTR (data from NCCH)
|
} else if (type_donor & (GAME_NCSD|GAME_NCCH)) { // CTR (data from NCCH)
|
||||||
if (LoadNcchFromGameFile(path_donor, (NcchHeader*) header, NULL) != 0) type_donor = 0;
|
if (LoadNcchFromGameFile(path_donor, (NcchHeader*) header) != 0) type_donor = 0;
|
||||||
if (!quick && (LoadSmdhFromGameFile(path_donor, (Smdh*) icon) != 0)) quick = true;
|
if (!quick && (LoadSmdhFromGameFile(path_donor, (Smdh*) icon) != 0)) quick = true;
|
||||||
} else if (type_donor & (GAME_CIA|GAME_CDNTMD|GAME_TWLTMD)) { // encrypted
|
} else if (type_donor & (GAME_CIA|GAME_CDNTMD|GAME_TWLTMD)) { // encrypted
|
||||||
type_donor = LoadEncryptedIconFromCiaTmd(path_donor, icon, header, false);
|
type_donor = LoadEncryptedIconFromCiaTmd(path_donor, icon, header, false);
|
||||||
|
|||||||
@ -1,21 +1,15 @@
|
|||||||
#include "vcart.h"
|
#include "vcart.h"
|
||||||
#include "fsdrive.h"
|
|
||||||
#include "fsinit.h"
|
|
||||||
#include "gamecart.h"
|
#include "gamecart.h"
|
||||||
#include "image.h"
|
|
||||||
#include "save_ctr.h"
|
|
||||||
|
|
||||||
#define FAT_LIMIT 0x100000000
|
#define FAT_LIMIT 0x100000000
|
||||||
#define VFLAG_DECRYPTED_SAVEGAME (1UL<<27)
|
#define VFLAG_SECURE_AREA_ENC (1UL<<28)
|
||||||
#define VFLAG_SECURE_AREA_ENC (1UL<<28)
|
#define VFLAG_GAMECART_NFO (1UL<<29)
|
||||||
#define VFLAG_GAMECART_NFO (1UL<<29)
|
#define VFLAG_SAVEGAME (1UL<<30)
|
||||||
#define VFLAG_SAVEGAME (1UL<<30)
|
#define VFLAG_PRIV_HDR (1UL<<31)
|
||||||
#define VFLAG_PRIV_HDR (1UL<<31)
|
|
||||||
|
|
||||||
static CartData* cdata = NULL;
|
static CartData* cdata = NULL;
|
||||||
static bool cart_init = false;
|
static bool cart_init = false;
|
||||||
static bool cart_checked = false;
|
static bool cart_checked = false;
|
||||||
static bool enable_dec_ctr_save = false;
|
|
||||||
|
|
||||||
u32 InitVCartDrive(void) {
|
u32 InitVCartDrive(void) {
|
||||||
if (!cart_checked) cart_checked = true;
|
if (!cart_checked) cart_checked = true;
|
||||||
@ -25,10 +19,6 @@ u32 InitVCartDrive(void) {
|
|||||||
free(cdata);
|
free(cdata);
|
||||||
cdata = NULL;
|
cdata = NULL;
|
||||||
}
|
}
|
||||||
if (cart_init && (cdata->cart_type & CART_CTR)) {
|
|
||||||
// for compatibility purposes save crypto and wear leveling init are optional
|
|
||||||
enable_dec_ctr_save = InitCtrCardSave(cdata) == 0;
|
|
||||||
}
|
|
||||||
return cart_init ? cdata->cart_id : 0;
|
return cart_init ? cdata->cart_id : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +34,7 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) {
|
|||||||
vfile->keyslot = 0xFF; // unused
|
vfile->keyslot = 0xFF; // unused
|
||||||
vfile->flags = VFLAG_READONLY;
|
vfile->flags = VFLAG_READONLY;
|
||||||
|
|
||||||
while (++vdir->index <= 10) {
|
while (++vdir->index <= 9) {
|
||||||
if ((vdir->index == 0) && (cdata->data_size < FAT_LIMIT)) { // standard full rom
|
if ((vdir->index == 0) && (cdata->data_size < FAT_LIMIT)) { // standard full rom
|
||||||
snprintf(vfile->name, 32, "%s.%s", name, ext);
|
snprintf(vfile->name, 32, "%s.%s", name, ext);
|
||||||
vfile->size = cdata->cart_size;
|
vfile->size = cdata->cart_size;
|
||||||
@ -83,16 +73,11 @@ bool ReadVCartDir(VirtualFile* vfile, VirtualDir* vdir) {
|
|||||||
vfile->flags |= VFLAG_READONLY;
|
vfile->flags |= VFLAG_READONLY;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if ((vdir->index == 8) && enable_dec_ctr_save) {
|
} else if (vdir->index == 8) { // gamecart info
|
||||||
snprintf(vfile->name, 32, "%s.dec.sav", name);
|
char info[256];
|
||||||
vfile->size = (cdata->cart_id & 0x8000000) /* card2 */ ? cdata->save_size : cdata->save_size - 0x2000;
|
|
||||||
vfile->flags = VFLAG_DECRYPTED_SAVEGAME | VFLAG_READONLY /* for now */;
|
|
||||||
return true;
|
|
||||||
} else if (vdir->index == 9) { // gamecart info
|
|
||||||
char info[301];
|
|
||||||
GetCartInfoString(info, sizeof(info), cdata);
|
GetCartInfoString(info, sizeof(info), cdata);
|
||||||
snprintf(vfile->name, 32, "%s.txt", name);
|
snprintf(vfile->name, 32, "%s.txt", name);
|
||||||
vfile->size = strnlen(info, 300);
|
vfile->size = strnlen(info, 255);
|
||||||
vfile->flags |= VFLAG_GAMECART_NFO;
|
vfile->flags |= VFLAG_GAMECART_NFO;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -109,8 +94,6 @@ int ReadVCartFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count)
|
|||||||
return ReadCartPrivateHeader(buffer, foffset, count, cdata);
|
return ReadCartPrivateHeader(buffer, foffset, count, cdata);
|
||||||
else if (vfile->flags & VFLAG_SAVEGAME)
|
else if (vfile->flags & VFLAG_SAVEGAME)
|
||||||
return ReadCartSave(buffer, foffset, count, cdata);
|
return ReadCartSave(buffer, foffset, count, cdata);
|
||||||
else if (vfile->flags & VFLAG_DECRYPTED_SAVEGAME)
|
|
||||||
return ReadDecryptedCtrCardSave(buffer, foffset, count, cdata);
|
|
||||||
else if (vfile->flags & VFLAG_GAMECART_NFO)
|
else if (vfile->flags & VFLAG_GAMECART_NFO)
|
||||||
return ReadCartInfo(buffer, foffset, count, cdata);
|
return ReadCartInfo(buffer, foffset, count, cdata);
|
||||||
|
|
||||||
@ -121,15 +104,7 @@ int ReadVCartFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count)
|
|||||||
int WriteVCartFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count) {
|
int WriteVCartFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count) {
|
||||||
if (!cdata) return -1;
|
if (!cdata) return -1;
|
||||||
if (vfile->flags & VFLAG_SAVEGAME) {
|
if (vfile->flags & VFLAG_SAVEGAME) {
|
||||||
int res = WriteCartSave(buffer, offset, count, cdata);
|
return WriteCartSave(buffer, offset, count, cdata);
|
||||||
if (cdata->cart_type & CART_CTR) {
|
|
||||||
enable_dec_ctr_save = InitCtrCardSave(cdata) == 0;
|
|
||||||
if (*GetMountPath() && !enable_dec_ctr_save && (DriveType(GetMountPath()) & DRV_CART) && strstr(GetMountPath(), ".sav")) {
|
|
||||||
// unmount the virtual DISA archive if user invalidated the encrypted and wear-leveled source
|
|
||||||
InitImgFS(NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -191,7 +191,7 @@
|
|||||||
"CALCULATE_SHA256": "SHA-256 berechnen",
|
"CALCULATE_SHA256": "SHA-256 berechnen",
|
||||||
"CALCULATE_SHA1": "SHA-1 berechnen",
|
"CALCULATE_SHA1": "SHA-1 berechnen",
|
||||||
"SHOW_FILE_INFO": "Datei-Info anzeigen",
|
"SHOW_FILE_INFO": "Datei-Info anzeigen",
|
||||||
"SHOW_IN_TEXTVIEWER": "Im Texteditor anzeigen",
|
"SHOW_IN_TEXTVIEWER": "Show in Text Editor",
|
||||||
"CALCULATE_CMAC": "CMAC berechnen",
|
"CALCULATE_CMAC": "CMAC berechnen",
|
||||||
"COPY_TO_OUT": "Nach %s kopieren",
|
"COPY_TO_OUT": "Nach %s kopieren",
|
||||||
"DUMP_TO_OUT": "Unter %s dumpen",
|
"DUMP_TO_OUT": "Unter %s dumpen",
|
||||||
@ -778,7 +778,7 @@
|
|||||||
"SCRIPTERR_APPLY_IPS_FAILD": "IPS anwenden fehlgeschlagen",
|
"SCRIPTERR_APPLY_IPS_FAILD": "IPS anwenden fehlgeschlagen",
|
||||||
"SCRIPTERR_APPLY_BPS_FAILED": "BPS anwenden fehlgeschlagen",
|
"SCRIPTERR_APPLY_BPS_FAILED": "BPS anwenden fehlgeschlagen",
|
||||||
"SCRIPTERR_APPLY_BPM_FAILED": "BPM anwenden fehlgeschlagen",
|
"SCRIPTERR_APPLY_BPM_FAILED": "BPM anwenden fehlgeschlagen",
|
||||||
"SCRIPTERR_TEXTVIEWER_FAILED": "Texteditor fehlgeschlagen",
|
"SCRIPTERR_TEXTVIEWER_FAILED": "text editor failed",
|
||||||
"SCRIPTERR_BAD_DUMPSIZE": "schlechte Dumpinggröße",
|
"SCRIPTERR_BAD_DUMPSIZE": "schlechte Dumpinggröße",
|
||||||
"SCRIPTERR_CART_INIT_FAIL": "karteninitialisierung fehlgeschlagen",
|
"SCRIPTERR_CART_INIT_FAIL": "karteninitialisierung fehlgeschlagen",
|
||||||
"SCRIPTERR_CART_DUMP_FAILED": "karteninitialisierung fehlgeschlagen",
|
"SCRIPTERR_CART_DUMP_FAILED": "karteninitialisierung fehlgeschlagen",
|
||||||
@ -792,12 +792,12 @@
|
|||||||
"SCRIPTERR_UNCLOSED_CONDITIONAL": "ungeschlossene Bedingung",
|
"SCRIPTERR_UNCLOSED_CONDITIONAL": "ungeschlossene Bedingung",
|
||||||
"SCRIPTERR_ERROR_MESSAGE_FAIL": "Fehlermeldung fehlgeschlagen",
|
"SCRIPTERR_ERROR_MESSAGE_FAIL": "Fehlermeldung fehlgeschlagen",
|
||||||
"ERROR_INVALID_TEXT_DATA": "Fehler: Ungültige Textdaten",
|
"ERROR_INVALID_TEXT_DATA": "Fehler: Ungültige Textdaten",
|
||||||
"ERROR_TEXT_FILE_TOO_BIG": "Fehler: Textdatei ist zu groß.\nTextdatei ist %u Bytes.\nMaximale Dateigröße ist %i Bytes.",
|
"ERROR_TEXT_FILE_TOO_BIG": "Error: Text file is too large.\nText file size is %u bytes.\nMax file size is %i bytes.",
|
||||||
"TEXTVIEWER_CONTROLS_DETAILS": "Textviewer-Steuerelemente:\n \n↑↓→←(+R) - Scrollen\nR+Y - Wortumbruch umschalten\nR+X - Gehe zu Zeile #\nB - Beenden\n",
|
"TEXTVIEWER_CONTROLS_DETAILS": "Textviewer-Steuerelemente:\n \n↑↓→←(+R) - Scrollen\nR+Y - Wortumbruch umschalten\nR+X - Gehe zu Zeile #\nB - Beenden\n",
|
||||||
"TEXTEDITOR_CONTROLS_DETAILS": "Text-Editor-Steuerelemente:\n \n↑↓→←(+R) - Scrollen\nR+Y - Wortumbruch umschalten\nR+X - zu Zeile #\nA - Bearbeitungsmodus aufrufen\nB - Beenden\n",
|
"TEXTEDITOR_CONTROLS_DETAILS": "Text Editor Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nA - Enter edit mode\nB - Exit\n",
|
||||||
"TEXTEDITOR_CONTROLS_KEYBOARD": "Text-Editor-Bedienelemente:\n \n↑↓→←(+R) - Cursor bewegen\nX - Zeichen löschen\nA - Zeilenumbruch einfügen\nL+↑↓→← - Text auswählen\nY - KOPIEREN / [+R] SCHNEIDEN\nB - Ansichtsmodus aufrufen\n",
|
"TEXTEDITOR_CONTROLS_KEYBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nX - Delete char\nA - Insert newline\nL+↑↓→← - Select text\nY - COPY / [+R] CUT\nB - Enter view mode\n",
|
||||||
"TEXTEDITOR_CONTROLS_CLIPBOARD": "Text-Editor-Bedienelemente:\n \n↑↓→←(+R) - Cursor bewegen\nX - Zeichen löschen\nA - Zeilenumbruch einfügen\nL+↑↓→← - Text auswählen\nY - EINFÜGEN / [+R] LÖSCHEN\nB - Ansichtsmodus aufrufen\n",
|
"TEXTEDITOR_CONTROLS_CLIPBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nX - Delete char\nA - Insert newline\nL+↑↓→← - Select text\nY - PASTE / [+R] CLEAR\nB - Enter view mode\n",
|
||||||
"TEXT_EDITS_SAVE_CHANGES": "Sie haben Text bearbeitet.\nÄnderungen in die Datei schreiben?",
|
"TEXT_EDITS_SAVE_CHANGES": "You made text edits.\nWrite changes to file?",
|
||||||
"CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "Aktuelle Zeile: %i\nGeben Sie unten eine neue Zeile ein.",
|
"CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "Aktuelle Zeile: %i\nGeben Sie unten eine neue Zeile ein.",
|
||||||
"PREVIEW_DISABLED": "(Vorschau deaktiviert)",
|
"PREVIEW_DISABLED": "(Vorschau deaktiviert)",
|
||||||
"PATH_LINE_N_ERR_LINE": "%s\nZeile %lu: %s\n%s",
|
"PATH_LINE_N_ERR_LINE": "%s\nZeile %lu: %s\n%s",
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"GM9_LANGUAGE": "English",
|
"GM9_LANGUAGE": "English",
|
||||||
"GM9_TRANS_VER": 2
|
"GM9_TRANS_VER": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@ -191,7 +191,7 @@
|
|||||||
"CALCULATE_SHA256": "Calcular SHA-256",
|
"CALCULATE_SHA256": "Calcular SHA-256",
|
||||||
"CALCULATE_SHA1": "Calcular SHA-1",
|
"CALCULATE_SHA1": "Calcular SHA-1",
|
||||||
"SHOW_FILE_INFO": "Mostrar información del archivo",
|
"SHOW_FILE_INFO": "Mostrar información del archivo",
|
||||||
"SHOW_IN_TEXTVIEWER": "Mostrar en el Editor de Texto",
|
"SHOW_IN_TEXTVIEWER": "Show in Text Editor",
|
||||||
"CALCULATE_CMAC": "Calcular CMAC",
|
"CALCULATE_CMAC": "Calcular CMAC",
|
||||||
"COPY_TO_OUT": "Copiar en %s",
|
"COPY_TO_OUT": "Copiar en %s",
|
||||||
"DUMP_TO_OUT": "Volcar en %s",
|
"DUMP_TO_OUT": "Volcar en %s",
|
||||||
@ -778,7 +778,7 @@
|
|||||||
"SCRIPTERR_APPLY_IPS_FAILD": "error al aplicar IPS",
|
"SCRIPTERR_APPLY_IPS_FAILD": "error al aplicar IPS",
|
||||||
"SCRIPTERR_APPLY_BPS_FAILED": "error al aplicar BPS",
|
"SCRIPTERR_APPLY_BPS_FAILED": "error al aplicar BPS",
|
||||||
"SCRIPTERR_APPLY_BPM_FAILED": "error al aplicar BPM",
|
"SCRIPTERR_APPLY_BPM_FAILED": "error al aplicar BPM",
|
||||||
"SCRIPTERR_TEXTVIEWER_FAILED": "Error en el editor de texto",
|
"SCRIPTERR_TEXTVIEWER_FAILED": "text editor failed",
|
||||||
"SCRIPTERR_BAD_DUMPSIZE": "volcado incorrecto",
|
"SCRIPTERR_BAD_DUMPSIZE": "volcado incorrecto",
|
||||||
"SCRIPTERR_CART_INIT_FAIL": "error al iniciar el cartucho",
|
"SCRIPTERR_CART_INIT_FAIL": "error al iniciar el cartucho",
|
||||||
"SCRIPTERR_CART_DUMP_FAILED": "error al volcar cartucho",
|
"SCRIPTERR_CART_DUMP_FAILED": "error al volcar cartucho",
|
||||||
@ -792,12 +792,12 @@
|
|||||||
"SCRIPTERR_UNCLOSED_CONDITIONAL": "condicional no cerrado",
|
"SCRIPTERR_UNCLOSED_CONDITIONAL": "condicional no cerrado",
|
||||||
"SCRIPTERR_ERROR_MESSAGE_FAIL": "mensaje de error fallido",
|
"SCRIPTERR_ERROR_MESSAGE_FAIL": "mensaje de error fallido",
|
||||||
"ERROR_INVALID_TEXT_DATA": "Error: datos de texto no válidos",
|
"ERROR_INVALID_TEXT_DATA": "Error: datos de texto no válidos",
|
||||||
"ERROR_TEXT_FILE_TOO_BIG": "Error: Archivo de texto muy grande.\nTamaño del archivo de texto: %u bytes.\nTamaño máximo del archivo: %i bytes.",
|
"ERROR_TEXT_FILE_TOO_BIG": "Error: Text file is too large.\nText file size is %u bytes.\nMax file size is %i bytes.",
|
||||||
"TEXTVIEWER_CONTROLS_DETAILS": "Controles de Textviewer:\n \n↑↓→←(+R) - Desplazarse\nR+Y - Cambiar ajuste de palabras\nR+X - Ir a línea #\nB - Salir\n",
|
"TEXTVIEWER_CONTROLS_DETAILS": "Controles de Textviewer:\n \n↑↓→←(+R) - Desplazarse\nR+Y - Cambiar ajuste de palabras\nR+X - Ir a línea #\nB - Salir\n",
|
||||||
"TEXTEDITOR_CONTROLS_DETAILS": "Controles del editor de texto:\n \n↑↓→←(+R) - Desplazarse\nR+Y - Ajuste de línea\nR+X - Ir a línea #\nA - Modo edición\nB - Salir\n",
|
"TEXTEDITOR_CONTROLS_DETAILS": "Text Editor Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nA - Enter edit mode\nB - Exit\n",
|
||||||
"TEXTEDITOR_CONTROLS_KEYBOARD": "Controles del editor de texto:\n \n↑↓→←(+R) - Mover cursor\nX - Borrar carácter\nA - Insertar nueva línea\nL+↑↓→← - Seleccionar texto\nY - COPIAR / [+R] CORTAR\nB - Entrar en modo vista\n",
|
"TEXTEDITOR_CONTROLS_KEYBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nX - Delete char\nA - Insert newline\nL+↑↓→← - Select text\nY - COPY / [+R] CUT\nB - Enter view mode\n",
|
||||||
"TEXTEDITOR_CONTROLS_CLIPBOARD": "Controles del editor de texto:\n \n↑↓→←(+R) - Mover cursor\nX - Borrar carácter\nA - Insertar nueva línea\nL+↑↓→← - Seleccionar texto\nY - PEGAR / [+R] BORRAR\nB - Entrar en modo vista\n",
|
"TEXTEDITOR_CONTROLS_CLIPBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nX - Delete char\nA - Insert newline\nL+↑↓→← - Select text\nY - PASTE / [+R] CLEAR\nB - Enter view mode\n",
|
||||||
"TEXT_EDITS_SAVE_CHANGES": "Has editado el texto.\n¿Escribir los cambios en el archivo?",
|
"TEXT_EDITS_SAVE_CHANGES": "You made text edits.\nWrite changes to file?",
|
||||||
"CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "Línea actual: %i\nIngresa una nueva línea a continuación.",
|
"CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "Línea actual: %i\nIngresa una nueva línea a continuación.",
|
||||||
"PREVIEW_DISABLED": "(vista previa desactivada)",
|
"PREVIEW_DISABLED": "(vista previa desactivada)",
|
||||||
"PATH_LINE_N_ERR_LINE": "%s\nlínea %lu: %s\n%s",
|
"PATH_LINE_N_ERR_LINE": "%s\nlínea %lu: %s\n%s",
|
||||||
@ -805,7 +805,7 @@
|
|||||||
"END_OF_SCRIPT_UNRESOLVED_FOR": "fin del script: 'for' sin resolver",
|
"END_OF_SCRIPT_UNRESOLVED_FOR": "fin del script: 'for' sin resolver",
|
||||||
"SYSINFO_MODEL": "Modelo: %s (%s)\n",
|
"SYSINFO_MODEL": "Modelo: %s (%s)\n",
|
||||||
"SYSINFO_SERIAL": "Serie: %s\n",
|
"SYSINFO_SERIAL": "Serie: %s\n",
|
||||||
"SYSINFO_GYRO_MODEL": "Modelo de giroscopio: %u\n",
|
"SYSINFO_GYRO_MODEL": "Gyro model: %u\r\n",
|
||||||
"SYSINFO_REGION_SYSTEM": "Región (actual): %s\n",
|
"SYSINFO_REGION_SYSTEM": "Región (actual): %s\n",
|
||||||
"SYSINFO_REGION_SALES": "Región (original): %s\n",
|
"SYSINFO_REGION_SALES": "Región (original): %s\n",
|
||||||
"SYSINFO_SOC_MANUFACTURING_DATE": "Fecha de fabricación: %s\n",
|
"SYSINFO_SOC_MANUFACTURING_DATE": "Fecha de fabricación: %s\n",
|
||||||
@ -819,11 +819,11 @@
|
|||||||
"SYSINFO_SYSTEM_ID1": "ID1: %s\n",
|
"SYSINFO_SYSTEM_ID1": "ID1: %s\n",
|
||||||
"SORTING_TICKETS_PLEASE_WAIT": "Ordenando tickets...",
|
"SORTING_TICKETS_PLEASE_WAIT": "Ordenando tickets...",
|
||||||
"LUA_NOT_INCLUDED": "Este GodMode9 fue compilado\nsin el soporte de Lua.",
|
"LUA_NOT_INCLUDED": "Este GodMode9 fue compilado\nsin el soporte de Lua.",
|
||||||
"ERROR_SIGNATURE_CHECK_FAILED": "Error: Error en la comprobación de la firma",
|
"ERROR_SIGNATURE_CHECK_FAILED": "Error: Signature check failed",
|
||||||
"USE_SIGNATURE_VERIFICATION": "¿Usar verificación de firma?",
|
"USE_SIGNATURE_VERIFICATION": "Use signature verification?",
|
||||||
"IGNORE_SIGNATURES": "Ignorar firmas",
|
"IGNORE_SIGNATURES": "Ignore signatures",
|
||||||
"VERIFY_SIGNATURES": "Verificar firmas",
|
"VERIFY_SIGNATURES": "Verify signatures",
|
||||||
"STANDARD_CRYPTO": "Cifrado estándar",
|
"STANDARD_CRYPTO": "Standard encryption",
|
||||||
"ORIGINAL_CRYPTO": "Cifrado original",
|
"ORIGINAL_CRYPTO": "Original encryption",
|
||||||
"SELECT_TYPE_OF_ENCRYPTION": "Seleccione el tipo de cifrado"
|
"SELECT_TYPE_OF_ENCRYPTION": "Select type of encryption"
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -191,7 +191,7 @@
|
|||||||
"CALCULATE_SHA256": "SHA-256を計算",
|
"CALCULATE_SHA256": "SHA-256を計算",
|
||||||
"CALCULATE_SHA1": "SHA-1を計算",
|
"CALCULATE_SHA1": "SHA-1を計算",
|
||||||
"SHOW_FILE_INFO": "ファイル情報を表示",
|
"SHOW_FILE_INFO": "ファイル情報を表示",
|
||||||
"SHOW_IN_TEXTVIEWER": "テキストエディタで表示",
|
"SHOW_IN_TEXTVIEWER": "Show in Text Editor",
|
||||||
"CALCULATE_CMAC": "CMACを計算",
|
"CALCULATE_CMAC": "CMACを計算",
|
||||||
"COPY_TO_OUT": "%sにコピー",
|
"COPY_TO_OUT": "%sにコピー",
|
||||||
"DUMP_TO_OUT": "%sにダンプ",
|
"DUMP_TO_OUT": "%sにダンプ",
|
||||||
@ -778,7 +778,7 @@
|
|||||||
"SCRIPTERR_APPLY_IPS_FAILD": "IPS当てる失敗",
|
"SCRIPTERR_APPLY_IPS_FAILD": "IPS当てる失敗",
|
||||||
"SCRIPTERR_APPLY_BPS_FAILED": "BPS当てる失敗",
|
"SCRIPTERR_APPLY_BPS_FAILED": "BPS当てる失敗",
|
||||||
"SCRIPTERR_APPLY_BPM_FAILED": "BPM当てる失敗",
|
"SCRIPTERR_APPLY_BPM_FAILED": "BPM当てる失敗",
|
||||||
"SCRIPTERR_TEXTVIEWER_FAILED": "テキストエディタ失敗",
|
"SCRIPTERR_TEXTVIEWER_FAILED": "text editor failed",
|
||||||
"SCRIPTERR_BAD_DUMPSIZE": "不良ダンプサイズ",
|
"SCRIPTERR_BAD_DUMPSIZE": "不良ダンプサイズ",
|
||||||
"SCRIPTERR_CART_INIT_FAIL": "カート初期化失敗",
|
"SCRIPTERR_CART_INIT_FAIL": "カート初期化失敗",
|
||||||
"SCRIPTERR_CART_DUMP_FAILED": "カートダンプ失敗",
|
"SCRIPTERR_CART_DUMP_FAILED": "カートダンプ失敗",
|
||||||
@ -792,12 +792,12 @@
|
|||||||
"SCRIPTERR_UNCLOSED_CONDITIONAL": "未解決条件付き",
|
"SCRIPTERR_UNCLOSED_CONDITIONAL": "未解決条件付き",
|
||||||
"SCRIPTERR_ERROR_MESSAGE_FAIL": "エラーメッセージ失敗",
|
"SCRIPTERR_ERROR_MESSAGE_FAIL": "エラーメッセージ失敗",
|
||||||
"ERROR_INVALID_TEXT_DATA": "エラー: 無効なテキストデータ",
|
"ERROR_INVALID_TEXT_DATA": "エラー: 無効なテキストデータ",
|
||||||
"ERROR_TEXT_FILE_TOO_BIG": "エラー: テキストファイルが大きすぎます。\nテキストファイルのサイズは%uバイトです。\n最大ファイルサイズは%iバイトです。",
|
"ERROR_TEXT_FILE_TOO_BIG": "Error: Text file is too large.\nText file size is %u bytes.\nMax file size is %i bytes.",
|
||||||
"TEXTVIEWER_CONTROLS_DETAILS": "テキストビューアーのコントロル\n \n↑↓→←(+R) - スクロール\nR+Y - ワードラップを切り替え\nR+X - 行番号に移動\nB - 終了\n",
|
"TEXTVIEWER_CONTROLS_DETAILS": "テキストビューアーのコントロル\n \n↑↓→←(+R) - スクロール\nR+Y - ワードラップを切り替え\nR+X - 行番号に移動\nB - 終了\n",
|
||||||
"TEXTEDITOR_CONTROLS_DETAILS": "テキストエディタのコントロル\n \n↑↓→←(+R) - スクロール\nR+Y - ワードラップを切り替え\nR+X - 行番号に移動\nA - 編集モードにする\nB - 終了\n",
|
"TEXTEDITOR_CONTROLS_DETAILS": "Text Editor Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nA - Enter edit mode\nB - Exit\n",
|
||||||
"TEXTEDITOR_CONTROLS_KEYBOARD": "テキストエディタのコントロル\n \n↑↓→←(+R) - カーソルの移動\nX - 文字の削除\nA - 改行の挿入\nL + ↑↓→← - テキストの選択\nY - コピー / [+R] 切り取り\nB - 表示モードにする\n",
|
"TEXTEDITOR_CONTROLS_KEYBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nX - Delete char\nA - Insert newline\nL+↑↓→← - Select text\nY - COPY / [+R] CUT\nB - Enter view mode\n",
|
||||||
"TEXTEDITOR_CONTROLS_CLIPBOARD": "テキストエディタのコントロル\n \n↑↓→←(+R) - カーソルの移動\nX - 文字の削除\nA - 改行の挿入\nL + ↑↓→← - テキストの選択\nY - 貼り付け / [+R] クリア\nB - 表示モードにする\n",
|
"TEXTEDITOR_CONTROLS_CLIPBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nX - Delete char\nA - Insert newline\nL+↑↓→← - Select text\nY - PASTE / [+R] CLEAR\nB - Enter view mode\n",
|
||||||
"TEXT_EDITS_SAVE_CHANGES": "テキストを編集しました。\nファイルに変更を書き込みますか?",
|
"TEXT_EDITS_SAVE_CHANGES": "You made text edits.\nWrite changes to file?",
|
||||||
"CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "現在の行: %i\n以下に新しい行を入力してください。",
|
"CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "現在の行: %i\n以下に新しい行を入力してください。",
|
||||||
"PREVIEW_DISABLED": "(プレビュー無効)",
|
"PREVIEW_DISABLED": "(プレビュー無効)",
|
||||||
"PATH_LINE_N_ERR_LINE": "%s\n第%lu行: %s\n%s",
|
"PATH_LINE_N_ERR_LINE": "%s\n第%lu行: %s\n%s",
|
||||||
|
|||||||
@ -1,829 +0,0 @@
|
|||||||
{
|
|
||||||
"GM9_LANGUAGE": "한국어",
|
|
||||||
"GM9_TRANS_VER": 2,
|
|
||||||
"DATE_TIME_FORMAT": "%1$s%2$02lX년 %3$02lX월 %4$02lX일 %5$02lX:%6$02lX",
|
|
||||||
"DECIMAL_SEPARATOR": ".",
|
|
||||||
"THOUSAND_SEPARATOR": ",",
|
|
||||||
"FIRM_TOO_BIG": "FIRM 너무 큼, 부팅 불가",
|
|
||||||
"PATH_DO_NOT_BOOT_UNTRUSTED": "%s (%dkB)\n경고: 신뢰할 수 없는 출처에서\n받은 FIRM을 부팅하지 마세요.\n \nFIRM을 부팅하시겠습니까?",
|
|
||||||
"NOT_BOOTABLE_FIRM": "부팅 가능한 FIRM이 아닙니다.",
|
|
||||||
"FIRM_ENCRYPTED": "펌웨어가 암호화되어 있습니다.\n \n부팅 전에 암호화를 해제할까요?",
|
|
||||||
"MAKE_COPY_AT_OUT_TEMP_FIRM": "%s/temp.firm에 복사본을 만들기",
|
|
||||||
"TRY_BOOT_ANYWAYS": "무시하고 부팅",
|
|
||||||
"WARNING_BOOT_UNSUPPORTED_LOCATION": "경고: 지원되지 않는 경로에서\n부팅을 시도 중입니다.",
|
|
||||||
"ROOT": "[최상위]",
|
|
||||||
"LOADING": "로딩 중...",
|
|
||||||
"PANE_N": "%lu번 패널",
|
|
||||||
"CURRENT": "현재",
|
|
||||||
"DIR": "(폴더)",
|
|
||||||
"SD_FAT": "(SD FAT)",
|
|
||||||
"RAMDRIVE_FAT": "(FAT 램 드라이브)",
|
|
||||||
"GAME_VIRTUAL": "(가상 게임)",
|
|
||||||
"SYSNAND_FAT": "(SysNAND FAT)",
|
|
||||||
"SYSNAND_VIRTUAL": "(SysNAND 가상)",
|
|
||||||
"EMUNAND_FAT": "(EmuNAND FAT)",
|
|
||||||
"EMUNAND_VIRTUAL": "(가상 에뮤낸드)",
|
|
||||||
"IMAGE_FAT": "(이미지 FAT)",
|
|
||||||
"XORPAD_VIRTUAL": "(가상 XORpad)",
|
|
||||||
"MEMORY_VIRTUAL": "(메모리 가상)",
|
|
||||||
"ALIAS_FAT": "(별칭 FAT)",
|
|
||||||
"GAMECART_VIRTUAL": "(가상 게임 카트리지)",
|
|
||||||
"VRAM_VIRTUAL": "(비디오 메모리 가상)",
|
|
||||||
"SEARCH": "(검색)",
|
|
||||||
"TITLEMANAGER_VIRTUAL": "(가상 TitleManager)",
|
|
||||||
"LAB_SDCARD": "SD 카드",
|
|
||||||
"LAB_SYSNAND_CTRNAND": "시스낸드 CTR낸드",
|
|
||||||
"LAB_SYSNAND_TWLN": "TWLN 시스낸드",
|
|
||||||
"LAB_SYSNAND_TWLP": "TWLP 시스낸드",
|
|
||||||
"LAB_SYSNAND_SD": "SD 시스낸드",
|
|
||||||
"LAB_SYSNAND_VIRTUAL": "가상 시스낸드",
|
|
||||||
"LAB_EMUNAND_CTRNAND": "CTR낸드 에뮤낸드",
|
|
||||||
"LAB_EMUNAND_TWLN": " TWLN 에뮤낸드",
|
|
||||||
"LAB_EMUNAND_TWLP": "TWLP 에뮤낸드",
|
|
||||||
"LAB_EMUNAND_SD": "SD 에뮤낸드",
|
|
||||||
"LAB_EMUNAND_VIRTUAL": "가상 에뮤낸드",
|
|
||||||
"LAB_IMGNAND_CTRNAND": "IMGNAND CTRNAND",
|
|
||||||
"LAB_IMGNAND_TWLN": "IMGNAND TWLN",
|
|
||||||
"LAB_IMGNAND_TWLP": "IMGNAND TWLP",
|
|
||||||
"LAB_IMGNAND_VIRTUAL": "IMGNAND 가상",
|
|
||||||
"LAB_GAMECART": "카트리지",
|
|
||||||
"LAB_GAME_IMAGE": "게임 이미지",
|
|
||||||
"LAB_AESKEYDB_IMAGE": "AESKEYDB 이미지",
|
|
||||||
"LAB_BDRI_IMAGE": "BDRI 이미지",
|
|
||||||
"LAB_DISA_DIFF_IMAGE": "DISA/DIFF 이미지",
|
|
||||||
"LAB_MEMORY_VIRTUAL": "메모리 가상",
|
|
||||||
"LAB_VRAM_VIRTUAL": "비디오 메모리 가상",
|
|
||||||
"LAB_TITLE_MANAGER": "타이틀 관리",
|
|
||||||
"LAB_LAST_SEARCH": "마지막 검색",
|
|
||||||
"LAB_FAT_IMAGE": "FAT 이미지",
|
|
||||||
"LAB_BONUS_DRIVE": "보너스 드라이브",
|
|
||||||
"LAB_RAMDRIVE": "램 드라이브",
|
|
||||||
"LAB_NOLABEL": "이름 없음",
|
|
||||||
"N_BYTE": "%s 바이트",
|
|
||||||
"BYTE": " 바이트",
|
|
||||||
"KB": " KB",
|
|
||||||
"MB": " MB",
|
|
||||||
"GB": " GB",
|
|
||||||
"CLIPBOARD": "[클립보드]",
|
|
||||||
"PLUS_N_MORE": "+ %lu개 더 있음",
|
|
||||||
"MARK_DELETE_COPY": "L - 파일 선택 (↑↓→← 사용)\n X - 삭제 / [+R] 파일 이름 변경 \nY - 파일 복사 / [+R] 항목 생성\n",
|
|
||||||
"MARK_DELETE_PASTE": "L - 파일 선택 (↑↓→←와 함께 사용)\nX - 삭제 / [+R] 파일 이름 바꾸기\nY - 파일 붙여 넣기 / [+R] 항목 만들기\n",
|
|
||||||
"RELOCK_WRITE_PERMISSION": "R+Y - 쓰기 권한 해제\n",
|
|
||||||
"UNMOUNT_IMAGE": "R+X - 이미지 마운트 해제\n",
|
|
||||||
"UNMOUNT_SD": "R+B - SD 카드 마운트 해제\n",
|
|
||||||
"REMOUNT_SD": "R+B - SD 카드 다시 마운트\n",
|
|
||||||
"DIRECTORY_OPTIONS": "R+A - 경로 설정\n",
|
|
||||||
"DRIVE_OPTIONS": "R+A - 드라이브 설정\n",
|
|
||||||
"MAKE_SCREENSHOT": "R+L - 스크린 샷 만들기\n",
|
|
||||||
"PREV_NEXT_PANE": "R+←→ - 이전/다음 창으로 전환\n",
|
|
||||||
"CLEAR_CLIPBOARD": "SELECT - 클립보드 비우기\n",
|
|
||||||
"RESTORE_CLIPBOARD": "SELECT - 클립보드 복원\n",
|
|
||||||
"REBOOT_POWEROFF_HOME": "START - 다시 시작 / [+R] 종료\nHOME 버튼으로 HOME 메뉴 사용",
|
|
||||||
"NO_EMUNAND": "에뮤낸드 없음",
|
|
||||||
"REDNAND_SIZE_MIN": "RedNAND 사이즈 (최소)",
|
|
||||||
"GW_EMUNAND_SIZE_FULL": "GW 에뮤낸드 사이즈 (최대)",
|
|
||||||
"MULTINAND_SIZE_2X": "MultiNAND 사이즈 (2x)",
|
|
||||||
"MULTINAND_SIZE_3X": "MultiNAND 사이즈 (3x)",
|
|
||||||
"MULTINAND_SIZE_4X": "MultiNAND 사이즈 (4x)",
|
|
||||||
"USER_INPUT": "사용자 입력...",
|
|
||||||
"AUTO": "자동",
|
|
||||||
"16KB_CLUSTERS": "16KB 클러스터",
|
|
||||||
"32KB_CLUSTERS": "32KB 클러스터",
|
|
||||||
"64KB_CLUSTERS": "64KB 클러스터",
|
|
||||||
"SD_NOT_DETECTED": "오류: SD 카드가 감지되지 않았습니다.",
|
|
||||||
"FORMAT_SD_CHOOSE_EMUNAND": "SD 카드를 포맷하시겠습니까? (%lluMB)\n에뮤낸드 사이즈를 선택해 주세요:",
|
|
||||||
"SD_SIZE_IS_ENTER_EMUNAND_SIZE": "SD 카드 크기는 %lluMB입니다.\n아래에 에뮤낸드 크기(MB)를 입력하세요:",
|
|
||||||
"FORMAT_SD_CHOOSE_CLUSTER": "SD 카드를 포맷 하시겠습니까? (%lluMB)\n클러스터 사이즈를 선택 해 주세요:",
|
|
||||||
"FORMAT_SD_ENTER_LABEL": "SD 카드를 포맷하시겠습니까? (%lluMB)\n레이블을 입력해 주세요:",
|
|
||||||
"FORMAT_SD_FAILED": "SD 카드 포맷: 실패!",
|
|
||||||
"REDNAND_TYPE": "RedNAND 타입",
|
|
||||||
"REDNAND_TYPE_MULTI": "RedNAND 타입 (다중)",
|
|
||||||
"REDNAND_TYPE_SINGLE": "RedNAND 타입 (단일)",
|
|
||||||
"GW_EMUNAND_TYPE": "GW 에뮤낸드 타입",
|
|
||||||
"DONT_SET_UP": "설정하지 않기",
|
|
||||||
"CHOOSE_EMUNAND_TYPE": "설정 할 에뮤낸드 타입을 선택 해 주세요:",
|
|
||||||
"CLONE_SYSNAND_TO_REDNAND": "시스낸드를 RedNAND로 복제하겠습니까?",
|
|
||||||
"CLONING_SYSNAND_TO_EMUNAND_FAILED": "시스낸드를 애뮤낸드로 복제: 실패!",
|
|
||||||
"PRESS_A_TO_CONTINUE": "<A> 를 눌러 계속하기",
|
|
||||||
"HEXEDITOR_CONTROLS": "헥스 편집기 조작 방법:\n \n↑↓→←(+R) - 스크롤\nR+Y - 표시 방식 변경\nX - 검색 / 바로 가기...\nA - 편집 모드\nA+↑↓→← - 값 편집\nB - 나가기\n",
|
|
||||||
"NOT_FOUND": "찾을 수 없음!",
|
|
||||||
"GO_TO_OFFSET": "오프셋으로 이동",
|
|
||||||
"SEARCH_FOR_STRING": "문자열 검색",
|
|
||||||
"SEARCH_FOR_DATA": "데이터 검색",
|
|
||||||
"CURRENT_OFFSET_SELECT_ACTION": "현재 오프셋: %08lX\n작업 선택:",
|
|
||||||
"CURRENT_OFFSET_ENTER_NEW": "현재 오프셋입니다: %08lX\n새 오프셋을 입력하세요.",
|
|
||||||
"ENTER_SEARCH_REPEAT_SEARCH": "검색할 문자열을 입력하세요.\n(검색을 반복하려면 R+X를 누르세요)",
|
|
||||||
"MADE_EDITS_SAVE_CHANGES": "%lu개의 항목(들)을 수정했습니다.\n파일에 변경 내용을 기록하시겠습니까?",
|
|
||||||
"FAILED_WRITING_TO_FILE": "파일 쓰기 실패!",
|
|
||||||
"CALCULATING_SHA_FAILED": "SHA-%s 계산 실패!",
|
|
||||||
"SHA_VERIFICATION_PASSED": "\nSHA 검증: 통과!",
|
|
||||||
"SHA_VERIFICATION_FAILED": "\nSHA 검증: 실패",
|
|
||||||
"IDENTICAL_WITH_PREVIOUS": "\n이전 파일과 동일합니다:\n",
|
|
||||||
"WRITE_SHA_FILE": "\n.SHA 파일을 쓰시겠습니까?",
|
|
||||||
"WRITE_SHA1_FILE": "\n.SHA1 파일을 쓰시겠습니까?",
|
|
||||||
"CALCULATING_CMAC_FAILED": "CMAC 계산 실패!",
|
|
||||||
"CMAC_VERIFICATION_PASSED": "CMAC 검증: 통과!",
|
|
||||||
"CMAC_VERIFICATION_FAILED": "CMAC 검증: 실패!",
|
|
||||||
"FIX_CMAC_IN_FILE": "\n파일의 CMAC를 고치시겠습니까?",
|
|
||||||
"FIXING_CMAC_FAILED": "CMAC 수리: 실패!",
|
|
||||||
"COPY_ALL_SELECTED_ITEMS": "선택한 항목 %lu을(를) 모두 복사하시겠습니까?",
|
|
||||||
"FAILED_COPYING_ITEM": "항목 복사 실패",
|
|
||||||
"ITEMS_COPIED_TO_OUT": "%lu 항목들이 %s로 복사돼었습니다",
|
|
||||||
"PATH_COPIED_TO_OUT": "%s\n%s로 복사됨",
|
|
||||||
"CART_INIT_FAILED": "카트리지 초기화 실패!",
|
|
||||||
"CART_DETECTED_SIZE_INPUT_BELOW": "카트리지: %s\n감지된 크기: %s\n\n덤프 크기를 입력하세요.",
|
|
||||||
"NDS_CART_DECRYPT_SECURE_AREA": "카트리지: %s\nNDS 카트리지 감지됨\n보안 구역을 해독하겠습니까?",
|
|
||||||
"FAILED_DUMPING_CART": "%s\n카트리지 덤핑 실패",
|
|
||||||
"PATH_DUMPED_TO_OUT": "%s\n%s에 덤프됨",
|
|
||||||
"CREATED": "생성 날짜",
|
|
||||||
"MODIFIED": "수정 날짜",
|
|
||||||
"ANALYZING_DRIVE": "드라이브를 분석 중이니 잠시만 기다려 주세요...",
|
|
||||||
"ANALYZING_DIR": "폴더를 분석 중이니 잠시만 기다려 주세요...",
|
|
||||||
"N_FILES_N_SUBDIRS_TOTAL_SIZE_FREE_USED_TOTAL": "파일 %lu개 & 하위 폴더 %lu개\n총 사이즈 %s\n\n빈 공간: %s\n사용된 공간: %s\n총 공간: %s",
|
|
||||||
"N_FILES_N_SUBDIRS_TOTAL_SIZE": "파일 %lu개 & 하위 폴더 %lu개\n총 사이즈 %s",
|
|
||||||
"FILESIZE_X": "파일 크기: %s",
|
|
||||||
"READONLY_HIDDEN_SYSTEM_ARCHIVE_VIRTUAL": " \n[%c] %s읽기 전용 [%c] %s숨겨짐\n[%c] %s시스템 [%c] %s아카이브\n[%c] %s가상\n%s",
|
|
||||||
"UDRL_CHANGE_ATTRIBUTES": " \n(↑↓→←을 사용해 속성을 수정)\n",
|
|
||||||
"A_TO_CONTINUE": "(<A> 계속하기)",
|
|
||||||
"A_APPLY_B_CANCEL": "(<A> 적용하기, <B> 취소하기)",
|
|
||||||
"A_YES_B_NO": "(<A> 예, <B> 아니요)",
|
|
||||||
"A_SELECT_B_CANCEL": "(<A> 선택, <B> 취소)",
|
|
||||||
"HOLD_B_TO_CANCEL": "(B 버튼을 꾹 눌러 취소하기)",
|
|
||||||
"FAILED_TO_SET_ATTRIBUTES": "속성 설정 실패!",
|
|
||||||
"NAND_IMAGE_OPTIONS": "NAND 이미지 옵션...",
|
|
||||||
"CTRNAND_OPTIONS": "CTRNAND 옵션...",
|
|
||||||
"MOUNT_FAT_IMAGE": "FAT 이미지로 마운팅하기",
|
|
||||||
"CIA_IMAGE_OPTIONS": "CIA 이미지 옵션...",
|
|
||||||
"NCSD_IMAGE_OPTIONS": "NCSD 이미지 옵션...",
|
|
||||||
"NCCH_IMAGE_OPTIONS": "NCCH 이미지 옵션...",
|
|
||||||
"MOUNT_AS_EXEFS_IMAGE": "EXEFS 이미지로 마운팅하기",
|
|
||||||
"MOUNT_AS_ROMFS_IMAGE": "ROMFS 이미지로 마운팅하기",
|
|
||||||
"TMD_FILE_OPTIONS": "TMD 파일 옵션...",
|
|
||||||
"TMD_CDN_OPTIONS": "TMD/CDN 옵션...",
|
|
||||||
"TMD_TWL_OPTIONS": "TMD/TWL 옵션...",
|
|
||||||
"MANAGE_TITLE": "타이틀 관리...",
|
|
||||||
"BOSS_FILE_OPTIONS": "BOSS 파일 옵션...",
|
|
||||||
"DECRYPT_NUS_CDN_FILE": "NUS/CDN 파일 해독하기",
|
|
||||||
"SHOW_SMDH_TITLE_INFO": "SMDH 타이틀 정보 표시",
|
|
||||||
"NDS_IMAGE_OPTIONS": "NDS 이미지 옵션...",
|
|
||||||
"GBA_IMAGE_OPTIONS": "GBA 이미지 옵션...",
|
|
||||||
"TICKET_OPTIONS": "티켓 옵션...",
|
|
||||||
"TAD_IMAGE_OPTIONS": "TAD 이미지 옵션...",
|
|
||||||
"SHOW_3DSX_TITLE_INFO": "3DSX 타이틀 정보 표시",
|
|
||||||
"FIRM_IMAGE_OPTIONS": "FIRM 이미지 옵션...",
|
|
||||||
"AGBSAVE_OPTIONS": "AGBSAVE 옵션...",
|
|
||||||
"DUMP_GBA_VC_SAVE": "GBA VC 세이브 파일 덤프",
|
|
||||||
"TICKET_DB_OPTIONS": "Ticket.db 옵션...",
|
|
||||||
"MOUNT_AS_DIFF_IMAGE": "DIFF 이미지로 마운트",
|
|
||||||
"MOUNT_AS_DISA_IAMGE": "DISA 이미지로 마운트",
|
|
||||||
"INSTALL_CIFINISH_BIN": "cifinish.bin 설치",
|
|
||||||
"TITLEKEY_OPTIONS": "Titlekey 옵션...",
|
|
||||||
"AESKEYDB_OPTIONS": "AESkeydb 옵션...",
|
|
||||||
"BUILD_X": "%s 빌드",
|
|
||||||
"NCCHINFO_OPTIONS": "NCCHinfo 옵션...",
|
|
||||||
"EXECUTE_GM9_SCRIPT": "GM9 스크립트 실행",
|
|
||||||
"EXECUTE_LUA_SCRIPT": "Lua 스크립트 실행",
|
|
||||||
"FONT_OPTIONS": "폰트 옵션...",
|
|
||||||
"LANGUAGE_OPTIONS": "언어 설정...",
|
|
||||||
"VIEW_PNG_FILE": "PNG 파일 보기",
|
|
||||||
"REBUILD_NCSD_HEADER": "NCSD 헤더 리빌드",
|
|
||||||
"SHOW_IN_HEXEDITOR": "헥스 편집기에서 보기",
|
|
||||||
"CALCULATE_SHA256": "SHA-256 계산",
|
|
||||||
"CALCULATE_SHA1": "SHA-1 계산",
|
|
||||||
"SHOW_FILE_INFO": "파일 정보 보기",
|
|
||||||
"SHOW_IN_TEXTVIEWER": "텍스트 편집기에서 보기",
|
|
||||||
"CALCULATE_CMAC": "CMAC 계산",
|
|
||||||
"COPY_TO_OUT": "%s(으)로 복사",
|
|
||||||
"DUMP_TO_OUT": "%s(으)로 덤프",
|
|
||||||
"INJECT_DATA_AT_OFFSET": "@offset에 데이터 삽입",
|
|
||||||
"OPEN_THIS_FOLDER": "이 폴더 열기",
|
|
||||||
"OPEN_CONTAINING_FOLDER": "들어있는 폴더 열기",
|
|
||||||
"OPEN_TITLE_FOLDER": "타이틀 폴더 열기",
|
|
||||||
"PATH_N_FILES_SELECTED": "%s (%lu 개 선택됨)",
|
|
||||||
"CHECK_CURRENT_CMAC_ONLY": "현재 CMAC만 확인",
|
|
||||||
"VERIFY_CMAC_FOR_ALL": "모든 CMAC 검증",
|
|
||||||
"FIX_CMAC_FOR_ALL": "모든 CMAC 수리",
|
|
||||||
"N_N_N_FILES_OK_FIXED_TOTAL_N_OF_N_HAVE_NO_CMAC": "%lu/%lu/%lu 파일 완료/고침/전체\n%lu/%lu개의 파일엔 CMAC이(가) 없습니다",
|
|
||||||
"N_OF_N_FILES_VERIFIED_N_OF_N_FILES_FIXED": "%lu/%lu개의 파일 검증 완료\n%lu/%lu개의 파일 고쳐짐",
|
|
||||||
"N_OF_N_FILES_VERIFIED_N_OF_N_HAVE_NO_CMAC": "%lu/%lu개의 파일 검증 완료\n%lu/%lu개는 CMAC가 없음",
|
|
||||||
"N_OF_N_FILES_VERIFIED": "%lu/%lu개의 파일 검증 완료",
|
|
||||||
"INJECT_DATA_FROM_SPECIFY_OFFSET_BELOW": "%s에 데이터를 삽입하시겠습니까?\n오프셋을 설정해주세요.",
|
|
||||||
"FAILED_INJECTING_PATH": "%s를 주입하는데 실패함",
|
|
||||||
"MOUNT_CXI_NDS_TO_DRIVE": "CXI/NDS를 드라이브에 마운트",
|
|
||||||
"MOUNT_IMAGE_TO_DRIVE": "이미지를 드라이브에 마운트",
|
|
||||||
"RESTORE_SYSNAND_SAFE": "시스낸드 복원 (안전모드)",
|
|
||||||
"UPDATE_EMBEDDED_BACKUP": "임베디드 백업 업데이트",
|
|
||||||
"SHOW_TITLE_INFO": "타이틀 정보 표시",
|
|
||||||
"DECRYPT_FILE": "파일 복호화 (...)",
|
|
||||||
"DECRYPT_FILE_OUT": "파일 복호화 (%s)",
|
|
||||||
"ENCRYPT_FILE": "파일 암호화 (...)",
|
|
||||||
"ENCRYPT_FILE_OUT": "파일 암호화 (%s)",
|
|
||||||
"BUILD_CIA_FROM_FILE": "파일에서 CIA 빌드",
|
|
||||||
"BUILD_CIA_STANDARD": "CIA 빌드 (표준)",
|
|
||||||
"BUILD_CIA_LEGIT": "CIA 빌드 (legit)",
|
|
||||||
"DUMP_CXI_NDS_FILE": "CXI/NDS 파일 덤프",
|
|
||||||
"INSTALL_GAME_IMAGE": "게임 이미지 설치",
|
|
||||||
"INSTALL_TICKET": "티켓 설치",
|
|
||||||
"DUMP_TICKET_FILE": "티켓 파일 덤프",
|
|
||||||
"UNINSTALL_TITLE": "타이틀 삭제",
|
|
||||||
"VERIFY_FILE": "파일 검증",
|
|
||||||
"TRANSFER_IMAGE_TO_CTRNAND": "이미지를 CTRNAND로 이동",
|
|
||||||
"INJECT_TO_H_AND_S": "안전을 위한 주의사항에 주입",
|
|
||||||
"TRIM_FILE": "파일 트림",
|
|
||||||
"RENAME_FILE": "파일 이름 바꾸기",
|
|
||||||
"BUILD_XORPADS_SD": "XORpads 빌드 (SD 출력)",
|
|
||||||
"BUILD_XORPADS_INPLACE": "XORpads 빌드 (기존 파일 대체)",
|
|
||||||
"EXTRACT_X": "%s 압축 해제",
|
|
||||||
"INIT_X": "%s 초기화",
|
|
||||||
"INSTALL_X": "%s 설치",
|
|
||||||
"INSTALL_FIRM": "FIRM 설치",
|
|
||||||
"BOOT_FIRM": "FIRM 부팅",
|
|
||||||
"SET_AS_ACTIVE_FONT": "활성화된 글꼴로 설정",
|
|
||||||
"SET_AS_ACTIVE_LANGUAGE": "활성화된 언어로 설정",
|
|
||||||
"DUMP_BA_VC_SAVE": "GBA VC 세이브 덤프",
|
|
||||||
"INJECT_GBA_VC_SAVE": "GBA VC 세이브 주입",
|
|
||||||
"SET_AS_DEFAULT": "기본값으로 설정",
|
|
||||||
"MOUNTING_IMAGE_FAILED": "이미지 마운트: 실패",
|
|
||||||
"PATH_MOUNTED_AS_DRIVE_ENTER_PATH_NOW": "%s\n%s 드라이브로 마운트됨\n경로를 입력하시겠습니까?",
|
|
||||||
"DECRYPT_TO_OUT": "%s로 해독",
|
|
||||||
"DECRYPT_INPLACE": "기존 위치로 해독",
|
|
||||||
"TRY_TO_DECRYPT_ALL_N_SELECTED_FILES": "선택된 %lu개의 파일들을 전부 해독하겠습니까?",
|
|
||||||
"TRYING_TO_DECRYPT_N_FILES": "%lu개의 파일 해독 시도 중...",
|
|
||||||
"DECRYPTION_FAILED_CONTINUE": "해독 실패\n \n계속하시겠습니까?",
|
|
||||||
"N_OF_N_FILES_DECRYPTED_N_OF_N_NOT_ENCRYPTED_N_OF_N_NOT_SAME_TYPE": "%lu/%lu개 파일 해독 완료\n%lu/%lu개 파일 암호화되지 않음\n%lu/%lu개 파일은 같은 유형이 아님",
|
|
||||||
"N_OF_N_FILES_DECRYPTED": "%lu/%lu 해독 완료",
|
|
||||||
"N_FILES_WRITTEN_TO_OUT": "%lu개의 파일이 %s에 저장됨",
|
|
||||||
"FILE_NOT_ENCRYPTED": "파일이 암호화되지 않았습니다",
|
|
||||||
"DECRYPTION_SUCCESS": "해독 성공",
|
|
||||||
"DECRYPTION_FAILED": "해독 실패",
|
|
||||||
"PATH_DECRYPTED_TO_OUT": "%s\n%s에 해독됨",
|
|
||||||
"ENCRYPT_TO_OUT": "%s에 암호화",
|
|
||||||
"ENCRYPT_INPLACE": "기존 위치에 암호화",
|
|
||||||
"TRY_TO_ENCRYPT_N_SELECTED_FILES": "선택된 %lu개의 파일을 전부 암호화하시겠습니까?",
|
|
||||||
"TRYING_TO_ENCRYPT_N_FILES": "%lu개의 파일 암호화 시도 중...",
|
|
||||||
"ENCRYPTION_FAILED_CONTINUE": "암호화 실패\n \n계속하시겠습니까?",
|
|
||||||
"N_OF_N_FILES_ENCRYPTED_N_OF_N_NOT_SAME_TYPE": "%lu/%lu개의 파일 암호화됨\n%lu/%lu개의 파일은 같은 유형이 아님",
|
|
||||||
"N_OF_N_FILES_ENCRYPTED": "%lu/%lu개의 파일 암호화 완료",
|
|
||||||
"ENCRYPTION_SUCCESS": "암호화 성공",
|
|
||||||
"ENCRYPTION_FAILED": "암호화 실패",
|
|
||||||
"PATH_ENCRYPTED_TO_OUT": "%s\n%s에 암호화됨",
|
|
||||||
"TRY_TO_PROCESS_N_SELECTED_FILES": "선택된 %lu개의 파일을 전부 처리하시겠습니까?",
|
|
||||||
"PATH_BUILD_TYPE_FAILED_CONTINUE": "%s\n%s 빌드 실패\n \n계속하시겠습니까?",
|
|
||||||
"N_OF_N_TYPES_BUILT_N_OF_N_NOT_SAME_TYPE": "%lu/%lu %s 빌드됨\n%lu/%lu개는 같은 유형이 아님",
|
|
||||||
"N_OF_N_TYPES_BUILT": "%lu/%lu %s 빌드됨",
|
|
||||||
"N_FILES_FAILED_CONVERTION_VERIFICATION_RECOMMENDED": "%lu개의 파일을 변환하는 데 실패하였습니다.\n파일 검증을 권장합니다.",
|
|
||||||
"PATH_TYPE_BUILT_TO_OUT": "%s\n%s %s에 빌드됨",
|
|
||||||
"PATH_TYPE_BUILD_FAILED": "%s\n%s 빌드 실패",
|
|
||||||
"FILE_FAILED_CONVERSION_VERIFY_NOW": "파일 변환 실패.\n \n파일을 검증하시겠습니까?",
|
|
||||||
"VERIFICATION_SUCCESS": "검증 성공",
|
|
||||||
"VERIFICATION_FAILED": "검증 실패",
|
|
||||||
"CONTENT_IS_MISSING": "콘텐츠를 찾을 수 없음",
|
|
||||||
"INSTALL_TO_SYSNAND": "시스낸드에 설치",
|
|
||||||
"INSTALL_TO_EMUNAND": "에뮤낸드에 설치",
|
|
||||||
"TRY_TO_INSTALL_N_SELECTED_FILES": "선택된 %lu개의 파일을 전부 설치하시겠습니까?",
|
|
||||||
"TRYING_TO_INSTALL_N_FILES": "%lu개의 파일 설치 시도 중...",
|
|
||||||
"INSTALL_FAILED_CONTINUE": "설치에 실패했습니다\n \n계속할까요?",
|
|
||||||
"N_OF_N_FILES_INSTALLED_N_OF_N_NOT_SAME_TYPE": "%lu/%lu개의 파일 설치됨\n%lu/%lu개의 파일은 같은 유형이 아님",
|
|
||||||
"N_OF_N_FILES_INSTALLED": "%lu/%lu개의 파일 설치 완료",
|
|
||||||
"INSTALL_SUCCESS": "설치 성공",
|
|
||||||
"INSTALL_FAILED": "설치 실패",
|
|
||||||
"FILE_FAILED_INSTALL_VERIFY_NOW": "파일 설치 실패.\n\n파일을 검증하시겠습니까?",
|
|
||||||
"KEEP_TICKET_AND_SAVEGAME": "티켓 & 세이브파일 보관",
|
|
||||||
"UNINSTALL_EVERYTHING": "모든 것 삭제",
|
|
||||||
"ABORT_UNINSTALL": "삭제 중지",
|
|
||||||
"UNINSTALL_N_SELECTED_TITLES": "선택한 항복 %lu를 모두 삭제하시겠습니까?",
|
|
||||||
"UNINSTALL_SELECTED_TITLE": "선택한 타이틀을 삭제하시겠습니까?",
|
|
||||||
"N_OF_N_TITLES_UNINSTALLED": "%lu/%lu 타이틀 삭제됨",
|
|
||||||
"UNINSTALLING_PLEASE_WAIT": "삭제중, 잠시만 기다려주세요...",
|
|
||||||
"UNINSTALL_FAILED": "삭제 실패!",
|
|
||||||
"TRY_TO_VERIFY_N_SELECTED_FILES": "선택된 %lu개의 파일들을 전부 검증하시겠습니까?",
|
|
||||||
"VERIFICATION_FAILED_CONTINUE": "검증 실패\n \n계속하시겠습니까?",
|
|
||||||
"N_OF_N_FILES_VERIFIED_N_OF_N_NOT_SAME_TYPE": "%lu/%lu개의 파일 검증 완료\n%lu/%lu개의 파일은 같은 유형이 아님",
|
|
||||||
"VERIFYING_FILE_PLEASE_WAIT": "파일을 검증 중입니다. 잠시만 기다려 주세요...",
|
|
||||||
"NAND_VALIDATION_SUCCESS": "NAND 검증 성공",
|
|
||||||
"NAND_VALIDATION_FAILED": "NAND 검증 실패",
|
|
||||||
"DUMP_FOR_N_SELECTED_FILES": "선택된 %lu개의 파일들을 전부 덤프하시겠습니까?",
|
|
||||||
"N_OF_N_LEGIT_TICKETS_DUMPED_ATTEMPT_DUMP_ALL": "%lu/%lu개의 legit 티켓이 덤프되었습니다.\n \n모든 티켓 덤프를 시도할까요?",
|
|
||||||
"N_OF_N_TICKETS_DUMPED_TO_OUT": "%lu/%lu개의 티켓을 %s에 덤프함",
|
|
||||||
"PATH_TICKET_DUMPED_TO_OUT": "%s\n티켓을 %s에 덤프했습니다",
|
|
||||||
"LEGIT_TICKET_NOT_FOUND_DUMP_ANYWAYS": "%s\nlegit 티켓을 찾을 수 없습니다.\n \n어쨌든 덤프하시겠습니까?",
|
|
||||||
"DUMP_TICKET_FAILED": "티켓 파일 덤프에 실패함!",
|
|
||||||
"BUILDING_X": "%s 빌드 중...",
|
|
||||||
"BUILDING_X_SYSNAND": "%s 빌드 중 (시스낸드)...",
|
|
||||||
"BUILDING_X_EMUNAND": "%s 빌드 중 (에뮤낸드)...",
|
|
||||||
"PATH_N_OF_N_FILES_PROCESSED_N_OF_N_FILES_IGNORED": "%s\n%lu/%lu개의 파일 처리됨\n%lu/%lu개의 파일 건너뜀",
|
|
||||||
"PATH_N_OF_N_FILES_PROCESSED": "%s\n%lu/%lu개의 파일 처리됨",
|
|
||||||
"BUILD_DATABASE_SUCCESS": "데이터베이스 빌드에 성공했습니다.",
|
|
||||||
"BUILD_DATABASE_FAILED": "데이터베이스 빌드에 실패했습니다.",
|
|
||||||
"TRY_TO_TRIM_N_SELECTED_FILES": "선택된 %lu개의 파일들을 전부 트림하시겠습니까?",
|
|
||||||
"TRIMMING_FAILED_CONTINUE": "트림 실패\n\n계속하시겠습니까?",
|
|
||||||
"N_OF_N_FILES_TRIMMED_N_OF_N_NOT_OF_SAME_TYPE_X_SAVED": "%lu/%lu개의 파일 트림 완료\n%lu/%lu개의 파일은 같은 유형이 아님\n%s 저장됨",
|
|
||||||
"N_OF_N_FILES_TRIMMED_X_SAVED": "%lu/%lu개 파일 트림 완료\n%s 저장됨",
|
|
||||||
"FILE_CANT_BE_TRIMMED": "파일을 트림할 수 없습니다.",
|
|
||||||
"FILE_ALREADY_TRIMMED": "이 파일은 이미 트림되었습니다.",
|
|
||||||
"PATH_CURRENT_SIZE_TRIMMED_SIZE_DIFFERENCE_TRIM_FILE": "%s\n현재 크기: %s\n트림된 크기: %s\n차이: %s\n \n이 파일을 트림하시겠습니까?",
|
|
||||||
"TRIMMING_FAILED": "트림 실패.",
|
|
||||||
"PATH_TRIMMED_BY_X": "%s\n%s에 의해 트림되었습니다.",
|
|
||||||
"TRY_TO_RENAME_N_SELECTED_FILES": "선택된 %lu개의 파일의 이름을 전부 변경하시겠습니까?",
|
|
||||||
"N_OF_N_RENAMED": "%lu/%lu개 이름 변경 완료",
|
|
||||||
"COULD_NOT_RENAME_TO_GOOD_NAME": "좋은 이름으로 이름을 변경할 수 없습니다",
|
|
||||||
"SYSNAND_H_AND_S_INJECT": "시스낸드 안전을 위한 주의사항 주입",
|
|
||||||
"EMUNAND_H_AND_S_INJECT": "에뮤낸드 안전을 위한 주의사항 주입",
|
|
||||||
"H_AND_S_INJECT_SUCCESS": "안전을 위한 주의사항 주입 성공",
|
|
||||||
"H_AND_S_INJECT_FAILURE": "안전을 위한 주의사항 주입 실패",
|
|
||||||
"TRY_EXTRACT_ALL_N_SELECTED_FILES": "선택된 %lu개의 파일을 전부 추출하시겠습니까?",
|
|
||||||
"N_OF_N_FILES_EXTRACTED_N_OF_N_NOT_SAME_TYPE": "%lu/%lu개 파일 추출됨\n%lu/%lu개는 같은 유형이 아닙니다",
|
|
||||||
"N_OF_N_FILES_EXTRACTED": "%lu/%lu개 파일 추출됨",
|
|
||||||
"EXTRACTING_DOT_CODE": ".code를 추출 중이니 잠시만 기다려주세요...",
|
|
||||||
"PATH_EXT_EXTRACTED_TO_OUT": "%s\n%s %s에 추출됨",
|
|
||||||
"DOT_CODE_EXTRACT_FAILED": ".code 추출 실패",
|
|
||||||
"TRANSFER_TO_SYSNAND": "시스낸드로 이동",
|
|
||||||
"TRANSFER_TO_EMUNAND": "EmuNAND로 이동",
|
|
||||||
"CTRNAND_TRANSFER_SUCCESS": "CTRNAND 이동 성공",
|
|
||||||
"CTRNAND_TRANSFER_FAILED": "CTRNAND 이동 실패",
|
|
||||||
"NO_VALID_DESTINATION_FOUND": "유효한 목적지를 찾을 수 없습니다",
|
|
||||||
"NAND_RESTORE_SUCCESS": "NAND 복원 성공",
|
|
||||||
"NAND_RESTORE_FAILED": "NAND 복원 실패",
|
|
||||||
"REBUILD_NCSD_SUCCESS": "NCSD 리빌드 성공",
|
|
||||||
"REBUILD_NCSD_FAILED": "NCSD 리빌드 실패",
|
|
||||||
"PATH_NCCHINFO_PADGEN_SUCCESS": "%s\nNCCHinfo padgen 성공%c출력 폴더: %s",
|
|
||||||
"PATH_NCCHINFO_PADGEN_FAILED": "%s\nNCCHinfo padgen 실패%c%0.0s",
|
|
||||||
"UPDATING_EMBEDDED_BACKUP": "임베드된 백업 업데이트 중...",
|
|
||||||
"BACKUP_UPDATE_NOT_REQUIRED": "백업 업데이트: 필요 없음",
|
|
||||||
"BACKUP_UPDATE_COMPLETED": "백업 업데이트: 완료",
|
|
||||||
"BACKUP_UPDATE_FAILED": "백업 업데이트: 실패!",
|
|
||||||
"WARNING_KEYS_NOT_VERIFIED_CONTINUE_AT_YOUR_OWN_RISK": "경고: 키가 검증되지 않았습니다.\n위험을 감수하고 진행하시겠습니까?",
|
|
||||||
"AESKEYDB_INIT_SUCCESS": "AESkeydb 초기화 성공",
|
|
||||||
"AESKEYDB_INIT_FAILED": "AESkeydb 초기화 실패",
|
|
||||||
"AESKEYDB_INSTALL_SUCCESS": "AESkeydb 설치 성공",
|
|
||||||
"AESKEYDB_INSTALL_FAILED": "AESkeydb 설치 실패",
|
|
||||||
"INSTALL_TO_FIRM0": "FIRM0에 설치",
|
|
||||||
"INSTALL_TO_FIRM1": "FIRM1에 설치",
|
|
||||||
"INSTALL_TO_BOTH": "양쪽에 설치",
|
|
||||||
"PATH_N_KB_INSTALL_TO_SYSNAND": "%s (%dkB)\n시스낸드에 설치하시겠습니까?",
|
|
||||||
"PATH_N_KB_INSTALL_SUCCESS": "%s (%dkB)\n설치 성공",
|
|
||||||
"PATH_N_KB_INSTALL_FAILED": "%s (%dkB)\n설치 실패",
|
|
||||||
"WARNING_DO_NOT_RUN_UNTRUSTED_SCRIPTS": "경고: 신뢰할 수 없는 출처로부터의\n스크립트를 실행하지 마세요.\n \n스크립트를 실행할까요?",
|
|
||||||
"SCRIPT_EXECUTE_SUCCESS": "스크립트 실행 성공",
|
|
||||||
"SCRIPT_EXECUTE_FAILURE": "스크립트 실행 실패",
|
|
||||||
"ERROR_CANNOT_VIEW_FILE": "오류: 파일을 볼 수 없습니다\n(아마도 파일이 너무 큽니다)",
|
|
||||||
"SAVEGAME_DUMPED_TO_OUT": "게임 세이브를 %s로 덤프했습니다.",
|
|
||||||
"SAVEGAME_DUMP_FAILED": "게임 세이브 덤프 실패!",
|
|
||||||
"GBA_SAVEGAME_MUST_BE_IN_CLIPBOARD": "GBA VC 게임 세이브가\n클립보드에 있어야 합니다.",
|
|
||||||
"SAVEGAME_INJECT_SUCCESS": "게임 세이브 주입 성공.",
|
|
||||||
"SAVEGAME_INJECT_FAILED": "게임 세이브 주입 실패!",
|
|
||||||
"FONT_WILL_BE_ACTIVE_ON_NEXT_BOOT": "다음 부팅에 글꼴이 활성화됩니다",
|
|
||||||
"LANGUAGE_WILL_BE_ACTIVE_ON_NEXT_BOOT": "다음 부팅에 언어가 활성화됩니다",
|
|
||||||
"HOME_MORE_MENU_SELECT_ACTION": "HOME 더보기... 메뉴입니다.\n작업을 선택하세요:",
|
|
||||||
"SD_FORMAT_MENU": "SD 포맷 메뉴",
|
|
||||||
"BONUS_DRIVE_MENU": "보너스 드라이브 설정",
|
|
||||||
"SWITCH_EMUNAND": "EmuNAND 전환",
|
|
||||||
"BUILD_SUPPORT_FILES": "지원 파일 빌드",
|
|
||||||
"RESTORE_H_AND_S": "안전을 위한 주의사항 복구",
|
|
||||||
"SET_RTC_DATE_TIME": "RTC 날짜 및 시간 설정",
|
|
||||||
"CONFGURE_BRIGHTNESS": "밝기 설정",
|
|
||||||
"CALIBRATE_TOUCHSCREEN": "터치스크린 보정",
|
|
||||||
"SYSTEM_INFO": "시스템 정보",
|
|
||||||
"SHOW_README": "ReadMe 표시",
|
|
||||||
"INITIALIZING_SD_FAILED_RETRY": "SD카드 초기화에 실패했습니다! 다시 시도하시겠습니까?",
|
|
||||||
"SETUP_FAILED": "설치에 실패했습니다!",
|
|
||||||
"CURRENT_EMUNAND_OFFSET_IS_N_SWITCH_TO_NEXT": "현재 EmuNAND 오프셋은 %06lX입니다.\n다음 오프셋으로 전환하시겠습니까?",
|
|
||||||
"BUILT_IN_OUT_STATUSES": "%s에 아래 파일이\n빌드되었습니다:\n \n%-18.18s %s\n%-18.18s %s\n%-18.18s %s",
|
|
||||||
"OK_SYS_EMU": "완료 (Sys&Emu)",
|
|
||||||
"OK_SYS": "완료 (Sys)",
|
|
||||||
"FAILED": "실패",
|
|
||||||
"RESTORE_H_AND_S_EMUNAND": "안전을 위한 주의사항 복원 (에뮤낸드)",
|
|
||||||
"RESTORE_H_AND_S_SYSNAND": "안전을 위한 주의사항 복원(시스낸드)",
|
|
||||||
"TITLE_SET_RTC_DATE_TIME": "RTC 날짜&시간 설정:",
|
|
||||||
"NEW_RTC_DATE_TIME_IS_TIME": "새 RTC 날짜 및 시간은:\n%s\n입니다.\n힌트: 홈 메뉴 시간은\nRTC 설정 후 수동으로\n조정해야 합니다.",
|
|
||||||
"TOUCHSCREEN_CALIBRATION_SUCCESS": "터치스크린 보정 완료!",
|
|
||||||
"TOUCHSCREEN_CALIBRATION_FAILED": "터치스크린 보정 실패!",
|
|
||||||
"GODMODE9_README_TOC": "GodMode9 ReadMe 읽기 목차",
|
|
||||||
"ESSENTIAL_BACKUP_NOT_FOUND_CREATE_NOW": "필수 파일 백업을 찾을 수 없습니다.\n지금 생성할까요?",
|
|
||||||
"BACKUP_EMBEDDED_WRITTEN_TO_OUT": "백업은 시스낸드에 내장된 백업에 작성하고\n%s으로 전송합니다.",
|
|
||||||
"RTC_DATE_TIME_SEEMS_TO_BE_WRONG_SET_NOW": "RTC 날짜&시간이 잘못 설정되었습니다.\n지금 설정하시겠습니까?",
|
|
||||||
"RESUME_GODMODE9": "GodMode9 재개",
|
|
||||||
"RESUME_BOOTLOADER": "부트로더 재개",
|
|
||||||
"SELECT_PAYLOAD": "페이로드 선택...",
|
|
||||||
"SELECT_SCRIPT": "스크립트를 선택하세요...",
|
|
||||||
"SELECT_LUA_SCRIPT": "Lua 스크립트를 선택하세요...",
|
|
||||||
"POWEROFF_SYSTEM": "시스템 종료",
|
|
||||||
"REBOOT_SYSTEM": "시스템 재부팅",
|
|
||||||
"FLAVOR_BOOTLOADER_SELECT_OPTION": "%s 부트로더 메뉴입니다.\n작업을 선택하세요:",
|
|
||||||
"BOOTLOADER_PAYLOADS_MENU_SELECT_PAYLOAD": "부트로더 페이로드 메뉴입니다.\n페이로드를 선택하세요:",
|
|
||||||
"BOOTLOADER_SCRIPTS_MENU_SELECT_SCRIPT": "부트로더 스크립트 메뉴입니다.\n스크립트를 선택하세요:",
|
|
||||||
"NO_BOOTABLE_FIRM_FOUND_RESUMING_GODMODE9": "부팅 가능한 FIRM을 찾을 수 없습니다.\nGodMode9를 다시 시작합니다...",
|
|
||||||
"OUT_OF_MEMORY": "메모리 부족.",
|
|
||||||
"INVALID_DIRECTORY_OBJECT": "잘못된 디렉토리 오브젝트",
|
|
||||||
"INVALID_ROOT_DIRECTORY": "잘못된 최상위 폴더입니다.",
|
|
||||||
"WRITE_PERMISSIONS_WERE_CHANGED_RELOCK": "쓰기 권한이 변경되었습니다.\n다시 잠그시겠습니까?",
|
|
||||||
"OPEN_TITLE_MANAGER": "타이틀 관리자 열기",
|
|
||||||
"SEARCH_FOR_FILES": "파일 찾기...",
|
|
||||||
"FIX_CMACS_FOR_DRIVE": "드라이브 CMAC 수리",
|
|
||||||
"SHOW_DIRECTORY_INFO": "폴더 정보 보기",
|
|
||||||
"SHOW_DRIVE_INFO": "드라이브 정보 보기",
|
|
||||||
"FAILED_SETTING_UP_TITLE_MANAGER": "타이틀 관리자 설정에 실패했습니다!",
|
|
||||||
"SEARCH_FILE_ENTER_SEARCH_BELOW": "%s를 검색하시겠습니까?\n아래에 검색어를 입력하세요.",
|
|
||||||
"FOUND_N_RESULTS": "%lu개의 결과를 찾았습니다.",
|
|
||||||
"FIX_CMACS_FOR_DRIVE_FINISHED": "드라이브 CMAC 수리가 완료되었습니다.",
|
|
||||||
"FAILED_TO_ANALYZE_DRIVE": "드라이브 분석 실패\n",
|
|
||||||
"FAILED_TO_ANALYZE_DIR": "폴더 분석에 실패했습니다\n",
|
|
||||||
"NOT_ALLOWED_IN_VIRTUAL_PATH": "가상 경로에서 허용되지 않음",
|
|
||||||
"DELETE_N_PATHS": "%lu개의 경로를 삭제하시겠습니까?",
|
|
||||||
"DELETING_FILES_PLEASE_WAIT": "파일을 삭제 중입니다. 잠시만 기다려 주세요...",
|
|
||||||
"FAILED_DELETING_N_OF_N_PATHS": "%lu/%lu개의 경로를 삭제하지 못했습니다.",
|
|
||||||
"DELETE_FILE": "\"%s\"를 삭제할까요?",
|
|
||||||
"FAILED_DELETING_PATH": "삭제 실패:\n%s",
|
|
||||||
"NOT_ALLOWED_IN_SEARCH_DRIVE": "검색 드라이브에서 허용되지 않습니다.",
|
|
||||||
"NOT_ALLOWED_IN_VIRTUAL_GAME_PATH": "가상 게임 경로에서 허용되지 않습니다.",
|
|
||||||
"NOT_ALLOWED_IN_XORPAD_DRIVE": "XORped 드라이브에서 허용되지 않습니다.",
|
|
||||||
"NOT_ALLOWED_IN_GAMECART_DRIVE": "게임 카드에서 허용되지 않습니다.",
|
|
||||||
"NOT_ALLOWED_IN_ALIAS_PATH": "별칭 경로에서 허용되지 않습니다.",
|
|
||||||
"COPY_PATHS": "경로 복사",
|
|
||||||
"MOVE_PATHS": "경로 이동",
|
|
||||||
"PASTE_FILE_HERE": "여기에 \"%s\"를 붙여넣을까요?",
|
|
||||||
"PASTE_N_PATHS_HERE": "%lu 경로를 여기에 붙여넣으시겠습니까?",
|
|
||||||
"FAILED_COPYING_PATH_PROCESS_REMAINING": "경로 복사 실패:\n%s\n남은 프로세스?",
|
|
||||||
"FAILED_COPYING_PATH": "경로 복사 실패:\n%s",
|
|
||||||
"FAILED_MOVING_PATH_PROCESS_REMAINING": "?",
|
|
||||||
"FAILED_MOVING_PATH": "경로 이동 실패:\n%s",
|
|
||||||
"RENAME_FILE_ENTER_NEW_NAME_BELOW": "%s의 이름을 바꿀까요?\n아래에 새 이름을 입력하세요.",
|
|
||||||
"FAILED_RENAMING_PATH": "경로 이름 변경 실패:\n%s",
|
|
||||||
"CREATE_A_NEW_ENTRY_HERE_SELECT_TYPE": "여기에 새 항목을 만들까요?\n종류를 선택하세요.",
|
|
||||||
"CREATE_A_FOLDER": "폴더 생성",
|
|
||||||
"CREATE_A_DUMMY_FILE": "더미 파일 만들기",
|
|
||||||
"CREATE_NEW_FOLDER_HERE_ENTER_NAME_BELOW": "여기에 새 폴더를 생성할까요?\n아래에 이름을 입력하세요.",
|
|
||||||
"CREATE_NEW_FILE_HERE_ENTER_NAME_BELOW": "여기에 새 파일을 생성할까요?\n아래에 이름을 입력하세요.",
|
|
||||||
"CREATE_NEW_FILE_HERE_ENTER_SIZE_BELOW": "여기에 새 파일을 생성할까요?\n아래에 파일 크기를 입력하세요.",
|
|
||||||
"FAILED_CREATING_FOLDER_PATH": "폴더 생성 실패:\n%s",
|
|
||||||
"FAILED_CREATING_FILE_PATH": "파일 생성 실패:\n%s",
|
|
||||||
"TITLE_MANAGER": "타이틀 관리자",
|
|
||||||
"BRICK_MY_3DS": "내 3DS를 벽돌로 만들기",
|
|
||||||
"LANGUAGE": "언어...",
|
|
||||||
"SCRIPTS": "스크립트...",
|
|
||||||
"LUA_SCRIPTS": "Lua 스크립트...",
|
|
||||||
"PAYLOADS": "페이로드...",
|
|
||||||
"MORE": "더보기...",
|
|
||||||
"BRACKET_MORE": "[더보기...]",
|
|
||||||
"HOME_BUTTON_PRESSED_SELECT_ACTION": "HOME 버튼을 눌렀습니다.\n작업을 선택하세요:",
|
|
||||||
"POWER_BUTTON_PRESSED_SELECT_ACTION": "전원 버튼을 눌렀습니다.\n작업을 선택하세요:",
|
|
||||||
"1_DRIVE_NAND_TWL": "[1:] 낸드 / TWL",
|
|
||||||
"4_DRIVE_NAND_TWL": "[4:] 낸드 / TWL",
|
|
||||||
"A_DRIVE_SD_CARD": "[A:] SD 카드",
|
|
||||||
"B_DRIVE_SD_CARD": "[B:] SD 카드",
|
|
||||||
"TITLE_MANAGER_MENU_SELECT_TITLES_SOURCE": "타이틀 관리자 메뉴입니다.\n타이틀의 소스를 선택하세요:",
|
|
||||||
"LANGUAGES_DIRECTORY_NOT_FOUND": "언어 경로를 찾을 수 없습니다.\n(기본 경로: 0:/gm9/%s)",
|
|
||||||
"SCRIPTS_DIRECTORY_NOT_FOUND": "스크립트 경로를 찾을 수 없습니다.\n(기본 경로: 0:/gm9/%s)",
|
|
||||||
"LUA_SCRIPTS_DIRECTORY_NOT_FOUND": "Lua 스크립트 경로를 찾을 수 없습니다.\n(기본 경로: 0:/gm9/%s)",
|
|
||||||
"HOME_LANGUAGE_MENU_SELECT_LANGUAGE": "HOME 언어... 메뉴입니다.\n언어를 선택하세요:",
|
|
||||||
"HOME_SCRIPTS_MENU_SELECT_SCRIPT": "HOME 스크립트... 메뉴입니다.\n스크립트를 선택하세요:",
|
|
||||||
"HOME_LUA_SCRIPTS_MENU_SELECT_SCRIPT": "HOME Lua 스크립트... 메뉴입니다.\n스크립트를 선택하세요:",
|
|
||||||
"PAYLOADS_DIRECTORY_NOT_FOUND": "페이로드 경로를 찾을 수 없습니다.\n(기본 경로: 0:/gm9/%s)",
|
|
||||||
"HOME_PAYLOADS_MENU_SELECT_PAYLOAD": "HOME 페이로드... 메뉴입니다.\n페이로드를 선택하세요:",
|
|
||||||
"UNEXPECTED_SD_CARD_REMOVAL_TO_PREVENT_DATA_LOSS_UNMOUNT_BEFORE_EJECT": "!예기치 않은 SD 카드 제거!\n \n데이터 손실을 방지하려면 SD 카드를\n꺼내기 전에 마운트를 해제하세요.",
|
|
||||||
"FLAVOR_SCRIPTS_MENU_SELECT_SCRIPT": "%s 스크립트 메뉴입니다.\n스크립트를 선택하세요:",
|
|
||||||
"COMPILED_AS_SCRIPT_AUTORUNNER_BUT_NO_SCRIPT_DERP": "스크립트 자동 실행기로 컴파일되었지만\n스크립트가 존재하지 않습니다.\n \n이런!",
|
|
||||||
"KEYBOARD_CONTROLS_DETAILS": "키보드 컨트롤:\n \n←/→ - 커서 이동\nR - 대문자/대문자 잠금\nX - 문자 삭제\nY - 문자 삽입\nA - 제출\nB - 취소\n \nSELECT를 눌러\n클래식 프롬프트로 전환",
|
|
||||||
"TOUCH_CROSSHAIRS_TO_CALIBRATE_TOUCHSCREEN_USE_STYLUS": "빨간색 십자가를 터치해서\n터치 스크린을 보정합니다.\n \n최상의 결과를 위해선\n터치 펜을 사용하세요!",
|
|
||||||
"INVALID": "유효하지 않음",
|
|
||||||
"TO_PROCEED_ENTER_THIS": "진행하려면, 버튼을 누르세요:",
|
|
||||||
"TO_PROCEED_HOLD_X": "계속하려면, <X>를 꾹 누르세요:",
|
|
||||||
"N_MORE": " [%d개 더 있음]",
|
|
||||||
"CANCEL": "취소",
|
|
||||||
"R_FAST_SCROLL_L_CLEAR_DATA": "R - (↑↓) 빠른 스크롤\nL - 데이터 지우기",
|
|
||||||
"X_REMOVE_CHAR_Y_INSERT_CHAR": "X - 문자 제거\nY - 문자 삽입",
|
|
||||||
"ETA_N_HOUR_N_MIN_N_SEC": "남은 시간 %02llu시간 %02llu분 %02llu초",
|
|
||||||
"ETA_N_MIN_N_SEC": "남은 시간 %02llu분 %02llu초",
|
|
||||||
"BRIGHTNESS_CONTROLS": "[←] 밝기 감소\n[→] 밝기 증가\n \n[X] 볼륨 슬라이더 컨트롤 사용\n[A] 현재 밝기로 설정\n[B] 취소",
|
|
||||||
"SEARCHING_PLEASE_WAIT": "검색 중입니다, 잠시 기다려 주십시오...",
|
|
||||||
"RENAME_TO_GOOD_NAME": "좋은 이름으로 바꾸겠습니까?",
|
|
||||||
"SD_WRITE_PROTECTED_CANT_CONTINUE": "SD 카드가 쓰기 금지되어 있습니다!\n계속할 수 없습니다.",
|
|
||||||
"SYSNAND_LVL_N": "시스낸드 (lvl%lu)",
|
|
||||||
"EMUNAND_LVL_N": "에뮤낸드 (lvl%lu)",
|
|
||||||
"GAME_IMAGES": "게임 이미지",
|
|
||||||
"GAMECART_SAVES": "게임 카트리지 세이브",
|
|
||||||
"IMAGES": "이미지",
|
|
||||||
"MEMORY_AREAS": "메모리 영역",
|
|
||||||
"SD_SYSTEM_DATA": "SD 시스템 데이터",
|
|
||||||
"SD_CARD": "SD 카드",
|
|
||||||
"RAM_DRIVE": "램 드라이브",
|
|
||||||
"WRITING_TO_DRIVE_IS_LOCKED_UNLOCK_NOW": "%s 으로의 쓰기는 잠겨있습니다!\n지금 해제하시겠습니까?",
|
|
||||||
"UNLOCK_WRITE_FOR_DRIVE_NOT_ALLOWED": "%s 에 대한 쓰기 권한 잠금 해제는\n허용되지 않습니다.",
|
|
||||||
"ENABLE_BASE_WRITE": "기본 쓰기 권한을\n활성화하려고 합니다.",
|
|
||||||
"ENABLE_SD_WRITE": "SD 카드 쓰기 권한을\n활성화하려고 합니다.",
|
|
||||||
"ENABLE_IMAGE_WRITE": "이미지 쓰기 권한을\n활성화하려고 합니다.",
|
|
||||||
"ENABLE_RAM_DRIVE_WRITE": "RAM 드라이브 쓰기 권한을\n활성화하려고 합니다.",
|
|
||||||
"ENABLE_EMUNAND_0_WRITE": "에뮤낸드 lvl0 쓰기 권한을\n활성화하려고 합니다.",
|
|
||||||
"ENABLE_SYSNAND_0_WRITE": "시스낸드 lvl0 쓰기 권한을\n활성화하려고 합니다.",
|
|
||||||
"ENABLE_EMUNAND_1_WRITE": "에뮤낸드\nlv1 쓰기 권한을 활성화하려고 합니다.\n \n이렇게 하면 시스템 데이터, 설치,\n사용자 데이터 및 게임 세이브를\n수정할 수 있습니다.",
|
|
||||||
"ENABLE_SYSNAND_1_WRITE": "SysNAND\nlv1 쓰기 권한을 활성화하려고 합니다.\n \n이렇게 하면 시스템 데이터, 설치,\n사용자 데이터 및 게임 세이브를\n수정할 수 있습니다.",
|
|
||||||
"ENABLE_GAMECART_SAVE_WRITE": "게임 카트리지 세이브 쓰기 권한을\n활성화하려고 합니다.",
|
|
||||||
"ENABLE_SYSNAND_2_WRITE": "!조심하세요!\n \nSysNAND lvl2 쓰기 권한을\n활성화하려고 합니다.\n \n이렇게 하면 복구 불가능한 시스템 데이터를\n수정할 수 있습니다!",
|
|
||||||
"ENABLE_MEMORY_WRITE": "조심하세요!\n \n메모리 쓰기 권한을\n활성화하려고 합니다.\n \n특정 영역에 쓰기를 하는 것은\n예기치 않은 결과가 발생할 수 있습니다.",
|
|
||||||
"ENABLE_SD_DATA_WRITE": "!권장하지 않습니다!\n \nSD 데이터 쓰기 권한을\n활성화하려고 합니다.\n \n이곳의 모든 내용은\n암호화되어 있습니다.\n설치, 사용자 데이터 및\n게임 세이브의 수정을 위해\nA:/B: 드라이브를 대신\n사용하는 것이 좋습니다.",
|
|
||||||
"ENABLE_SYSNAND_3_WRITE": "!이것이 유일한 경고입니다!\n \n시스낸드 lvl3 쓰기 권한을\n활성화 하려 하고 있습니다.\n \n이 경우 부트로더 설치 파일이나,\n필수 시스템 파일을 덮어쓰거나\n콘솔을 벽돌로 만들 수 있습니다!",
|
|
||||||
"UNLOCK_WRITE_NOT_ALLOWED": "쓰기 권한 잠금 해제는 허용되지 않습니다.",
|
|
||||||
"CANT_UNLOCK_WRITE_TRY_GODMODE9": "쓰기 권한을 해제할 수 없습니다.\nGodMode9을 대신 사용해보세요!",
|
|
||||||
"ERROR_SD_TOO_SMALL": "오류: SD 카드가 너무 작습니다",
|
|
||||||
"WARNING_PROCEEDING_WILL_FORMAT_SD_DELETE_ALL_DATA": "!경고!\n\n진행하면 이 SD가 포맷됩니다.\n이로 인해 저장된\n모든 데이터가 영구적으로 삭제됩니다.",
|
|
||||||
"FORMAT_BONUS_DRIVE_DELETE_ALL_DATA": "보너스 드라이브를 포맷하시겠습니까?\n이 작업은 드라이브의 모든 데이터를 \n영구적으로 삭제합니다.",
|
|
||||||
"FORMATTING_SD_PLEASE_WAIT": "SD 카드 포맷 중, 잠시만 기다려 주세요...",
|
|
||||||
"FORMATTING_DRIVE_PLEASE_WAIT": "드라이브 포맷 중, 잠시만 기다려 주세요...",
|
|
||||||
"ERROR_SD_CARD_IO_FAILURE": "오류: SD 카드 입출력 실패",
|
|
||||||
"FILE_IS_MOUNTED_UNMOUNT_TO_UNLOCK": "파일이 현재 마운트되었습니다.\n마운트 해제하여 잠금을 해제하시겠습니까?",
|
|
||||||
"ERROR_CANT_INJECT_FILE_INTO_ITSELF": "오류: 자기자신에 파일을 삽입할 수 없습니다",
|
|
||||||
"OPERATION_WOULD_WRITE_BEYOND_EOF": "작업이 파일 끝을 넘어 기록하려고 합니다.",
|
|
||||||
"NOT_ENOUGH_DATA_IN_FILE": "파일에 데이터가 충분하지 않습니다.",
|
|
||||||
"CANCEL_IS_NOT_ALLOWED_HERE": "여기에서는 취소가 허용되지 않습니다.",
|
|
||||||
"B_DETECTED_CANCEL": "B 버튼이 감지되었습니다. 취소할까요?",
|
|
||||||
"ERROR_CANNOT_APPEND_FOLDER": "오류: 폴더를 추가할 수 없습니다",
|
|
||||||
"ERROR_OVERWRITING_FILE_WITH_DIR": "오류: 디렉토리가 있는 파일을 덮어씌우고 있습니다",
|
|
||||||
"ERROR_OVERWRITING_DIR_WITH_FILE": "오류: 파일로 디렉토리를 덮어쓰고 있습니다.",
|
|
||||||
"ERROR_CANNOT_OPEN_DESTINATION_FILE": "오류: 대상 파일을 열 수 없습니다",
|
|
||||||
"ERROR_NOT_ENOUGH_SPACE_AVAILABLE": "오류: 사용 가능한 공간이 충분하지 않습니다",
|
|
||||||
"ERROR_ONLY_FAT_FILES_CAN_BE_MOVED": "오류: FAT 파일만 이동할 수 있습니다",
|
|
||||||
"ERROR_DESTINATION_IS_PART_OF_ORIGIN": "오류: 목적지가 출발지의 일부입니다.",
|
|
||||||
"ERROR_DESTINATION_EQUALS_ORIGIN_CHOOSE_ANOTHER_NAME": "목적지가 출발지와 같습니다.\n다른 이름을 선택하시겠습니까?",
|
|
||||||
"CHOOSE_NEW_NAME": "새 이름 선택",
|
|
||||||
"OVERWRITE_FILES": "파일 덮어쓰기",
|
|
||||||
"SKIP_FILES": "파일 건너뛰기",
|
|
||||||
"OVERWRITE_ALL": "모두 덮어쓰기",
|
|
||||||
"SKIP_ALL": "모두 건너뛰기",
|
|
||||||
"DESTINATION_ALREADY_EXISTS": "대상이 이미 존재합니다:\n%s",
|
|
||||||
"CHOOSE_NEW_DESTINATION_NAME": "새로운 목적지 이름 선택",
|
|
||||||
"COPY_OPERATION_IS_NOT_ALLOWED": "복사 작업이 허용되지 않습니다.",
|
|
||||||
"DESTINATION_EQUALS_ORIGIN": "목적지가 시작점과 같습니다.",
|
|
||||||
"ENTRY_NOT_FOUND_PATH_INJECT_INTO_PATH_INSTEAD": "항목을 찾을 수 없습니다: %s\n대신 %s에 주입하시겠습니까?",
|
|
||||||
"FILE_SMALLER_THAN_SPACE_SIZES_CONTINUE": "사용 가능 공간보다 파일이 작습니다:\n%s (%s)\n%s (%s)\n계속하시겠습니까?",
|
|
||||||
"NO_USABLE_ENTRIES_FOUND": "사용할 수 있는 항목을 찾을 수 없습니다.",
|
|
||||||
"BEAT_NO_ERROR": "오류 없음",
|
|
||||||
"BEAT_END_OF_ACTION_LIST": "작업 목록 끝",
|
|
||||||
"BEAT_ABORTED_BY_USER": "사용자에 의해 중단됨",
|
|
||||||
"BEAT_FAILED_TO_READ_WRITE_FILE": "파일 읽기/쓰기 실패",
|
|
||||||
"BEAT_ATTEMPTED_TO_WRITE_BEYOND_EOF": "파일의 끝을 넘어 쓰려고 했습니다.",
|
|
||||||
"BEAT_INVALID_PATCH_FILE": "잘못된 패치 파일",
|
|
||||||
"BEAT_INVALID_INPUT_FILE": "잘못된 입력 파일",
|
|
||||||
"BEAT_OUTPUT_FILE_CHECKSUM_MISMATCH": "출력 파일 체크섬 불일치",
|
|
||||||
"BEAT_FILE_CHECKSUM_FAILED": "파일 체크섬 실패",
|
|
||||||
"BEAT_EXPECTED_MORE_PATCH_DATA": "더 많은 패치 데이터가 필요합니다.",
|
|
||||||
"BEAT_OUT_OF_MEMORY": "메모리 부족",
|
|
||||||
"BEAT_UNKNOWN_ERROR": "알 수 없는 오류",
|
|
||||||
"FAILED_TO_INITIALIZE_BPM_FILE": "BPM파일 초기화 실패:\n%s",
|
|
||||||
"FAILED_TO_INITIALIZE_BPS_FILE": "BPM파일 초기화 실패:\n%s",
|
|
||||||
"PATCH_SUCCESSFULLY_APPLIED": "패치가 성공적으로 적용되었습니다",
|
|
||||||
"PATCHING_ABORTED_BY_USER": "사용자에 의해 패치 적용이 중단되었습니다",
|
|
||||||
"FAILED_TO_RUN_PATCH": "%s 패치를\n실행하지 못했습니다",
|
|
||||||
"DECOMPRESSING_DOT_CODE": ".code 파일 압축 해제 중...",
|
|
||||||
"DECOMPRESSING_DOT_CODE_B_DETECTED_CANCEL": ".code 파일 압축 해제 중...\nB 버튼이 감지되었습니다. 취소할까요?",
|
|
||||||
"COMPRESSING_DOT_CODE": ".code 파일 압축 중...",
|
|
||||||
"COMPRESSING_DOT_CODE_B_DETECTED_CANCEL": ".code 파일 압축 중...\nB 버튼이 감지되었습니다. 취소할까요?",
|
|
||||||
"PATCH_MOST_LIKELY_NOT_FOR_THIS_FILE": "이 패치는 이 파일에 대해 의도된 것이 아닐 가능성이 가장 큽니다.",
|
|
||||||
"YOU_MOST_LIKELY_APPLIED_PATCH_ON_OUTPUT": "출력 파일에 패치를 적용했을 수 있습니다.",
|
|
||||||
"PATCH_TECHNICALLY_VALID_BUT_SEEMS_SCRAMBLED": "이 패치는 기술적으로 유효하지만, 섞여 있거나 잘못된 형식인 것 같습니다.",
|
|
||||||
"PATCH_IS_INVALID": "패치가 유효하지 않습니다.",
|
|
||||||
"FILES_BIGGER_THAN_16MB_IPS_DOESNT_SUPPORT_THAT": "파일 하나 또는 두 개가 모두 16MB보다 큽니다.\nIPS 형식은 이를 지원하지 않습니다.",
|
|
||||||
"REQUESTED_FILE_PATH_WAS_INVALID": "요청한 파일 경로가 유효하지 않습니다.",
|
|
||||||
"PATCHING_CANCELED": "패치가 취소되었습니다.",
|
|
||||||
"NOT_ENOUGH_MEMORY": "메모리가 부족합니다.",
|
|
||||||
"REGION_AMERICAS": "미국",
|
|
||||||
"REGION_AUSTRALIA": "호주",
|
|
||||||
"REGION_BRAZIL": "브라질",
|
|
||||||
"REGION_CHINA": "중국",
|
|
||||||
"REGION_EUROPE": "유럽",
|
|
||||||
"REGION_FRANCE": "프랑스",
|
|
||||||
"REGION_GERMANY": "독일",
|
|
||||||
"REGION_ITALY": "이탈리아",
|
|
||||||
"REGION_JAPAN": "일본",
|
|
||||||
"REGION_KOREA": "대한민국",
|
|
||||||
"REGION_SPAIN": "스페인",
|
|
||||||
"REGION_TAIWAN": "대만",
|
|
||||||
"REGION_UNKNOWN": "알 수 없음",
|
|
||||||
"REGION_UNITED_KINGDOM": "영국",
|
|
||||||
"REGION_MIDDLE_EAST": "중동",
|
|
||||||
"GENERATING_QR_CODE": "QR 코드 생성 중...",
|
|
||||||
"REINITIALIZING_SD_CARD": "SD 카드를 초기화하고 있습니다...",
|
|
||||||
"DUMPING_STATE_TO_SD_CARD": "SD 카드로 상태를 덤프 중...",
|
|
||||||
"PRESS_POWER_TO_TURN_OFF": "전원 버튼을 눌러 전원을 끕니다",
|
|
||||||
"CLEANING_UP_TITLES_PLEASE_WAIT": "타이틀을 정리 중입니다, 잠시만 기다려 주세요...",
|
|
||||||
"ERROR_NOT_NCCH_FILE": "오류: NCCH 파일이 아닙니다",
|
|
||||||
"ERROR_FILE_IS_TOO_SMALL": "오류: 파일이 너무 작습니다",
|
|
||||||
"ATTEMPT_FIX_THIS_TIME": "이번에만 고치기",
|
|
||||||
"ATTEMPT_FIX_ALWAYS": "항상 고치기",
|
|
||||||
"ABORT_VERIFICATION": "검증 중단",
|
|
||||||
"ERROR_BAD_CRYPTO_FLAGS": "오류: 잘못된 암호 플래그",
|
|
||||||
"ERROR_BAD_EXEFS_HEADER": "오류: 잘못된 ExeFS 헤더",
|
|
||||||
"ERROR_MISSING_EXTHEADER": "오류: ExtHeader가 누락되었습니다.",
|
|
||||||
"ERROR_CRYPTO_NOT_SET_UP": "오류: 암호화가 설정되지 않았습니다.",
|
|
||||||
"PATH_NCCH_VERIFICATION_FAILED_INFO": "%s\nNCCH 검증에 실패했습니다:\nExtHdr/ExeFS/RomFS: %s/%s/%s",
|
|
||||||
"OK": "완료",
|
|
||||||
"FAIL": "실패",
|
|
||||||
"ERROR_NOT_NCSD_FILE": "오류: NCSD 파일이 아닙니다",
|
|
||||||
"PATH_CONTENT_N_SIZE_AT_OFFSET_VERIFICATION_FAILED": "%s\n콘텐츠%lu (%08lX@%08lX):\n검증 실패",
|
|
||||||
"ERROR_PROBABLY_NOT_CIA_FILE": "오류: CIA 파일이 아닐 가능성 높음",
|
|
||||||
"ERROR_TMD_PROBABLY_CORRUPTED": "오류: TMD 파일이 아마 손상되었습니다",
|
|
||||||
"PATH_ID_N_SIZE_AT_OFFSET_VERIFICATION_FAILED": "%s\nID %08lX (%08llX@%08llX)\n확인 실패",
|
|
||||||
"ERROR_CDN_TITLEKEY_NOT_FOUND": "오류: CDN titlekey를 찾을 수 없습니다",
|
|
||||||
"DLC_CONTENT_IS_MISSING_IGNORE_ALL_AND_CONTINUE": "DLC 콘텐츠가 누락되었습니다.\n \n모두 무시하고 계속하시겠습니까?",
|
|
||||||
"PATH_SECTION_N_HASH_MISMATCH": "%s\n섹션 %lu 해시 불일치",
|
|
||||||
"ARM9_ENTRYPOINT_IS_MISSING": "ARM9 진입점이 없습니다.",
|
|
||||||
"WARNING_ARM11_ENTRYPOINT_IS_MISSING": "경고: ARM11 진입점이 없습니다.",
|
|
||||||
"ERROR_NOT_A_BOSS_FILE": "에러: BOSS 파일이 아닙니다",
|
|
||||||
"BOSS_PAYLOAD_HASH_MISMATCH_TRY_TO_FIX_IT": "BOSS 페이로드 해시 불일치.\n\n자동으로 고쳐 볼까요?",
|
|
||||||
"GENERIC_TICKET_PIRATE_LEGIT": "일반 티켓 (\"pirate legit\")",
|
|
||||||
"PERSONALIZED_TICKET_LEGIT": "개인화된 티켓 (legit)",
|
|
||||||
"ID_N_LEGIT_TICKET_IS_PERSONALIZED_USING_THIS_NOT_RECOMMENDED_CHOOSE_DEFAULT_ACTION": "ID %016llX\nLegit 티켓은 개인 전용입니다.\n이것을 사용하는 것은 추천하지 않습니다.\n기본 작업을 선택하세요:",
|
|
||||||
"ID_N_TITLEKEY_NOT_FOUND": "ID %016llX\nTitlekey를 찾을 수 없습니다.",
|
|
||||||
"ID_N_TMD_IN_TAD_NOT_LEGIT": "ID %016llX\nTAD 내의 TMD가 legit이 아닙니다.",
|
|
||||||
"ID_N_TMD_NOT_LEGIT": "ID %016llX\nTMD가 legit이 아닙니다.",
|
|
||||||
"ID_N_DOT_N_STATUS": "ID %016llX.%08lX\n%s",
|
|
||||||
"CONTENT_IS_CORRUPT": "콘텐츠 손상됨",
|
|
||||||
"INSERT_CONTENT_FAILED": "콘텐츠 삽입 실패",
|
|
||||||
"INSTALL_CONTENT_FAILED": "콘텐츠 설치 실패",
|
|
||||||
"INSTALL_ERROR_THIS_SYSTEM_IS_MISSING_DB_FILES_MAYBE_SD_MISSING_OR_UNINITIALIZED": "설치 오류:\n이 시스템에 하나 이상의\n.db 파일이 없습니다.\n \nSD 카드가 꽂혀있지 않거나\n기본 세팅을 안 하지 않았나요?",
|
|
||||||
"INSTALL_ERROR_THIS_SYSTEM_IS_MISSING_TICKET_DB": "설치 오류: \n시스템에 ticket.db\n파일이 없습니다.",
|
|
||||||
"SKIPPED_TITLE_0004008C000CBD00_NEEDS_SPECIAL_COMPILE_FLAGS": "타이틀을 스킵했습니다:\nid 0004008C000CBD00을 가진 타이틀은\n특수 컴파일러 플래그가 필요합니다.",
|
|
||||||
"ERROR_FAKE_SIGNED_TICKET_ONLY_VALID_SIGNED_TICKETS_CAN_BE_INSTALLED": "오류: 가짜 서명된 티켓\n \n유효하게 서명된 티켓만\n시스템에 설치할 수 있습니다.",
|
|
||||||
"PATH_ERROR_UNKNOWN_CID_N_THIS_TICKET_DOES_NOT_BELONG_TO_THIS_3DS": "%s\n오류: 알 수 없는 cid %08lX\n \n이 티켓은 이 3DS 콘솔 전용이\n아닙니다.",
|
|
||||||
"INSTALLING_TICKET": "티켓 설치 중...",
|
|
||||||
"POSSIBLY_BROKEN": "고장 가능성 높음",
|
|
||||||
"PERSONAL_LEGIT": "개인용 Legit",
|
|
||||||
"PERSONAL_LEGIT_DLC": "개인용 Legit DLC",
|
|
||||||
"PERSONAL_PIRATE_LEGIT": "개인용 복돌 Legit",
|
|
||||||
"PERSONAL_PIRATE_LEGIT_DLC": "개인용 복돌 Legit DLC",
|
|
||||||
"PERSONAL_CUSTOM": "개인용 커스텀",
|
|
||||||
"PERSONAL_CUSTOM_DLC": "개인용 커스텀 DLC",
|
|
||||||
"UNIVERSAL_LEGIT": "공용 Legit",
|
|
||||||
"UNIVERSAL_LEGIT_DLC": "공용 Legit DLC",
|
|
||||||
"UNIVERSAL_PIRATE_LEGIT": "공용 복돌 Legit",
|
|
||||||
"UNIVERSAL_PIRATE_LEGIT_DLC": "공용 복돌 Legit DLC",
|
|
||||||
"UNIVERSAL_CUSTOM": "공용 커스텀",
|
|
||||||
"UNIVERSAL_CUSTOM_DLC": "공용 커스텀 DLC",
|
|
||||||
"CONTENTS_IN_CIA_FOUND_TOTAL": "CIA 내의 콘텐츠: %lu/%lu개",
|
|
||||||
"CONTENTS_IN_CIA_TOTAL": "TMD 내의 콘텐츠: %lu개",
|
|
||||||
"CONSOLE_ID_N": "콘솔 ID: %08lX\n",
|
|
||||||
"SHOW_GAME_INFO_DETAILS": "%s\n%s %s 타이틀\n \n타이틀 ID: %016llX\n타이틀 버전: %lu.%lu.%lu\n콘텐츠 크기: %s\n%s\n%s \n티켓/TMD: %s/%s\n검증: %s",
|
|
||||||
"STATE_LEGIT": "legit",
|
|
||||||
"STATE_ILLEGIT": "illegit",
|
|
||||||
"STATE_UNKNOWN": "알 수 없음",
|
|
||||||
"STATE_INVALID": "유효하지 않음",
|
|
||||||
"STATE_PENDING_PROCEED_WITH_VERIFICATION": "보류\n \n검증을 진행할까요?",
|
|
||||||
"STATE_PASSED": "통과",
|
|
||||||
"STATE_FAILED": "실패",
|
|
||||||
"OUTPUT_FILE_ALREADY_EXISTS_UPDATE_THIS": "출력 파일이 이미 존재합니다.\n이 파일을 업데이트하시겠습니까?",
|
|
||||||
"DSI_ENHANCED": "DSi 향상",
|
|
||||||
"DSI_EXCLUSIVE": "DSi 전용",
|
|
||||||
"FIXING_CMACS_PLEASE_WAIT": "CMAC를 고치는 중입니다. 잠시만 기다려 주세요...",
|
|
||||||
"NCSD_HEADER_IS_NOT_VALID": "NCSD 헤더가 유효하지 않습니다",
|
|
||||||
"NAND_DUMP_MISSING_DATA": "NAND 덤프의 누락된 데이터",
|
|
||||||
"ERROR_CTR_MBR_IS_CORRUPT": "오류: CTR MBR이 손상됨",
|
|
||||||
"ERROR_TWL_MBR_IS_CORRUPT": "오류: TWL MBR이 손상됨",
|
|
||||||
"PATH_ERROR_CTR_PARTITION_N_IS_CORRUPT": "%s\n오류: CTR 파티션%lu 이 손상됨",
|
|
||||||
"PATH_ERROR_TWL_PARTITION_N_IS_CORRUPT": "%s\n오류: TWL 파티션%lu 이 손상됨",
|
|
||||||
"NO_VALID_FIRM_FOUND": "유효한 FIRM을 찾을 수 없음",
|
|
||||||
"ERROR_NAND_DUMP_IS_CORRUPT_STILL_CONTINUE": "오류: 낸드 덤프가 손상되었습니다.\n그래도 계속하시겠습니까?",
|
|
||||||
"ERROR_SYSTEM_IS_LOCKED": "오류: 시스템이 잠겼습니다.",
|
|
||||||
"ESSENTIAL_FILES_BACKUP_IS_REQUIRED_CREATE_ONE_NOW": "필수 파일 백업이 필요합니다.\n지금 생성할까요?",
|
|
||||||
"WARNING_PROCEEDING_WILL_OVERWRITE_SYSNAND_WITH_DUMP": "!경고!\n \n계속 진행하면 제공된 덤프로\n시스낸드를 덮어씁니다.\n \n(B9S/A9LH는 그대로 유지됩니다.)",
|
|
||||||
"IMAGE_NCSD_CORRUPT_OR_CUSTOMIZED_SAFE_RESTORE_NOT_POSSIBLE": "이미지의 NCSD가 손상되었거나 커스터마이징된 경우,\n안전 복원이 불가능합니다!",
|
|
||||||
"WARNING_NCSD_DIFFERS_BETWEEN_IMAGE_AND_LOCAL_ELEVATED_WRITE_PERMISSIONS_REQUIRED": "!경고!\n \nNCSD의 이미지와 로컬이 다릅니다,\n높은 수준의 쓰기 권한이 필요합니다.\n \n본인 책임 하에 진행하시겠습니까?",
|
|
||||||
"NOT_AN_INSTALLABLE_FIRM": "설치 가능한 FIRM이 아닙니다.",
|
|
||||||
"FIRM_LOAD_VERIFY_ERROR": "FIRM 로드/검증 오류.",
|
|
||||||
"PATH_FIRM_N_NOT_FOUND_OR_TOO_SMALL": "%s\n펌웨어%lu 를 찾을 수 없거나 크기가 너무 작습니다.",
|
|
||||||
"SECTOR_0X96_CRYPTO_FAIL": "섹터 0x96 암호화 실패.",
|
|
||||||
"SECTOR_0X96_CORRUPTED_PROVIDE_SECRET_SECTOR_BIN_TO_FIX": "섹터 0x96이 손상되었습니다.\n \n섹터 0x96을 복구하려면\n\"secret_sector.bin\"을 제공하십시오.",
|
|
||||||
"SECTOR_0X96_CORRUPTED_FIX_DURING_INSTALLATION": "섹터 0x96이 손상되었습니다.\n \n설치하는 동안\n섹터 0x96을 수정하시겠습니까?",
|
|
||||||
"WARNING_PROCEEDING_WILL_INSTALL_FIRM_TO_SYSNAND_AND_INJECT_SIGHAX_UNSUPPORTED_FIRM_WILL_BRICK": "!경고!\n \n계속 진행하면\n제공된 FIRM을 시스낸드에\n설치하고 sighax를 주입합니다.\n \n지원되지 않는 FIRM을 설치하면\n콘솔을 벽돌로 만들 것입니다!",
|
|
||||||
"INSTALLING_FIRM_PLEASE_WAIT": "FIRM 설치 중, 잠시만 기다려 주세요...",
|
|
||||||
"THIS_IS_BAD_FAILED_WRITING_SECTOR_0X96_TRY_FIX_BEFORE_REBOOT": "!좋지 않음!\n \n 0x96 섹터 쓰기에 실패했습니다%lu.\n재부팅하기 전에 고쳐보세요!",
|
|
||||||
"THIS_IS_BAD_FAILED_WRITING_FIRM_N_TRY_FIX_BEFORE_REBOOT": "!이건 나빠!\n \nFIRM%lu 작성 실패.\n재부팅 전에 수정하십시오!",
|
|
||||||
"CHECKING_INSTALLATION_PLEASE_WAIT": "설치 확인 중, 잠시만 기다려주세요...",
|
|
||||||
"THIS_IS_BAD_FAILED_VERIFYING_SECTOR_0X96_TRY_FIX_BEFORE_REBOOT": "!이건 나빠!\n \n섹터 0x96 검증 실패.\n재부팅 전에 수리해보세요!",
|
|
||||||
"THIS_IS_BAD_FAILED_VERIFYING_FIRM_N_TRY_FIX_BEFORE_REBOOT": "!좋지 않음!\n \nFIRM 확인에 실패했습니다%lu.\n재부팅하기 전에 고쳐보세요!",
|
|
||||||
"PERFECT_KEYDB_IS_ALREADY_INSTALLED": "완벽한 %s이(가) 이미 설치되었습니다!",
|
|
||||||
"PATH_NOT_PERFECT_KEYDB_IMAGE_CANNOT_INSTALL_TO_NAND": "%s\n완벽한 %s 이미지가 아닙니다.\n낸드에 설치할 수 없습니다!",
|
|
||||||
"PATH_FAILED_WRITING_KEYDB_TO_NAND": "%s\n%s를 NAND에 쓰기 실패했습니다!",
|
|
||||||
"USE_L_R_TO_SAVE": "(L+R 을 눌러 저장)",
|
|
||||||
"EASTER_NOT_FOUND": "(%s 찾을 수 없음)",
|
|
||||||
"BUILDING_TO_OUT_ARG": "%s로 빌드 중:\n%s...",
|
|
||||||
"EJECT_SD_CARD": "SD 카드를 꺼내세요...",
|
|
||||||
"INSERT_SD_CARD": "SD 카드를 삽입하세요...",
|
|
||||||
"SCRIPTERR_UNKNOWN_CMD": "알 수 없는 cmd",
|
|
||||||
"SCRIPTERR_BAD_NUMBER_OF_ARGS": "잘못된 # 개수",
|
|
||||||
"SCRIPTERR_UNRECOGNIZED_FLAGS": "인식할 수 없는 플래그",
|
|
||||||
"SCRIPTERR_ILLEGAL_FLAG": "불법 깃발",
|
|
||||||
"SCRIPTERR_UNRESOLVED_QUOTES": "해결되지 않은 인용",
|
|
||||||
"SCRIPTERR_TOO_MANY_ARGUMENTS": "인자가 너무 많습니다",
|
|
||||||
"SCRIPTERR_ARGUMENT_EXPAND_FAILED": "인수 확장이 실패했습니다.",
|
|
||||||
"SCRIPTERR_QUOTE_NOT_AN_ERROR": "'아니'라는 오류",
|
|
||||||
"SCRIPTERR_SYNTAX_ERROR_AFTER_IF": "'if' 다음에 구문 오류입니다.",
|
|
||||||
"SCRIPTERR_ELIF_WITHOUT_IF": "'if' 없이 'elif'",
|
|
||||||
"SCRIPTERR_SYNTAX_ERROR_AFTER_ELIF": "'elif' 다음에 구문 오류입니다.",
|
|
||||||
"SCRIPTERR_ELSE_WITHOUT_IF": "'if' 없는 'else'",
|
|
||||||
"SCRIPTERR_END_WITHOUT_IF": "'if' 없이 'end'",
|
|
||||||
"SCRIPTERR_FOR_INSIDE_FOR": "'for' 안에 'for'",
|
|
||||||
"SCRIPTERR_DIR_NOT_FOUND": "디렉터리를 찾을 수 없습니다.",
|
|
||||||
"SCRIPTERR_FOR_WITHOUT_NEXT": "'next' 없이 'for'",
|
|
||||||
"SCRIPTERR_NEXT_WITHOUT_FOR": "'for' 없이 'next'",
|
|
||||||
"SCRIPTERR_FORPATH_ERROR": "경로 오류",
|
|
||||||
"SCRIPTERR_LABEL_NOT_FOUND": "레이블을 찾을 수 없습니다",
|
|
||||||
"SCRIPTERR_USER_ABORT": "사용자 중단",
|
|
||||||
"SCRIPTERR_KEY_NOT_PRESSED": "키 입력되지 않음",
|
|
||||||
"SCRIPTERR_OUT_OF_MEMORY": "메모리 부족",
|
|
||||||
"SCRIPTERR_VAR_FAIL": "변수 실패",
|
|
||||||
"SCRIPTERR_FORBIDDEN_DRIVE": "금지된 드라이브",
|
|
||||||
"SCRIPTERR_INVALID_PATH": "잘못된 경로",
|
|
||||||
"SCRIPTERR_FILESELECT_ABORT": "파일 선택 중단",
|
|
||||||
"SCRIPTERR_DIRSELECT_ABORT": "디렉토리 선택 중단",
|
|
||||||
"SCRIPTERR_SET_FAIL": "설정 실패",
|
|
||||||
"SCRIPTERR_CHAR_NOT_FOUND": "문자가 없습니다.",
|
|
||||||
"SCRIPTERR_ARGV_2_IS_NOT_CHAR": "argv[2]는 문자열 아닙니다.",
|
|
||||||
"SCRIPTERR_ARGV_2_MUST_BE_2_CHARS": "argv[2]는 2개의 문자여야 합니다.",
|
|
||||||
"SCRIPTERR_ARG_MATCH": "인자 일치",
|
|
||||||
"SCRIPTERR_NO_ARG_MATCH": "일치하는 인자 없음",
|
|
||||||
"SCRIPTERR_PERMISSION_FAIL": "권한 실패",
|
|
||||||
"SCRIPTERR_COPY_FAIL": "copy 실패",
|
|
||||||
"SCRIPTERR_MOVE_FAIL": "move 실패",
|
|
||||||
"SCRIPTERR_INJECT_FAIL": "inject 실패",
|
|
||||||
"SCRIPTERR_FILLBYTE_FAIL": "fillbyte 실패",
|
|
||||||
"SCRIPTERR_FILL_FAIL": "fill 실패",
|
|
||||||
"SCRIPTERR_BAD_FILESIZE": "잘못된 파일 크기",
|
|
||||||
"SCRIPTERR_CREATE_DUMMY_FILE": "더미 파일 생성 실패",
|
|
||||||
"SCRIPTERR_REMOVE_FAIL": "제거 실패",
|
|
||||||
"SCRIPTERR_MAKEDIR_FAIL": "makedir 실패",
|
|
||||||
"SCRIPTERR_MOUNT_FAIL": "mount 실패",
|
|
||||||
"SCRIPTERR_FIND_FAIL": "find 실패",
|
|
||||||
"SCRIPTERR_FINDNOT_FAIL": "findnot 실패",
|
|
||||||
"SCRIPTERR_NO_SIZE_GIVEN": "크기가 주어지지 않음",
|
|
||||||
"SCRIPTERR_SIZE_TOO_BIG": "크기가 너무 큼",
|
|
||||||
"SCRIPTERR_READ_FAIL": "읽기 실패",
|
|
||||||
"SCRIPTERR_CONVERSION_FAIL": "변환 실패",
|
|
||||||
"SCRIPTERR_INVALID_DATA": "유효하지 않은 데이터",
|
|
||||||
"SCRIPTERR_WRITE_FAIL": "쓰기 실패",
|
|
||||||
"SCRIPTERR_SHA_ARG0_FAIL": "sha arg0 실패",
|
|
||||||
"SCRIPTERR_SHA_ARG1_FAIL": "sha arg1 실패",
|
|
||||||
"SCRIPTERR_SHA_DOES_NOT_MATCH": "sha가 일치하지 않습니다",
|
|
||||||
"SCRIPTERR_SHA_WRITE_FAIL": "sha write 실패",
|
|
||||||
"SCRIPTERR_FILE_WRITE_FAIL": "file write 실패",
|
|
||||||
"SCRIPTERR_FIXCMAC_FAILED": "fixcmac 실패",
|
|
||||||
"SCRIPTERR_VERIFICATION_FAILED": "검증 실패",
|
|
||||||
"SCRIPTERR_DECRYPT_FAILED": "해독 실패",
|
|
||||||
"SCRIPTERR_ENCRYPT_FAILED": "암호화 실패",
|
|
||||||
"SCRIPTERR_BUILD_CIA_FAILED": "CIA 빌드 실패",
|
|
||||||
"SCRIPTERR_INSTALL_GAME_FAILED": "게임 설치 실패",
|
|
||||||
"SCRIPTERR_DOES_NOT_CONTAIN_DOT_CODE": ".code 파일이 포함되어 있지 않습니다",
|
|
||||||
"SCRIPTERR_EXTRACT_DOT_CODE_FAILED": ".code 파일 추출 실패",
|
|
||||||
"SCRIPTERR_COMPRESS_DOT_CODE_FAILED": ".code 파일 압축 실패",
|
|
||||||
"SCRIPTERR_BUILD_FAILED": "빌드 실패",
|
|
||||||
"SCRIPTERR_UNKNOWN_FILE": "알 수 없는 파일",
|
|
||||||
"SCRIPTERR_APPLY_IPS_FAILD": "IPS 적용 실패",
|
|
||||||
"SCRIPTERR_APPLY_BPS_FAILED": "BPS 적용 실패",
|
|
||||||
"SCRIPTERR_APPLY_BPM_FAILED": "BPM 적용 실패",
|
|
||||||
"SCRIPTERR_TEXTVIEWER_FAILED": "텍스트 편집기 오류 발생",
|
|
||||||
"SCRIPTERR_BAD_DUMPSIZE": "잘못된 덤프 크기",
|
|
||||||
"SCRIPTERR_CART_INIT_FAIL": "cart init 실패",
|
|
||||||
"SCRIPTERR_CART_DUMP_FAILED": "카트리지 덤프 실패",
|
|
||||||
"SCRIPTERR_NOT_A_DIR": "디렉토리가 아님",
|
|
||||||
"SCRIPTERR_FILE_NOT_FOUND": "파일을 찾을 수 없음",
|
|
||||||
"SCRIPTERR_NOT_A_BOOTABLE_FIRM": "부팅 가능한 펌웨어가 아닙니다",
|
|
||||||
"SCRIPTERR_SD_NOT_MOUNTED": "SD 카드 마운트되지 않음",
|
|
||||||
"SCRIPTERR_UNKNOWN_ERROR": "알 수 없는 오류",
|
|
||||||
"SCRIPTERR_COMMAND_SUCCESS": "명령 성공",
|
|
||||||
"SCRIPTERR_CONTROL_FLOW_ERROR": "제어 흐름 오류",
|
|
||||||
"SCRIPTERR_UNCLOSED_CONDITIONAL": "닫히지 않은 조건문",
|
|
||||||
"SCRIPTERR_ERROR_MESSAGE_FAIL": "오류 메시지 실패",
|
|
||||||
"ERROR_INVALID_TEXT_DATA": "오류: 잘못된 텍스트 데이터",
|
|
||||||
"ERROR_TEXT_FILE_TOO_BIG": "오류: 텍스트 파일 크기가 너무 큽니다.\n텍스트 파일 크기는 %u 바이트입니다.\n최대 파일 크기는 %i 바이트입니다.",
|
|
||||||
"TEXTVIEWER_CONTROLS_DETAILS": "텍스트 뷰어 조작법:\n \n↑↓→←(+R) - 스크롤\nR+Y - 줄 바꿈 전환\nR+X - #번째 줄로 이동\nB - 나가기",
|
|
||||||
"TEXTEDITOR_CONTROLS_DETAILS": "텍스트 편집기 조작:\n\n↑↓→←(+R) - 스크롤\nR+Y - 줄 바꿈 토글\nR+X - 이전 줄로 이동\nA - 편집 모드 진입\nB - 종료\n",
|
|
||||||
"TEXTEDITOR_CONTROLS_KEYBOARD": "텍스트 편집기 조작법:\n\n↑↓→←(+R) - 커서 이동\nX - 문자 삭제\nA - 줄 바꿈\nL+↑↓→← - 텍스트 선택\nY - 복사 / [+R] 잘라내기\nB - 보기 모드 진입\n",
|
|
||||||
"TEXTEDITOR_CONTROLS_CLIPBOARD": "텍스트 편집기 조작법:\n\n↑↓→←(+R) - 커서 이동\nX - 문자 삭제\nA - 줄 바꿈\nL+↑↓→← - 텍스트 선택\nY - 붙여넣기 / [+R] - 지우기\nB - 보기 모드 진입\n",
|
|
||||||
"TEXT_EDITS_SAVE_CHANGES": "텍스트를 수정하셨습니다.\n파일에 변경 사항을 저장하시겠습니까?",
|
|
||||||
"CURRENT_LINE_N_ENTER_NEW_LINE_BELOW": "현재 줄: %i\n아래에 새 줄을 입력하세요.",
|
|
||||||
"PREVIEW_DISABLED": "(미리보기 비활성화됨)",
|
|
||||||
"PATH_LINE_N_ERR_LINE": "%s\nline %lu: %s\n%s",
|
|
||||||
"END_OF_SCRIPT_UNRESOLVED_IF": "스크립트 끝: 해결되지 않은 'if'",
|
|
||||||
"END_OF_SCRIPT_UNRESOLVED_FOR": "스크립트 끝: 해결되지 않은 'for'",
|
|
||||||
"SYSINFO_MODEL": "모델: %s (%s)\n",
|
|
||||||
"SYSINFO_SERIAL": "일련 번호: %s\n",
|
|
||||||
"SYSINFO_GYRO_MODEL": "자이로 모델: %u\n",
|
|
||||||
"SYSINFO_REGION_SYSTEM": "시스템 지역: %s\n",
|
|
||||||
"SYSINFO_REGION_SALES": "판매 지역: %s\n",
|
|
||||||
"SYSINFO_SOC_MANUFACTURING_DATE": "SoC 제조 날짜: %s\n",
|
|
||||||
"SYSINFO_SYSTEM_ASSEMBLY_DATE": "시스템 제조 날짜: %s\n",
|
|
||||||
"SYSINFO_ORIGINAL_FIRMWARE": "기존 펌웨어: %s\n",
|
|
||||||
"SYSINFO_FRIENDCODE_SEED": "친구코드 시드: %s\n",
|
|
||||||
"SYSINFO_SD_KEYY": "SD keyY: %s\n",
|
|
||||||
"SYSINFO_NAND_CID": "낸드 CID: %s\n",
|
|
||||||
"SYSINFO_SD_CID": "SD CID: %s",
|
|
||||||
"SYSINFO_SYSTEM_ID0": "시스템 ID0: %s\n",
|
|
||||||
"SYSINFO_SYSTEM_ID1": "시스템 ID1: %s\n",
|
|
||||||
"SORTING_TICKETS_PLEASE_WAIT": "티켓 분류 중, 잠시만 기다려주세요...",
|
|
||||||
"LUA_NOT_INCLUDED": "이 GodMode9 빌드는 Lua 스크립트\n지원 없이 컴파일되었습니다.",
|
|
||||||
"ERROR_SIGNATURE_CHECK_FAILED": "오류: 서명 확인 실패",
|
|
||||||
"USE_SIGNATURE_VERIFICATION": "서명 인증을 사용할까요?",
|
|
||||||
"IGNORE_SIGNATURES": "서명 무시",
|
|
||||||
"VERIFY_SIGNATURES": "서명 검증",
|
|
||||||
"STANDARD_CRYPTO": "스탠다드 암호화",
|
|
||||||
"ORIGINAL_CRYPTO": "원본 암호화",
|
|
||||||
"SELECT_TYPE_OF_ENCRYPTION": "암호화 유형을 선택하세요"
|
|
||||||
}
|
|
||||||
@ -60,8 +60,8 @@
|
|||||||
"LAB_RAMDRIVE": "RAMDYSK",
|
"LAB_RAMDRIVE": "RAMDYSK",
|
||||||
"LAB_NOLABEL": "BEZ ETYKIETY",
|
"LAB_NOLABEL": "BEZ ETYKIETY",
|
||||||
"N_BYTE": "%s B",
|
"N_BYTE": "%s B",
|
||||||
"BYTE": " B",
|
"BYTE": " B",
|
||||||
"KB": " KB",
|
"KB": " kB",
|
||||||
"MB": " MB",
|
"MB": " MB",
|
||||||
"GB": " GB",
|
"GB": " GB",
|
||||||
"CLIPBOARD": "[SCHOWEK]",
|
"CLIPBOARD": "[SCHOWEK]",
|
||||||
@ -191,13 +191,13 @@
|
|||||||
"CALCULATE_SHA256": "Oblicz SHA-256",
|
"CALCULATE_SHA256": "Oblicz SHA-256",
|
||||||
"CALCULATE_SHA1": "Oblicz SHA-1",
|
"CALCULATE_SHA1": "Oblicz SHA-1",
|
||||||
"SHOW_FILE_INFO": "Informacje o pliku",
|
"SHOW_FILE_INFO": "Informacje o pliku",
|
||||||
"SHOW_IN_TEXTVIEWER": "Pokaż w tekstedytorze",
|
"SHOW_IN_TEXTVIEWER": "Show in Text Editor",
|
||||||
"CALCULATE_CMAC": "Oblicz CMAC",
|
"CALCULATE_CMAC": "Oblicz CMAC",
|
||||||
"COPY_TO_OUT": "Kopiuj do %s",
|
"COPY_TO_OUT": "Kopiuj do %s",
|
||||||
"DUMP_TO_OUT": "Zrzut do %s",
|
"DUMP_TO_OUT": "Zrzut do %s",
|
||||||
"INJECT_DATA_AT_OFFSET": "Inject data @offset",
|
"INJECT_DATA_AT_OFFSET": "Inject data @offset",
|
||||||
"OPEN_THIS_FOLDER": "Otwórz ten folder",
|
"OPEN_THIS_FOLDER": "Otwórz ten folder",
|
||||||
"OPEN_CONTAINING_FOLDER": "Otwórz lokalizację pliku",
|
"OPEN_CONTAINING_FOLDER": "Open containing folder",
|
||||||
"OPEN_TITLE_FOLDER": "Otwórz folder aplikacji",
|
"OPEN_TITLE_FOLDER": "Otwórz folder aplikacji",
|
||||||
"PATH_N_FILES_SELECTED": "%s\n(zaznaczono %s)",
|
"PATH_N_FILES_SELECTED": "%s\n(zaznaczono %s)",
|
||||||
"CHECK_CURRENT_CMAC_ONLY": "Sprawdź tylko aktualny CMAC",
|
"CHECK_CURRENT_CMAC_ONLY": "Sprawdź tylko aktualny CMAC",
|
||||||
@ -308,9 +308,9 @@
|
|||||||
"PATH_TICKET_DUMPED_TO_OUT": "%s\nTicket dumped to %s",
|
"PATH_TICKET_DUMPED_TO_OUT": "%s\nTicket dumped to %s",
|
||||||
"LEGIT_TICKET_NOT_FOUND_DUMP_ANYWAYS": "%s\nLegit ticket not found.\n \nDump anyways?",
|
"LEGIT_TICKET_NOT_FOUND_DUMP_ANYWAYS": "%s\nLegit ticket not found.\n \nDump anyways?",
|
||||||
"DUMP_TICKET_FAILED": "Niepowodzenie zrzutu ticketa!",
|
"DUMP_TICKET_FAILED": "Niepowodzenie zrzutu ticketa!",
|
||||||
"BUILDING_X": "Kompilowanie %s...",
|
"BUILDING_X": "Building %s...",
|
||||||
"BUILDING_X_SYSNAND": "Kompilowanie %s (SysNAND)...",
|
"BUILDING_X_SYSNAND": "Building %s (SysNAND)...",
|
||||||
"BUILDING_X_EMUNAND": "Kompilowanie %s (EmuNAND)...",
|
"BUILDING_X_EMUNAND": "Building %s (EmuNAND)...",
|
||||||
"PATH_N_OF_N_FILES_PROCESSED_N_OF_N_FILES_IGNORED": "%s\n%lu/%lu files processed\n%lu/%lu files ignored",
|
"PATH_N_OF_N_FILES_PROCESSED_N_OF_N_FILES_IGNORED": "%s\n%lu/%lu files processed\n%lu/%lu files ignored",
|
||||||
"PATH_N_OF_N_FILES_PROCESSED": "%s\n%lu/%lu files processed",
|
"PATH_N_OF_N_FILES_PROCESSED": "%s\n%lu/%lu files processed",
|
||||||
"BUILD_DATABASE_SUCCESS": "Kompilacja bazy danych powiodła się.",
|
"BUILD_DATABASE_SUCCESS": "Kompilacja bazy danych powiodła się.",
|
||||||
@ -362,7 +362,7 @@
|
|||||||
"INSTALL_TO_BOTH": "Install to both",
|
"INSTALL_TO_BOTH": "Install to both",
|
||||||
"PATH_N_KB_INSTALL_TO_SYSNAND": "%s (%dkB)\nInstall to SysNAND?",
|
"PATH_N_KB_INSTALL_TO_SYSNAND": "%s (%dkB)\nInstall to SysNAND?",
|
||||||
"PATH_N_KB_INSTALL_SUCCESS": "%s (%dkB)\nInstall success",
|
"PATH_N_KB_INSTALL_SUCCESS": "%s (%dkB)\nInstall success",
|
||||||
"PATH_N_KB_INSTALL_FAILED": "%s (%dKB)\nInstalacja nie powiodła się",
|
"PATH_N_KB_INSTALL_FAILED": "%s (%dkB)\nInstall failed",
|
||||||
"WARNING_DO_NOT_RUN_UNTRUSTED_SCRIPTS": "Warning: Do not run scripts\nfrom untrusted sources.\n \nExecute script?",
|
"WARNING_DO_NOT_RUN_UNTRUSTED_SCRIPTS": "Warning: Do not run scripts\nfrom untrusted sources.\n \nExecute script?",
|
||||||
"SCRIPT_EXECUTE_SUCCESS": "Script execute success",
|
"SCRIPT_EXECUTE_SUCCESS": "Script execute success",
|
||||||
"SCRIPT_EXECUTE_FAILURE": "Script execute failure",
|
"SCRIPT_EXECUTE_FAILURE": "Script execute failure",
|
||||||
@ -428,19 +428,19 @@
|
|||||||
"FIX_CMACS_FOR_DRIVE_FINISHED": "Fix CMACs for drive finished.",
|
"FIX_CMACS_FOR_DRIVE_FINISHED": "Fix CMACs for drive finished.",
|
||||||
"FAILED_TO_ANALYZE_DRIVE": "Failed to analyze drive\n",
|
"FAILED_TO_ANALYZE_DRIVE": "Failed to analyze drive\n",
|
||||||
"FAILED_TO_ANALYZE_DIR": "Niepowodzenie analizy katalogu\n",
|
"FAILED_TO_ANALYZE_DIR": "Niepowodzenie analizy katalogu\n",
|
||||||
"NOT_ALLOWED_IN_VIRTUAL_PATH": "Niedozwolone w ścieżce wirtualnej",
|
"NOT_ALLOWED_IN_VIRTUAL_PATH": "Not allowed in virtual path",
|
||||||
"DELETE_N_PATHS": "Ścieżek: %lu - usunąć?",
|
"DELETE_N_PATHS": "Delete %lu path(s)?",
|
||||||
"DELETING_FILES_PLEASE_WAIT": "Usuwanie plików, proszę czekać...",
|
"DELETING_FILES_PLEASE_WAIT": "Usuwanie plików, proszę czekać...",
|
||||||
"FAILED_DELETING_N_OF_N_PATHS": "Failed deleting %lu/%lu path(s)",
|
"FAILED_DELETING_N_OF_N_PATHS": "Failed deleting %lu/%lu path(s)",
|
||||||
"DELETE_FILE": "Usunąć \"%s\"?",
|
"DELETE_FILE": "Usunąć \"%s\"?",
|
||||||
"FAILED_DELETING_PATH": "Failed deleting:\n%s",
|
"FAILED_DELETING_PATH": "Failed deleting:\n%s",
|
||||||
"NOT_ALLOWED_IN_SEARCH_DRIVE": "Niedozwolone w dysku wyszukiwania",
|
"NOT_ALLOWED_IN_SEARCH_DRIVE": "Niedozwolone w dysku wyszukiwania",
|
||||||
"NOT_ALLOWED_IN_VIRTUAL_GAME_PATH": "Niedozwolone w ścieżce wirtualnej gry",
|
"NOT_ALLOWED_IN_VIRTUAL_GAME_PATH": "Not allowed in virtual game path",
|
||||||
"NOT_ALLOWED_IN_XORPAD_DRIVE": "Not allowed in XORpad drive",
|
"NOT_ALLOWED_IN_XORPAD_DRIVE": "Not allowed in XORpad drive",
|
||||||
"NOT_ALLOWED_IN_GAMECART_DRIVE": "Not allowed in gamecart drive",
|
"NOT_ALLOWED_IN_GAMECART_DRIVE": "Not allowed in gamecart drive",
|
||||||
"NOT_ALLOWED_IN_ALIAS_PATH": "Niedozwolone w ścieżce aliasu",
|
"NOT_ALLOWED_IN_ALIAS_PATH": "Not allowed in alias path",
|
||||||
"COPY_PATHS": "Kopiuj ścieżkę/-i",
|
"COPY_PATHS": "Copy path(s)",
|
||||||
"MOVE_PATHS": "Przenieś ścieżkę/-i",
|
"MOVE_PATHS": "Move path(s)",
|
||||||
"PASTE_FILE_HERE": "Wkleić \"%s\" tutaj?",
|
"PASTE_FILE_HERE": "Wkleić \"%s\" tutaj?",
|
||||||
"PASTE_N_PATHS_HERE": "Paste %lu paths here?",
|
"PASTE_N_PATHS_HERE": "Paste %lu paths here?",
|
||||||
"FAILED_COPYING_PATH_PROCESS_REMAINING": "Failed copying path:\n%s\nProcess remaining?",
|
"FAILED_COPYING_PATH_PROCESS_REMAINING": "Failed copying path:\n%s\nProcess remaining?",
|
||||||
@ -731,7 +731,7 @@
|
|||||||
"SCRIPTERR_OUT_OF_MEMORY": "out of memory",
|
"SCRIPTERR_OUT_OF_MEMORY": "out of memory",
|
||||||
"SCRIPTERR_VAR_FAIL": "błąd zmiennej",
|
"SCRIPTERR_VAR_FAIL": "błąd zmiennej",
|
||||||
"SCRIPTERR_FORBIDDEN_DRIVE": "forbidden drive",
|
"SCRIPTERR_FORBIDDEN_DRIVE": "forbidden drive",
|
||||||
"SCRIPTERR_INVALID_PATH": "błędna ścieżka",
|
"SCRIPTERR_INVALID_PATH": "invalid path",
|
||||||
"SCRIPTERR_FILESELECT_ABORT": "fileselect abort",
|
"SCRIPTERR_FILESELECT_ABORT": "fileselect abort",
|
||||||
"SCRIPTERR_DIRSELECT_ABORT": "dirselect abort",
|
"SCRIPTERR_DIRSELECT_ABORT": "dirselect abort",
|
||||||
"SCRIPTERR_SET_FAIL": "błąd ustawiania",
|
"SCRIPTERR_SET_FAIL": "błąd ustawiania",
|
||||||
@ -792,7 +792,7 @@
|
|||||||
"SCRIPTERR_UNCLOSED_CONDITIONAL": "unclosed conditional",
|
"SCRIPTERR_UNCLOSED_CONDITIONAL": "unclosed conditional",
|
||||||
"SCRIPTERR_ERROR_MESSAGE_FAIL": "błąd wyświetlania błędu",
|
"SCRIPTERR_ERROR_MESSAGE_FAIL": "błąd wyświetlania błędu",
|
||||||
"ERROR_INVALID_TEXT_DATA": "Error: Invalid text data",
|
"ERROR_INVALID_TEXT_DATA": "Error: Invalid text data",
|
||||||
"ERROR_TEXT_FILE_TOO_BIG": "Błąd: Plik tekstowy jest zbyt duży.\nRozmiar pliku: %u B\nRozmiar maksymalny: %i B",
|
"ERROR_TEXT_FILE_TOO_BIG": "Error: Text file is too large.\nText file size is %u bytes.\nMax file size is %i bytes.",
|
||||||
"TEXTVIEWER_CONTROLS_DETAILS": "Instrukcja tekstprzeglądarki:\n \n↑↓→←(+R) - Przewijanie\nR+Y - Przełącz zawijanie wierszy\nR+X - Przejdź do linii #\nB - Wyjście\n",
|
"TEXTVIEWER_CONTROLS_DETAILS": "Instrukcja tekstprzeglądarki:\n \n↑↓→←(+R) - Przewijanie\nR+Y - Przełącz zawijanie wierszy\nR+X - Przejdź do linii #\nB - Wyjście\n",
|
||||||
"TEXTEDITOR_CONTROLS_DETAILS": "Text Editor Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nA - Enter edit mode\nB - Exit\n",
|
"TEXTEDITOR_CONTROLS_DETAILS": "Text Editor Controls:\n \n↑↓→←(+R) - Scroll\nR+Y - Toggle wordwrap\nR+X - Goto line #\nA - Enter edit mode\nB - Exit\n",
|
||||||
"TEXTEDITOR_CONTROLS_KEYBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nX - Delete char\nA - Insert newline\nL+↑↓→← - Select text\nY - COPY / [+R] CUT\nB - Enter view mode\n",
|
"TEXTEDITOR_CONTROLS_KEYBOARD": "Text Editor Controls:\n \n↑↓→←(+R) - Move cursor\nX - Delete char\nA - Insert newline\nL+↑↓→← - Select text\nY - COPY / [+R] CUT\nB - Enter view mode\n",
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from argparse import ArgumentParser, FileType
|
from argparse import ArgumentParser, FileType
|
||||||
from os import path
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
# Special keys
|
# Special keys
|
||||||
@ -25,14 +24,3 @@ for key in source:
|
|||||||
# Escape \r, \n, and quotes
|
# Escape \r, \n, and quotes
|
||||||
val = source[key].replace("\r", "\\r").replace("\n", "\\n").replace('"', '\\"')
|
val = source[key].replace("\r", "\\r").replace("\n", "\\n").replace('"', '\\"')
|
||||||
args.inl.write('STRING(%s, "%s")\n' % (key, val))
|
args.inl.write('STRING(%s, "%s")\n' % (key, val))
|
||||||
|
|
||||||
# Create the English stub
|
|
||||||
english = {
|
|
||||||
"GM9_LANGUAGE": "English",
|
|
||||||
"GM9_TRANS_VER": version
|
|
||||||
}
|
|
||||||
|
|
||||||
enJson = path.join(path.dirname(args.source.name), "en.json")
|
|
||||||
with open(enJson, "wt") as file:
|
|
||||||
json.dump(english, file, indent="\t")
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user