Skip to content

Linux CIA Conversion Guide

niansa edited this page Mar 24, 2018 · 8 revisions

Linux CIA Conversion Guide

Overview

Initial Set Up

  1. Preparation
  2. Set up devkitPro - Build some packages without binaries.
  3. Get rsfgen.py, extractcci.sh and makecia.sh - Glue logic scripts.
  4. Get/Build Brahma2 - Run uncart.
  5. Get/Build uncart - Dump cartridges.
  6. Get/Build Relys/Project_CTR - Pick apart and reassemble 3DS games.
  7. Get Decrypt9WIP - Decrypt games and generate xorpads of eshop games.
  8. Build padxorer - Decrypt eShop games from xorpads

Process

  1. Dump/Decrypt cartridge games with uncart/decrypt9wip
  2. Dump/Decrypt eShop games with decrypt9wip/padxorer
  3. Extract data from a cartridge CCI
  4. Determine NCCH files from eShop .app.out files
  5. Build CIA file from an extracted CCI
  6. Build CIA file from .app.out files
  7. Optional: Decrypt and unpack an eShop's RomFS and ExeFS

Appendix

A. padxorer

Initial Set Up

After this section, you'll have a full set of tools for dumping and decrypting any game you've bought for the 3DS, as well as most .3ds files you may find on the internet. This only needs to be done once.

Preparation

On your system, you'll need GCC and make. You also need a python 3 version. I have only tested with as early as python 3.4.

Create a directory to keep everything in. I have a directory named 3ds in my home where I keep all my 3DS-related stuff. For each game, you should put the game's files in its own directory (and for the cartridge game tools, they expect this layout.).

Each section with shell commands assumes you start at the root of this directory.

Set up devkitPro

From https://sourceforge.net/projects/devkitpro/files/Automated%20Installer/

Get devkitARMupdate.pl and put it in the directory.

Make it executable: chmod a+x devkitARMupdate.pl

Run it: ./devkitARMupdate.pl

Make a shell script and save it somewhere:

#!/bin/sh
PS1="devkitPro ${PS1}"
export DEVKITPRO="${HOME}/devkitPro"
export DEVKITARM="${DEVKITPRO}/devkitARM"

"Source" it (This pulls the environment variables in to your environment): source activate.sh

Now you have a working 3DS dev environment. You can activate it at any time by sourcing that script.

Get rsfgen.py, extractcci.sh and makecia.sh

Go to https://github.com/paulguy/minitools and save rsfgen.py, extractcci.sh and makecia.sh to the directory made in Preparation.

Make extractcco.sh and makecia.sh executable: chmod a+x extractcci.sh makecia.sh

Get/Build Brahma2

Go to https://github.com/delebile/Brahma2 and download the ZIP and extract it.

Go to https://github.com/Myriachan/libkhax/tree/master and download the ZIP, then extract the files within libkhax-master in to Brahma2-master/source/libkhax.

In your shell, make sure the devkitPro environment is set up.

Change to the directory: cd Brahma2-master

Build it: make

Put it on your 3DS.

Get/Build uncart

Go to https://github.com/citra-emu/uncart and download the ZIP and extract it.

Make sure the devkitPro environment is set up in your shell.

Change to the directory: cd uncart-master

Build it: make

Create a directory on your SD card named brahma then put uncart-master.bin in it.

Get/Build Relys/Project_CTR

Create a directory somewhere to keep everything, name doesn't matter: mkdir 3DS

Go to https://github.com/Relys/Project_CTR then download the source ZIP, extract it in to the directory you created in Preparation (This is necessary for the scripts to find everything.).

Rename the Project_CTR directory (Also necessary): mv Project_CTR-master Project_CTR

Go in to the ctrtool directory: cd Project_CTR/ctrtool

Build it: make

Go in to makerom directory: cd ../makerom

Build it: make

Get Decrypt9WIP

Get the latest from: https://github.com/d0k3/Decrypt9WIP/releases

Put it on your 3DS.

Build padxorer

Copy padxorer.c from Appendix A in to its own file at the root of the directory created in Preparation.

Build it following the instructions in the file.

Process

Dump/Decrypt cartridge games with uncart/decrypt9wip

Insert a game card and run uncart using Brahma2 and follow instructions then wait for it to complete. Move them to /D9Game on your SD card so decrypt9 can do its thing on them.

Follow the instructions in the README for Decrypt9WIP appropriate for the game you're looking to decrypt.

Make sure to use SysNAND / EmuNAND Options -> Update SeedDB for games that use the new style 9.6.0 encryption, though I'm not entirely sure how this works since I don't have any games that use this.

You'll want to use the Game Decryptor Options -> NCCH/NCSD Decryptor feature to actually decrypt the games.

Rename it to have a .cci extension and put it in a subdirectory of the root containing the Project_CTR directory:

mkdir "Name of Game"
mv /path/to/sdcard/D9Game/CTR-P-XXXX.3ds "Name of Game/Name of Game.cci"

Dump/Decrypt eShop games with decrypt9wip/padxorer

Use the XORpad Generator Options -> SD Padgen SysNAND or EmuNAND option depending on where the games are installed. Select /title and hit A. You'll need a LOT of free storage on your SD card (basically enough for a whole second copy of all your games and a lot of time for this task to complete.

Create a directory to put the game .app files and xorpads then change to it:

mkdir "Name of Game"
cd "Name of Game"

Copy the files for the games you want to decrypt from your SD card. They're generally kept in: (You'll only need the .app files) /Nintendo 3DS/<unique id>/<some other id>/title/00040000/<game id>/content.

The unique id is printed in the SD Padgen screen. The some other id doesn't matter since it'll be the only directory there.

Copy the matching xorpads from /Decrypt9 on the SD card to the same directory as you put the .app files.

Run padxorer on each one. Each game may have 2 or 3 parts: the main game, the manual and possibly a download play image.

For example, I have the data and xorpads for Animal Crossing New Leaf. The data consists of 26d87923.app and 75df13fe.app, while the xorpads for it are title.00040000.00086300.content.26d87923.app.xorpad and title.00040000.00086300.content.75df13fe.app.xorpad. So to use padxorer on it you would do:

../padxorer 26d87923.app title.00040000.00086300.content.26d87923.app.xorpad
../padxorer 75df13fe.app title.00040000.00086300.content.75df13fe.app.xorpad

Now you have the decrypted components of an eshop game.

I personally don't know of a good way to identify which game is which based on title IDs. You might be able to look them up or just rely on some guesswork. FBI can tell you product codes ("CTR-P-XXXX") om the Delete and Launch modes, so that mighe help as a lead.

Extract data from cartridge CCI

Change in to the directory where you put the decrypted file cd "Name of Game"

Run extractcci.sh on it: ../extractcci.sh "Name of Game.cci"

This will take a while, just wait for it to complete.

Determine NCCH files from eShop .app.out files

You'll need to know which files are the Application, Manual or Download Play ("Child").

Change in to the directory: cd "Name of Game"

Run the following for each .app file: ../Project_CTR/ctrtool/ctrtool -i XXXXXXXX.app.out | grep "^ > Content type:" Replace XXXXXXXX to match the filename.

It should return the content type for that file. The main application being "Application", the manual being "Manual" and the Download Play being "Child". Make note of which is which.

Build CIA file from an extracted CCI

Change to the directory containing the extracted files: cd "Name of Game"

Run the makecia.sh script: ../makecia.sh "Name of Game.cci"

If all steps went well, you should now have a working CIA file.

Build CIA file from .app.out files

Change in to the directory containing the decrypted .app.out files: cd "Name of Game"

If your game has a Download Play "Child", the command would go like this:

../Project_CTR/makerom/makerom -f cia -content "AAAAAAAA.app.out:0" \
-content "BBBBBBBB.app.out:1" -content "CCCCCCCC.app.out:2" \
-o "Name of Game.cia"

Where AAAAAAAA is the file containing the Application, BBBBBBBB is the file containing the Manual and CCCCCCCC is the file containing the Download Play "Child".

If your game doesn't contain a Download Play "Child", the command would be this:

../Project_CTR/makerom/makerom -f cia -content "AAAAAAAA.app.out:0" \
-content "BBBBBBBB.app.out:1" -o "Name of Game.cia"

With the same substitutions as the Download Play example above.

Once this is complete, you should have a working CIA file.

Optional: Decrypt and unpack an eShop's RomFS and ExeFS

This isn't very useful for anything other than picking apart game files or helping with making mods for use with HANS. You'll just need Decrypt9WIP and ctrtool.

Take the CIA you created in the previous section and drop it in the D9Game directory on the SD card, run Decrypt9WIP and use the Game Decryptor Options -> CIA Decryptor (deep) option. You'll probably need slot0x25KeyX.bin on your SD card root, and you might need to run through the SysNAND or EmuNAND Options (Depending on which NAND the game is installed on.) -> Update SeedDB option to update the seeddb.bin if the game uses the new style encryption.

The CIA file will be decrypted in-place. Just copy the file back. It can be split back apart in to it's constituent NCCH images. ../Project_CTR/ctrtool/ctrtool -x "Name of Game.cia" --contents "Name of Game"

You'll get 2 or 3 files named Name of Game.<Content index>.<Content ID>. You want Content index 0000, which is the main application. 0001 is the Manual, 0002 is the Download Play child and if there's a 0007 present, that's a System Update.

To get the files out, use:

../Project_CTR/ctrtool/ctrtool -x --decompresscode --exheader="exheader.bin" \
--plainrgn="plainrgn.bin" --exefsdir="exefs" --romfsdir="romfs" \
"Name of Game.0000.<Content ID>"

That gives you enough to put it back together later and then reassemble it in to a CIA, but I'll leave that up to the reader to figure out. This file and the scripts give enough information on how to do that.

Appendix

padxorer

This is a program that will XOR 2 files together, used for decrypting files from xorpads.

/*
    padxorer by xerpi
    compile with:
        gcc -O3 padxorer.c -o padxorer
    usage:
        padxorer <input file 1> <input file 2>

    the output will be <input file 1>.out
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#define BUF_SIZE (8*1024*1024)

void print_usage();

int main(int argc, char *argv[])
{
    int ret_val = EXIT_SUCCESS;
    
    if (argc < 3) {
        print_usage();
        ret_val = EXIT_FAILURE;
        goto exit_fail_1;
    }
    
    FILE *fd_in1, *fd_in2, *fd_out;
    if (!(fd_in1 = fopen(argv[1], "rb"))) {
        printf("Error opening input file 1\n");
        ret_val = EXIT_FAILURE;
        goto exit_fail_1;
    }
    if (!(fd_in2 = fopen(argv[2], "rb"))) {
        printf("Error opening input file 2\n");
        ret_val = EXIT_FAILURE;
        goto exit_fail_2;
    }
    
    fseek(fd_in1, 0, SEEK_END);
    unsigned int in_size1 = ftell(fd_in1);
    fseek(fd_in1, 0, SEEK_SET);
    
    fseek(fd_in2, 0, SEEK_END);
    unsigned int in_size2 = ftell(fd_in2);
    fseek(fd_in2, 0, SEEK_SET);
    
    unsigned int min_size = (in_size1 < in_size2) ? in_size1 : in_size2;

    char *out_name = malloc(strlen(argv[1]) + strlen(".out") + 1);
    sprintf(out_name, "%s.out", argv[1]);
    
    if (!(fd_out = fopen(out_name, "wb+"))) {
        printf("Cannot create output file\n");
        ret_val = EXIT_FAILURE;
        goto exit_fail_3;
    }
    
    unsigned char *data_buf1 = (unsigned char *)malloc(BUF_SIZE);
    unsigned char *data_buf2 = (unsigned char *)malloc(BUF_SIZE);

    #define BAR_LEN 50
    unsigned int step_bytes = min_size/100;
    unsigned int temp_bytes = 0;
    size_t bytes_read1, bytes_read2, total_read = 0, min_read;
    while ((total_read < min_size) && (bytes_read1 = fread(data_buf1, 1, BUF_SIZE, fd_in1)) &&
                                      (bytes_read2 = fread(data_buf2, 1, BUF_SIZE, fd_in2))) {
        min_read = (bytes_read1 < bytes_read2) ? bytes_read1 : bytes_read2;
        total_read += min_read;
        temp_bytes += min_read;
        size_t i;
        for (i = 0; i < min_read; i++) {
            data_buf1[i] ^= data_buf2[i];
        }
        fwrite(data_buf1, 1, min_read, fd_out);
        
        if ((temp_bytes >= step_bytes) || (total_read == min_size)) {
            temp_bytes = 0;
            unsigned int percent = (unsigned int)(total_read*(100.0/min_size));
            printf("%3i%% [", percent);
            int j;
            int bar_size = (BAR_LEN*percent)/100;
            for (j = 0; j < BAR_LEN; j++) {
                if (j < bar_size) printf("=");
                else if (j == bar_size) printf(">");
                else printf(" ");
            }
            printf("]\r");
            fflush(stdout);
        }
    }
    fflush(fd_out);
    fclose(fd_out);
    free(data_buf1);
    free(data_buf2);
    printf("\nFinished!\n");
exit_fail_3:
    free(out_name);
    fclose(fd_in2);
exit_fail_2:
    fclose(fd_in1);
exit_fail_1:
    return ret_val;
}

void print_usage()
{
    printf("padxorer by xerpi\n"
           "usage: padxorer <input file 1> <input file 2>\n"
           "the output will be <input file 1>.out\n");
}