Hacking [Release] FunkyCIA2 - build CIAs from your eShop content. Super easy and fast! (**2.1 FIX!**)

mikedan435

New Member
Newbie
Joined
May 28, 2015
Messages
1
Trophies
0
Age
28
XP
41
Country
United States
So, I'm very confused on basically everything... I'm trying to get us updates off of a 2ds so i can move them to a pal 3ds running NTR, i have rxTools, i have a 2ds on 9.2, i just don't know exactly how to do anything, I'm sorry for my stupidity..
 

Cavioe

Well-Known Member
Member
Joined
May 28, 2015
Messages
308
Trophies
0
Age
52
XP
190
Country
United States
So in theory this is what I am planning on doing. On my 3DS console I have super smash and majora's mask on it from eshop. Both of these are legitimate purchased from the nintendo store. My 3DS is on 9.2 firmware right now. What I did so far was to use the "-personal" tag to make console specific CIA's. Why? Because I am trying to give my friends 2DS and 3DS consoles a copy of these games for free. This is so we can all play onlline.

So if I am understanding this correctly, we are going to play hot potatoe with these 2 eshop games by making the console specific -personal CIAs and then transfer them to the next console. The next console will do the same thing and make their own -personal CIAs. And then to the next. Would this work by using PBT-CFW to reinstall them to the console the CIA was made from?

If not, I guess I misunderstood the purpose of -personal tags. But if it does work then I would also do it for dsiware to use sudokuhax.
 

gamesquest1

Nabnut
Former Staff
Joined
Sep 23, 2013
Messages
15,153
Trophies
2
XP
12,247
So in theory this is what I am planning on doing. On my 3DS console I have super smash and majora's mask on it from eshop. Both of these are legitimate purchased from the nintendo store. My 3DS is on 9.2 firmware right now. What I did so far was to use the "-personal" tag to make console specific CIA's. Why? Because I am trying to give my friends 2DS and 3DS consoles a copy of these games for free. This is so we can all play onlline.

So if I am understanding this correctly, we are going to play hot potatoe with these 2 eshop games by making the console specific -personal CIAs and then transfer them to the next console. The next console will do the same thing and make their own -personal CIAs. And then to the next. Would this work by using PBT-CFW to reinstall them to the console the CIA was made from?

If not, I guess I misunderstood the purpose of -personal tags. But if it does work then I would also do it for dsiware to use sudokuhax.
nope, once you transfer them to the next console the "-personal" ticket you backed up will of been revoked/transfer server side, so once you connect online it will be seen that its no longer valid for your system, and from what i have read they will be automatically deleted......but really you could just make nand backup and restore after the system transfer, as long as you dont connect online they will still be seen as "legit" for your console
 

MelonGx

Well-Known Member
Member
Joined
Jan 8, 2009
Messages
1,653
Trophies
1
XP
915
Country
China
E-shop launches some pre-released titles.
You can download it to your 3DS after purchased. But it can't be played until the release date.

Question:
If I have bought and downloaded one, can I make the CIA from my ticket.db and play it before the release date?
 

gamesquest1

Nabnut
Former Staff
Joined
Sep 23, 2013
Messages
15,153
Trophies
2
XP
12,247
E-shop launches some pre-released titles.
You can download it to your 3DS after purchased. But it can't be played until the release date.

Question:
If I have bought and downloaded one, can I make the CIA from my ticket.db and play it before the release date?
nope, the pre-download feature uses a new type of encryption,basically you will get the ticket but it seems all new eshop games are utilizing the "SEEDDB" encryption method (i think we need a more recognizable name for that.....seedy encryption? :huh:)....basically your 3DS will not know the whole key until release date....at which point you need to visit the eshop (or maybe it will auto download ...idk)....but basically the last part of the encryption puzzle isn't in the users hands until release date, its also the reason some newer eshop games are unplayable on the n3DS (on 9.5 emunand)as the "seedy encryption*" method wasn't added until 9.6

* just seeing if that catches on now
 

MelonGx

Well-Known Member
Member
Joined
Jan 8, 2009
Messages
1,653
Trophies
1
XP
915
Country
China
nope, the pre-download feature uses a new type of encryption,basically you will get the ticket but it seems all new eshop games are utilizing the "SEEDDB" encryption method (i think we need a more recognizable name for that.....seedy encryption? :huh:)....basically your 3DS will not know the whole key until release date....at which point you need to visit the eshop (or maybe it will auto download ...idk)....but basically the last part of the encryption puzzle isn't in the users hands until release date, its also the reason some newer eshop games are unplayable on the n3DS (on 9.5 emunand)as the "seedy encryption*" method wasn't added until 9.6

* just seeing if that catches on now
Thanks very much.
Does that mean what we drag from the CDN server (of these pre-installed titles) will be a bunch of incomplete "seedy encrypted" source files?
 

Asia81

Yuri Lover ~
Member
Joined
Nov 15, 2014
Messages
6,658
Trophies
3
Age
29
XP
3,505
Country
France
wrong :) use cubic ninja and decrypt your nand with decrpyt9 and get the file yourself. search a little next time :)
But we can't decrypt the Emunand right ? On N3DS. For get the ticket.db

I think i will spoof the SysNand E-Shop, download my games updates and get the sysnand ticket .db using my nand decrypted by decrypt9.

I'm right ?
 

cearp

瓜老外
OP
Developer
Joined
May 26, 2008
Messages
8,728
Trophies
2
XP
8,520
Country
Tuvalu
But we can't decrypt the Emunand right ? On N3DS. For get the ticket.db

I think i will spoof the SysNand E-Shop, download my games updates and get the sysnand ticket .db using my nand decrypted by decrypt9.

I'm right ?
you can dump the emunand from the microsd card and decrypt it yes :)
 

MelonGx

Well-Known Member
Member
Joined
Jan 8, 2009
Messages
1,653
Trophies
1
XP
915
Country
China
Just dumped a pre-released game and installed it on MT emuNAND 9.4.
The original one looks like a complete one, but it requires Internet verifying the date.
The installed CIA doesn't have any Internet verification. It freezes on Nintendo 3DS screen due to the unsupported encryption.
If I had a GW emuNAND 9.7, it might be playable.
 

gamesquest1

Nabnut
Former Staff
Joined
Sep 23, 2013
Messages
15,153
Trophies
2
XP
12,247
Just dumped a pre-released game and installed it on MT emuNAND 9.4.
The original one looks like a complete one, but it requires Internet verifying the date.
The installed CIA doesn't have any Internet verification. It freezes on Nintendo 3DS screen due to the unsupported encryption.
If I had a GW emuNAND 9.7, it might be playable.
nope, you still wouldn't have the seed until release date
 

alatnet

Member
Newcomer
Joined
Aug 13, 2007
Messages
11
Trophies
0
XP
218
Country
United States
ok, i must be doing something stupid but i've been trying to use this and it doesn't seem to want to download any of the cia's even when i throw in the "ALL" type. though it's saying that pretty much EVERY title is a bad id with the "ALL" type. I did modify it to be able to use python 3.4 with no problems (just a few minor edits of converting print statements to print functions and change the urllib to urllib.request). Also had to yank out the os check but rigged it for windows version.

My 3ds is an n3ds and it took a while to get the ticket.db but i was able to get it.

Any help is appreciated.

Code:
# FunkyCIA2 (FunkyCIA v2.1)
# - preinstalled game/app creation is not broken (stopped wiping the ticket id)
# - added -personal, so that CIAs can be made for your own console, = not sharable, they should only work for your 3ds, although last time I checked for some reason they still do not install...

import sys
import os
import re
import binascii
from struct import unpack, pack
import urllib.request
import argparse
import string
import hashlib
import datetime
from collections import namedtuple
from collections import Counter

if not sys.version_info[:2] >= (2, 7):
    print("*****\n!!!!!Warning - Only tested with Python 2.7!!!!!\n*****\n")

windoze = '9c4f88f706dedde3bc0ebb66e34963e5'
magic = binascii.a2b_hex('00010004919EBE464AD0F552CD1B72E7884910CF55A9F02E50789641D896683DC005BD0AEA87079D8AC284C675065F74C8BF37C88044409502A022980BB8AD48383F6D28A79DE39626CCB2B22A0F19E41032F094B39FF0133146DEC8F6C1A9D55CD28D9E1C47B3D11F4F5426C2C780135A2775D3CA679BC7E834F0E0FB58E68860A71330FC95791793C8FBA935A7A6908F229DEE2A0CA6B9B23B12D495A6FE19D0D72648216878605A66538DBF376899905D3445FC5C727A0E13E0E2C8971C9CFA6C60678875732A4E75523D2F562F12AABD1573BF06C94054AEFA81A71417AF9A4A066D0FFC5AD64BAB28B1FF60661F4437D49E1E0D9412EB4BCACF4CFD6A3408847982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F742D43413030303030303033000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000158533030303030303063000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000137A0894AD505BB6C67E2E5BDD6A3BEC43D910C772E9CC290DA58588B77DCC11680BB3E29F4EABBB26E98C2601985C041BB14378E689181AAD770568E928A2B98167EE3E10D072BEEF1FA22FA2AA3E13F11E1836A92A4281EF70AAF4E462998221C6FBB9BDD017E6AC590494E9CEA9859CEB2D2A4C1766F2C33912C58F14A803E36FCCDCCCDC13FD7AE77C7A78D997E6ACC35557E0D3E9EB64B43C92F4C50D67A602DEB391B06661CD32880BD64912AF1CBCB7162A06F02565D3B0ECE4FCECDDAE8A4934DB8EE67F3017986221155D131C6C3F09AB1945C206AC70C942B36F49A1183BCD78B6E4B47C6C5CAC0F8D62F897C6953DD12F28B70C5B7DF751819A9834652625000100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010003704138EFBBBDA16A987DD901326D1C9459484C88A2861B91A312587AE70EF6237EC50E1032DC39DDE89A96A8E859D76A98A6E7E36A0CFE352CA893058234FF833FCB3B03811E9F0DC0D9A52F8045B4B2F9411B67A51C44B5EF8CE77BD6D56BA75734A1856DE6D4BED6D3A242C7C8791B3422375E5C779ABF072F7695EFA0F75BCB83789FC30E3FE4CC8392207840638949C7F688565F649B74D63D8D58FFADDA571E9554426B1318FC468983D4C8A5628B06B6FC5D507C13E7A18AC1511EB6D62EA5448F83501447A9AFB3ECC2903C9DD52F922AC9ACDBEF58C6021848D96E208732D3D1D9D9EA440D91621C7A99DB8843C59C1F2E2C7D9B577D512C166D6F7E1AAD4A774A37447E78FE2021E14A95D112A068ADA019F463C7A55685AABB6888B9246483D18B9C806F474918331782344A4B8531334B26303263D9D2EB4F4BB99602B352F6AE4046C69A5E7E8E4A18EF9BC0A2DED61310417012FD824CC116CFB7C4C1F7EC7177A17446CBDE96F3EDD88FCD052F0B888A45FDAF2B631354F40D16E5FA9C2C4EDA98E798D15E6046DC5363F3096B2C607A9D8DD55B1502A6AC7D3CC8D8C575998E7D796910C804C495235057E91ECD2637C9C1845151AC6B9A0490AE3EC6F47740A0DB0BA36D075956CEE7354EA3E9A4F2720B26550C7D394324BC0CB7E9317D8A8661F42191FF10B08256CE3FD25B745E5194906B4D61CB4C2E000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001434130303030303030330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007BE8EF6CB279C9E2EEE121C6EAF44FF639F88F078B4B77ED9F9560B0358281B50E55AB721115A177703C7A30FE3AE9EF1C60BC1D974676B23A68CC04B198525BC968F11DE2DB50E4D9E7F071E562DAE2092233E9D363F61DD7C19FF3A4A91E8F6553D471DD7B84B9F1B8CE7335F0F5540563A1EAB83963E09BE901011F99546361287020E9CC0DAB487F140D6626A1836D27111F2068DE4772149151CF69C61BA60EF9D949A0F71F5499F2D39AD28C7005348293C431FFBD33F6BCA60DC7195EA2BCC56D200BAF6D06D09C41DB8DE9C720154CA4832B69C08C69CD3B073A0063602F462D338061A5EA6C915CD5623579C3EB64CE44EF586D14BAAA8834019B3EEBEED3790001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')

logfile = str(datetime.datetime.now()).replace(' ', '_').replace(':', '-') + '.txt'
usedlog = 0
def writeInLog(msg):
    global usedlog
    usedlog = 1
    f = open(logfile, "a")
    f.write(msg+"\n")
    f.close()

#execname = './make_cdn_cia'
#if hashlib.md5(sys.platform).hexdigest() == windoze:
    execname = 'make_cdn_cia.exe'

##########From https://stackoverflow.com/questions/5783517/downloading-progress-bar-urllib2-python
def chunk_report(bytes_so_far, chunk_size, total_size):
    percent = float(bytes_so_far) / total_size
    percent = round(percent*100, 2)
    sys.stdout.write("Downloaded %d of %d bytes (%0.2f%%)\r" % (bytes_so_far, total_size, percent))
    if bytes_so_far >= total_size:
        sys.stdout.write('\n')

def chunk_read(response, outfname, chunk_size=2*1024*1024, report_hook=None):
    fh = open(outfname,'wb')
    total_size = response.info().getheader('Content-Length').strip()
    total_size = int(total_size)
    bytes_so_far = 0
    data = []
    while 1:
        if report_hook:
            report_hook(bytes_so_far, chunk_size, total_size)
        chunk = response.read(chunk_size)
        bytes_so_far += len(chunk)
        if not chunk:
            break
        fh.write(chunk)
    fh.close()
##########
SYMBOLS = {
    'customary'     : ('B', 'KB', 'MB', 'GB', 'T', 'P', 'E', 'Z', 'Y'),
}
def bytes2human(n, format='%(value).2f %(symbol)s', symbols='customary'):
    n = int(n)
    if n < 0:
        raise ValueError("n < 0")
    symbols = SYMBOLS[symbols]
    prefix = {}
    for i, s in enumerate(symbols[1:]):
        prefix[s] = 1 << (i+1)*10
    for symbol in reversed(symbols[1:]):
        if n >= prefix[symbol]:
            value = float(n) / prefix[symbol]
            return format % locals()
    return format % dict(symbol=symbols[0], value=n)
##########


parser = argparse.ArgumentParser()
parser.add_argument('inputfile', action='store', help='The ticket.db file')
choose = parser.add_mutually_exclusive_group()
choose.add_argument('-type', nargs='+', choices=['gameapp', 'update', 'dlc', 'demo', 'dsiware', 'system', 'ALL'], dest='chosentypelist', help='The types of contents that you wish to be downloaded')
choose.add_argument('-title', nargs='+', dest='specific_titles', help='Give TitleIDs to be specifically downloaded')
parser.add_argument('-ignoretitles', nargs='+', dest='ignore_titles', help='Give TitleIDs that will be ignored.')
parser.add_argument('-outputdir', action='store', dest='output_dir', help='The custom output directory to store output in, if desired')
parser.add_argument('-nosort', action='store_false', default=True, dest='sort', help='Turn OFF sorting of the titles into folders based on their type (eshop app, update, dlc, etc).')
parser.add_argument('-nodownload', action='store_false', default=True, dest='download', help='Turn OFF content downloading - will not generate CIA files.')
parser.add_argument('-nobuild', action='store_false', default=True, dest='build', help='Turn OFF generation of CIA files, titles will be downloaded only.')
parser.add_argument('-noignore', action='store_false', default=True, dest='ignorefake', help='Using this will stop tickets that are not attached to your eshop account from being ignored')
parser.add_argument('-nopatchdlc', action='store_false', default=True, dest='patch_dlc', help='This will disable unlocking all DLC content')
parser.add_argument('-nopatchdemo', action='store_false', default=True, dest='patch_demo', help='This will disable patching the demo play limit')
parser.add_argument('-personal', action='store_false', default=True, dest='blank_ids', help='Using this flag will disable blanking the device unique IDs in the CIA. Hopefully this can be used to make PERFECT backup CIAs that ONLY work with your console.')
parser.add_argument('-preinstalled', action='store_true', default=False, dest='preinstalled', help='Using this flag with ONE Title ID will make sure the correct ticket is chosen when making a CIA from a presinstalled game/app :) (maybe not required... but it will only help).')
parser.add_argument('-retry', type=int, default=4, dest='retry_count', choices=range(0, 10), help='How many times a file download will be attempted')
arguments = parser.parse_args()

tk = 0x140
badtitle = False
dlSystem = False
dlALL = False
typelist = []
titlelist = []
ignorelist = []
error = False

eshopapp = 0
dlp = 0
demo = 0
updatepatch = 0
dlc = 0
dsiware = 0
dsisysapp = 0
dsisysdata = 0
mystery = 0
system = 0
anything = 0

if (arguments.chosentypelist is arguments.specific_titles):
    print("Please either choose one or more content types to download (-type gameapp update dlc demo dsiware system ALL)")
    print("OR type one or more specific titles (-title TITLEID1 TITLEID2)")
    sys.exit(0)

if arguments.chosentypelist is not None:
    if 'gameapp' in arguments.chosentypelist: typelist.append('0000')
    if 'demo' in arguments.chosentypelist: typelist.append('0002')
    if 'update' in arguments.chosentypelist: typelist.append('000e')
    if 'dlc' in arguments.chosentypelist: typelist.append('008c')
    if 'dsiware' in arguments.chosentypelist: typelist.append('8004')
    if 'system' in arguments.chosentypelist: dlSystem = True
    if 'ALL' in arguments.chosentypelist: dlALL = True

if arguments.specific_titles is not None:
    for specific_title in arguments.specific_titles:
        if (len(specific_title) is 16) and all(c in string.hexdigits for c in specific_title):
            titlelist.append(specific_title.lower())
        else:
            print("The Title ID(s) must be 16 hexadecimal characters long")
            print (specific_title + " - is not ok.")
            print("")
            badtitle = True
if badtitle: #if any title that was specified was not ok, quit
    sys.exit(0)

if arguments.ignore_titles is not None:
    for ignore_title in arguments.ignore_titles:
        if (len(ignore_title) is 16) and all(c in string.hexdigits for c in ignore_title):
            ignorelist.append(ignore_title.lower())
        else:
            print("The Title ID(s) must be 16 hexadecimal characters long")
            print(ignore_title + " - is not ok.")
            print("")
            badtitle = True
if badtitle: #if any title that was specified was not ok, quit
    sys.exit(0)

if arguments.preinstalled:
    if arguments.specific_titles is not None:
        if len(arguments.specific_titles) > 1:
            print("Please one give only ONE Title ID")
            sys.exit(0)
    else:
        print("For a preinstalled game, please specify ONE title with, like \'-title TITLEID -preinstalled\', using the Title ID of your game")
        sys.exit(0)
    arguments.ignorefake = False



print("*******\nFunkyCIA2 by cearp - FunkyCIA v2.1\n*******\n")

ticketlist = []
not_eshop_tickets = []
eshop_tickets = []
completetitleids = []
Ticket = namedtuple('Ticket', ['data', 'titleid', 'consoleid', 'commonkeyindex'])

if not os.path.isfile(arguments.inputfile):
    print("The input file does not exist.")
    sys.exit(0)

with open(arguments.inputfile, 'rb') as fh:
    ticks = fh.read()

ticketOffsets = [m.start() for m in re.finditer(b'Root-CA00000003-XS0000000c', ticks)]
ticks = bytearray(ticks)

#This check can fail on other DB files and stuff (since 'Root-CA' can appear in them, but not necessarily mean there are tickets).
#Just make sure to only pass in a ticket.db file or individual tickets (or a file with a bunch of individual ones merged).
if (len(ticketOffsets) == 0):
    print("No tickets found. Did you input the correct file?")
    sys.exit(0)



for offs in ticketOffsets:

    commonKeyIndex = ticks[offs+0xB1]
    if ticks[offs+0x7C] != 0x1: #Try and make sure we're actually dealing with a ticket(this value is always 0x1 in a ticket).
        continue
    if commonKeyIndex > 5: #There's some master rusemen in ticket.db that get past the other checks.
        continue

    anything = anything + 1

    ticketData = bytearray(ticks[offs-0x140:offs+0x210])
    ticketlist.append(  Ticket(ticketData, binascii.hexlify(ticketData[tk+0x9C:tk+0xA4]).lower(), binascii.hexlify(ticketData[tk+0x98:tk+0x9C]), ticks[offs+0xB1]    )   )


print("Number of Tickets                       - " + str(len(ticketlist)))
dupetitles = []
y=Counter()
for tiktik in ticketlist:
    y[tiktik.titleid] += 1
dupetitles = [i for i in y if y[i]>1]
print("Number of Titles with Duplicates        - " + str(len([i for i in y if y[i]>1])))
print("Number of Unique Title Tickets          - " + str( len (list(y) ) ))

systik = 0
for tiktik in ticketlist:
    typecheck = tiktik.titleid[4:8]
    if (int(typecheck,16) & 0x10):
        systik += 1
    elif (typecheck == '8005'):
        systik += 1
    elif (typecheck == '800f'):
        systik += 1
print("Number of System Tickets (ignored)      - " + str(systik))


#does not look at system titles
for tiktik in ticketlist:
    if (tiktik.commonkeyindex == 0):
        if arguments.ignorefake:
            if (tiktik.consoleid == '00000000'):
                not_eshop_tickets.append(tiktik)
            else:
                eshop_tickets.append(tiktik)
        else:
            eshop_tickets.append(tiktik)

if(arguments.ignorefake):

    print("")
    print("Number of eShop Tickets                 - " + str(len(eshop_tickets)))

    dupetitles = []
    y=Counter()
    for tiktik in eshop_tickets:
        y[tiktik.titleid] += 1
    dupetitles = [i for i in y if y[i]>1]
    print("Number of eShop Titles with Duplicates  - " + str(len([i for i in y if y[i]>1])))
    print("Number of Unique eShop Titles           - " + str( len (list(y) ) ))

    print("Number of (non system) Tickets NOT from your eShop (ignored)     - " + str(len(not_eshop_tickets)))
    print("^^ can be installed CIAs, preinstalled games/apps etc... things not from YOUR eshop")
    print("^^ if you want to dump your preinstalled game, you need to use -preinstalled")

print("")

print("Going through the list...\n")
#reverse because newest ticket is at the bottom (maybe not a 100% fact?) (not really needed anyway  i think)
for tiktik in reversed(eshop_tickets):
   
    titleid = tiktik.titleid
    typecheck = titleid[4:8]

    if (typecheck in typelist) or (dlSystem and tiktik.commonkeyindex) or dlALL or (titleid in titlelist):

        if titleid in completetitleids:
            print("Complete title id...")
            continue

        if titleid in ignorelist:
            print("You are ignoring this title...")
            continue

        if (binascii.hexlify(tiktik.data[0x00:0x04]) != '00010004'):
            print("***\nTitle ID: " + titleid.decode("utf-8"))
            print("Bad Ticket Found... ignoring")
            print("There should be a good ticket later, no problem\n***\n")
            continue

        if arguments.preinstalled:
            if (tiktik.consoleid == '00000000'):
                print("Looks like we found the preinstalled game!\n")
            else:
                continue

        if (typecheck == '0000'):
            titletype = 'eShopApp'
            eshopapp = eshopapp + 1
        elif (typecheck == '0001'):
            titletype = 'DownloadPlayChild'
            dlp = dlp +1
        elif (typecheck == '0002'):
            titletype = 'Demo'
            demo = demo +1
            if(arguments.patch_demo):
                tiktik.data[tk+0x124:tk+0x164] = binascii.a2b_hex('00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
        elif (typecheck == '000e'):
            titletype = 'UpdatePatch'
            updatepatch = updatepatch + 1
        elif (typecheck == '008c'):
            titletype = 'DLC'
            dlc = dlc + 1
            if(arguments.patch_dlc):
                tiktik.data[tk+0x164:tk+0x210] = binascii.a2b_hex('00010014000000ac000000140001001400000000000000280000000100000084000000840003000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
        elif (typecheck == '8004'):
            titletype = 'DSiWare'
            dsiware = dsiware + 1
        #system apps
        elif (int(typecheck,16) & 0x10):
            titletype = 'System'
            system = system + 1
        elif (typecheck == '8005'):
            titletype = 'DSiSystemApp'
            dsisysapp = dsisysapp + 1
        elif (typecheck == '800f'):
            titletype = 'DSiSystemDataArchives'
            dsisysdata = dsisysdata + 1
        else:
            titletype = 'OtherContent---Mystery'
            print("??? No Idea what this is, probably cannot make a CIA. - " + titleid)
            print("Skipping title...")
            mystery = mystery + 1
            continue
       
        if(arguments.blank_ids):
            tiktik.data[tk+0x98:tk+0x9C] = binascii.a2b_hex('00000000')
            tiktik.data[tk+0xDC:tk+0xE0] = binascii.a2b_hex('00000000')

        if(arguments.output_dir is not None):
            if(arguments.sort):
                rawdir = os.path.join(arguments.output_dir, 'raw', titletype, titleid)
                ciadir = os.path.join(arguments.output_dir, 'cia', titletype, titleid)
            else:
                rawdir = os.path.join(arguments.output_dir, 'raw', titleid)
                ciadir = os.path.join(arguments.output_dir, 'cia', titleid)
        else:
            if(arguments.sort):
                rawdir = os.path.join('raw', titletype, titleid)
                ciadir = os.path.join('cia', titletype, titleid)
            else:
                rawdir = os.path.join('raw', titleid)
                ciadir = os.path.join('cia', titleid)

        if not os.path.exists(rawdir):
            os.makedirs(os.path.join(rawdir))
        open(os.path.join(rawdir, 'cetk'),'wb').write(tiktik.data+magic)

        completetitleids.append(titleid)

        #download stuff
        if(arguments.download):
            print("Downloading TMD...")

            if error:
                error = False

            baseurl = 'http://ccs.cdn.c.shop.nintendowifi.net/ccs/download/' + titleid

            for attempt in range(arguments.retry_count+1):
                try:
                    if(attempt > 0):
                        print("*Attempt " + str(attempt+1) + " of " + str(arguments.retry_count+1))
                    tmd = urllib2.urlopen(baseurl + '/tmd')
                except (urllib2.URLError, e):
                    print("Could not download TMD...")
                    error = True
                    continue
                error = False
                print("Downloaded TMD OK!")
                break
           
            if error:
                print("ERROR: Could not download TMD. Skipping title...\n")
                writeInLog('Trouble downloading tmd:  ' + baseurl + '/tmd')
                writeInLog('CIA not created:  ' + titleid)

            if not error:

                print("")

                tmd = tmd.read()
                open(os.path.join(rawdir) + '/tmd','wb').write(tmd)

                #download stuff
                print("Downloading Contents...")

                contentCount = int(binascii.hexlify(tmd[tk+0x9E:tk+0xA0]),16)
                for i in xrange(contentCount):
                    if not error:
                        cOffs = 0xB04+(0x30*i)
                        cID = binascii.hexlify(tmd[cOffs:cOffs+0x04])
                        print("Downloading " + str(i+1) + " of " + str(contentCount) + ". This file is " + bytes2human(int(binascii.hexlify(tmd[cOffs+0x08:cOffs+0x10]),16)))
                        outfname = os.path.join(rawdir, cID)
                       
                        for attempt in range(arguments.retry_count+1):
                            try:
                                if(attempt > 0):
                                    print("Attempt " + str(attempt+1) + " of " + str(arguments.retry_count+1))
                                response = urllib2.urlopen(baseurl + '/' + cID)
                                chunk_read(response, outfname, report_hook=chunk_report)
                                if (int(os.path.getsize(outfname)) != int(binascii.hexlify(tmd[cOffs+0x08:cOffs+0x10]),16) ):
                                    print("Content download not correct size\n")
                                    continue
                            except (urllib2.URLError, e):
                                print("Could not download content file...\n")
                                error = True
                                continue
                            error = False
                            break
                       
                        if error:
                            print("ERROR: Could not download content file... Skipping title")
                            writeInLog('Could not download content file:  ' + baseurl + '/' + cID)
                            writeInLog('CIA not created:  ' + titleid)
                       
                        print("")
               

                if not error:
                    print("Title download complete\n")

                    #cia generation
                    if(arguments.build):
                        if not os.path.exists(ciadir):
                            os.makedirs(ciadir)
                        makecommand = ' ' + os.path.join(rawdir) + ' ' + os.path.join(ciadir, titleid) + '.cia'
                        os.system(execname + makecommand)
                        if(os.path.isfile(os.path.join(ciadir, titleid) + '.cia')):
                            print("CIA created ok!")
                        else:
                            print("CIA not created...")
                            writeInLog('CIA not created:  ' + titleid)
                        print("")
                        print("")




print("eShop Apps            -  " + str(eshopapp))
print("Download Play Child   -  " + str(dlp))
print("Demo                  -  " + str(demo))
print("Update Patches        -  " + str(updatepatch))
print("DLC                   -  " + str(dlc))
print("System                -  " + str(system))
print("DSiWare               -  " + str(dsiware))
print("DSi System Apps       -  " + str(dsisysapp))
print("DSi System Data       -  " + str(dsisysdata))
print("Mystery Content       -  " + str(mystery))
total = eshopapp + demo + dlp + updatepatch + dlc + system + dsiware + dsisysapp + dsisysdata + mystery
print("\nAll tickets/titles found in file (might contain duplicates) - " + str(anything) +  ". You selected " + str(total) + " of them")

if(usedlog):
    print("\n\n***\n" + "*** " + " A log file was created - errors/problems are listed in there.\n" + "***")
 

chronoqz

Member
Newcomer
Joined
Apr 8, 2015
Messages
8
Trophies
0
Age
36
XP
63
Country
United States
i have a 3ds mario & luigi bundle, can i ask, is there any way to remove permanently from the eshop that digital content?, so when i dump the ticket that game doesnt show up
 

alatnet

Member
Newcomer
Joined
Aug 13, 2007
Messages
11
Trophies
0
XP
218
Country
United States
ok, i must be doing something stupid but i've been trying to use this and it doesn't seem to want to download any of the cia's even when i throw in the "ALL" type. though it's saying that pretty much EVERY title is a bad id with the "ALL" type. I did modify it to be able to use python 3.4 with no problems (just a few minor edits of converting print statements to print functions and change the urllib to urllib.request). Also had to yank out the os check but rigged it for windows version.

My 3ds is an n3ds and it took a while to get the ticket.db but i was able to get it.

Any help is appreciated.

Ok, now this is interesting. I dont know what the heck is up with python but it seems to not even get to the "if (typecheck == '0000'):" section of if statements.
I'm not much of a python program but i am converting this script into a java program that will give a gui interface to all of this but currently stuck at the part of bytes and needing to yank out the stuff in hex... so it's going to take a while... XP
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
    Xdqwerty @ Xdqwerty: and yes ik im stupid for using it