locdat for Sorting SD Card Channels

Discussion in 'Wii - Hacking' started by Skeeve, Aug 13, 2010.

Aug 13, 2010
  1. Skeeve
    OP

    Newcomer Skeeve Advanced Member

    Joined:
    Jan 1, 2010
    Messages:
    67
    Country:
    Germany
    Hi!

    In another forum I found a question whether or not there is an easier way to sort Channels on the SD Card because it is a bit boring to do it on the wii. Maybe there are tools around, but I don't know of any.

    So I searched a bit and hacked a small perl script that might help you (or some of you). It's just in early alpha stage but as I go on vacation now, I thought I'd better push it out for anyone to test. Help and man page for example are currently completely missing.

    Prerequisites are:<ol type='1'><li>perl must be installed</li><li>perl module Digest::MD5 (should be standard)</li><li>working openssl installation</li></ol>The prerequisites should be fulfilled by OS X (what I use) and Linux (I guess). For WinDOS you'll have to figure it out on your own.

    Copy the code below into a text file called "locdat" and make it executable.

    Insert your SD Card containing your channels into the computer.

    Start the scriot with <!--fonto:Courier New--><span style="font-family:Courier New"><!--/fonto-->./locdat /Path/to/SD/Card<!--fontc--></span><!--/fontc-->. This will output a list of all the channels found. The list will look like this:

    01-1-A: [XXXX] Name of the channel Region Platform

    Meaning:
    01: Page 1 of the SD Card menu (will go from 01 to 20)
    1: Row 1 (will go from 1 to 3)
    A: Column 1 (will go from A to D)
    [XXXX]: The 4 Letters of the Channel's ID
    Region: Something like "Europe", "US" ...
    Platform: Something like "WiiWare", "N64", "Neo-Geo"...

    If you save the list in a text file, either by redirecting the ouput or by calling <!--fonto:Courier New--><span style="font-family:Courier New"><!--/fonto-->./locdat /Path/to/SD/Card -out loc.txt<!--fontc--></span><!--/fontc-->, you can afterwards open the textfile in any descent texteditor (jEdit for example) and change the sequence to whatever you like. When Sorting, just the Channel ID in brackets and the colon before it is important. Everything else is ignored. So no need to move the cell addresses (01-1-A) around.

    There are 2 special cases:
    1. A line having just minus signs after the colon will create a page break in the SD Card Menu
    2. A line having no Channel ID after the colon will create an empty entry

    When you are satisfied with your changes, call <!--fonto:Courier New--><span style="font-family:Courier New"><!--/fonto-->./locdat /Path/to/SD/Card -e -in loc.txt<!--fontc--></span><!--/fontc--> to encode the new loc.dat in its default location.

    I hope the explanation is sufficient enough. If not, just ask and hope for someone else to jump in as I won't be back before August 29 <img src="style_emoticons/<#EMO_DIR#>/wink.gif" style="vertical-align:middle" emoid=";)" border="0" alt="wink.gif" />

    Note the 2 lines in the code you might need or want to modify:
    a) my $OPENSSL= '/usr/bin/openssl';
    b) my $lang= 'DE';

    Set in a) the path to your openssl binary.
    Set in b) your preferred language. As I'm German, I use "DE" here. This will pick the german Channel Description. Legal values are: JP US DE FR ES IT NL

    Question: Is anyone here who might want to host the script so that the Copy&Paste procedure is not necessary but one can download the file?

    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->#!/usr/bin/perl
    use strict;
    use warnings;

    use Digest::MD5;
    use Encode 'decode';
    use Getopt::Long;
    use Pod::Usage;

    =head1 locdat

    No POD yet

    SYNOPSIS

    decoding

    locdat SD_card_location
    ÂÂÂÂwill decode loc.dat from the default location and print to STDOUT

    locdat SD_card_location -out filename
    ÂÂÂÂwill decode loc.dat from the default location and store it in filename

    encoding

    locdat SD_card_location -e
    ÂÂÂÂwill encode STDIN and store the resulting loc.dat in the default location

    locdat SD_card_location -in filename [-e]
    ÂÂÂÂwill encode filename's content to a new loc.dat at the default location

    locdat -out another_loc.dat [-e]
    ÂÂÂÂwill create a new loc.dat named "another_loc.dat" from STDIN

    locdat -in filename -out another_loc.dat [-e]
    ÂÂÂÂwill create a new loc.dat named "another_loc.dat" from filename's content

    =cut

    # Where is openssl located?
    my $OPENSSL= '/usr/bin/openssl';

    # Preferred language
    my $lang= 'DE';

    # Default loc.dat location relative to SD Card location
    my $defaultloc= 'private/wii/loc.dat';

    # Default title location relative to SD Card location
    my $defaulttitle= 'private/wii/title';

    # Magic
    my $magic= 'sdal';

    # These are the Wii's shared secrets
    # Taken from http://hackmii.com/1208/04/keys-keys-keys/
    my %keys=(
    ÂÂÂÂ'md5-blanker' => "\x0e\x65\x37\x81\x99\xbe\x45\x17\xab\x06\xec\x22\x45\x1a\x57\x93",
    ÂÂÂÂ'sd-iv'ÂÂÂÂÂÂ => "\x21\x67\x12\xe6\xaa\x1f\x68\x9f\x95\xc5\xa2\x23\x24\xdc\x6a\x98",
    ÂÂÂÂ'sd-key'ÂÂÂÂÂÂ=> "\xab\x01\xb9\xd8\xe1\x62\x2b\x08\xaf\xba\xd8\x4d\xbf\xc2\xa5\x5d",
    );

    # VC info taken from http://wiibrew.org/wiki/Virtual_Console
    my %vc_info=(
    ÂÂÂÂC => 'Commodore 64',
    ÂÂÂÂE => 'Neo-Geo',
    ÂÂÂÂF => 'NES',
    ÂÂÂÂH => 'Wii channels', # (Only for saved channels, not VC)
    ÂÂÂÂJ => 'SNES',
    ÂÂÂÂL => 'Master System',
    ÂÂÂÂM => 'Genesis/Mega Drive',
    ÂÂÂÂN => 'N64',
    ÂÂÂÂP => 'TurboGrafx-16/PC Engine',
    ÂÂÂÂQ => 'TurboGrafx-CD',
    ÂÂÂÂR => 'Wii DVD', # (Only for savegames, not VC)'
    ÂÂÂÂW => 'WiiWare',
    );

    # Region info taken from the same source
    my %region_info=(
    ÂÂÂÂE => 'USA',ÂÂÂÂ
    ÂÂÂÂJ => 'Japan',ÂÂ
    ÂÂÂÂP => 'Europe',ÂÂ# (and other PAL regions such as Australia)
    ÂÂÂÂD => 'Germany', # (only if separate version)
    ÂÂÂÂF => 'France',ÂÂ# (only if separate version)
    ÂÂÂÂI => 'Italian', # (only if separate version)
    ÂÂÂÂS => 'Spanish', # (only if separate version)
    ÂÂÂÂA => 'All',ÂÂÂÂ # (?, only seen on channels)ÂÂÂÂÂÂÂÂÂÂÂÂÂÂ
    ÂÂÂÂL => 'Limited', # (region free games)ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ
    ÂÂÂÂM => 'Import',ÂÂ# (confirmed for Japaness imports in Europe)
    ÂÂÂÂN => 'Import',ÂÂ# (confirmed in Japeness imports in US)
    );

    # Localized Channelnames
    # Information from http://wiibrew.org/wiki/Content.bin
    my %lang_offset= (
    ÂÂÂÂJP => 0x09C, # Japanse
    ÂÂÂÂUS => 0x0F0, # English
    ÂÂÂÂDE => 0x144, # German
    ÂÂÂÂFR => 0x198, # French
    ÂÂÂÂES => 0x1EC, # Spanish
    ÂÂÂÂIT => 0x240, # Italian
    ÂÂÂÂNL => 0x294, # Dutch
    );
    my @langs=(qw(JP US DE FR ES IT NL));
    my $namesize= 42*2;
    my $namebytes= $namesize * scalar @langs;
    my $namepattern= "a$namesize" x scalar @langs;


    # Commandline for encrypting
    # "-out <filename>" will be appended
    my @encrypt=(
    ÂÂÂÂ$OPENSSL,
    ÂÂÂÂ'enc',
    ÂÂÂÂ'-aes-128-cbc',
    ÂÂÂÂ'-K',ÂÂjoin('',
    ÂÂÂÂÂÂÂÂunpack('H2' x length $keys{'sd-key'}, $keys{'sd-key'})),
    ÂÂÂÂ'-iv', join('',
    ÂÂÂÂÂÂÂÂunpack('H2' x length $keys{'sd-iv' }, $keys{'sd-iv' })),
    );

    # Commandline for decrypting
    # "-in <filename>" will be appended
    my @decrypt= (@encrypt, '-d');

    my (
    ÂÂÂÂ$encode,
    ÂÂÂÂ$infile,
    ÂÂÂÂ$outfile,
    ÂÂÂÂ$verbose,
    );
    help() unless GetOptions(
    ÂÂÂÂ'e|encode'ÂÂ => \$encode,
    ÂÂÂÂ'in=s'ÂÂÂÂÂÂ => \$infile,
    ÂÂÂÂ'out=s'ÂÂÂÂÂÂ=> \$outfile,
    ÂÂÂÂ'h|help'ÂÂÂÂ => \&help,
    ÂÂÂÂ'm|man'ÂÂÂÂÂÂ=> \&man,
    ÂÂÂÂ'v'ÂÂÂÂÂÂÂÂÂÂ=> \$verbose,
    );

    sub help { pod2usage(-verbose=>1); exit; }
    sub manÂÂ{ pod2usage(-verbose=>2); exit; }

    # infile defined?
    if ($infile) {
    ÂÂÂÂif (not -r $infile) {
    ÂÂÂÂÂÂÂÂdie "Can not find $infile!\n";
    ÂÂÂÂ}
    }

    my $loc_dat;
    # SD Card location defined?
    my ($SD)= @ARGV;
    if (not $SD) {
    ÂÂÂÂ$encode=1;
    ÂÂÂÂif ( not $outfile ) {
    ÂÂÂÂÂÂÂÂdie "For encoding -out or SD Card location is mandatory.\n";
    ÂÂÂÂ}
    }
    else {
    ÂÂÂÂ$loc_dat= "$SD/$defaultloc";
    ÂÂÂÂif ( ! -r $loc_dat ) {
    ÂÂÂÂÂÂÂÂdie "No such file: $loc_dat.\n";
    ÂÂÂÂ}
    }

    if ($encode) { # encoding
    ÂÂÂÂmy $in;
    ÂÂÂÂif ( $infile ) {
    ÂÂÂÂÂÂÂÂopen $in, '<:utf8', $infile
    ÂÂÂÂÂÂÂÂÂÂÂÂor die "Can not read $infile: $!\n";
    ÂÂÂÂ}
    ÂÂÂÂelse {
    ÂÂÂÂÂÂÂÂopen $in, '<&', \*STDIN
    ÂÂÂÂÂÂÂÂÂÂÂÂor die "Can not dup STDIN: $!\n";
    ÂÂÂÂ}
    ÂÂÂÂmy(@list)= ("\x00\x00\x00\x00") x 240;
    ÂÂÂÂmy $i=0;
    ÂÂÂÂwhile (<$in>) {
    ÂÂÂÂÂÂÂÂif (/:\s*-+\s*$/) {
    ÂÂÂÂÂÂÂÂÂÂÂÂ$i+=(12-$i%12)%12;
    ÂÂÂÂÂÂÂÂÂÂÂÂnext;
    ÂÂÂÂÂÂÂÂ}
    ÂÂÂÂÂÂÂÂnext unless /^(?:\s*\d+-\d-[A-DX])?\s*:\s*(?:\[([A-Z\d]{4})\])?/;
    ÂÂÂÂÂÂÂÂif ($i > scalar @list) {
    ÂÂÂÂÂÂÂÂÂÂÂÂdie "Too many entries ".($i+1).".\n";
    ÂÂÂÂÂÂÂÂ}
    ÂÂÂÂÂÂÂÂ$list[$i]= $1 if $1;
    ÂÂÂÂÂÂÂÂ++$i;
    ÂÂÂÂ}
    ÂÂÂÂclose $in;
    ÂÂÂÂmy $padding= "\x00" x 12;
    ÂÂÂÂmy $ctx= Digest::MD5->new;
    ÂÂÂÂ$ctx->add($magic);
    ÂÂÂÂ$ctx->add($keys{'md5-blanker'});
    ÂÂÂÂ$ctx->add(@list);
    ÂÂÂÂ$ctx->add($padding);
    ÂÂÂÂmy $md5= $ctx->digest;
    ÂÂÂÂif ( not $outfile ) {
    ÂÂÂÂÂÂÂÂ$outfile= "$SD/$defaultloc";
    ÂÂÂÂ}
    ÂÂÂÂopen my $out, '|-', @encrypt, '-out', $outfile
    ÂÂÂÂÂÂÂÂor die "Can't write to $outfile: $!\n";
    ÂÂÂÂprint $out $magic, $md5, join('', @list), $padding;
    ÂÂÂÂclose $out;
    }
    else { # decoding
    ÂÂÂÂopen my $in, '-|', @decrypt, '-in', $loc_dat
    ÂÂÂÂor die "Error decrypting '$loc_dat': $!\n";
    ÂÂÂÂ{ÂÂÂÂlocal $/;
    ÂÂÂÂÂÂÂÂ$_=<$in>;
    ÂÂÂÂ};
    ÂÂÂÂclose $in;
    ÂÂÂÂ# Filestructur info taken from http://wiibrew.org/wiki/Loc.dat
    ÂÂÂÂmy($magic, $md5, $list, $padding)= unpack('a4 H32 a960 a12', $_);
    ÂÂÂÂmy @list= unpack "a4" x 240, $list;
    ÂÂÂÂ
    ÂÂÂÂ# Retrieve information from content.bin
    ÂÂÂÂ# structure taken from http://wiibrew.org/wiki/Content.bin
    ÂÂÂÂ
    ÂÂÂÂ# supress openssl warnings. It complains
    ÂÂÂÂ# because not all its data will be read.
    ÂÂÂÂ# The close below appears too early for openssl, but
    ÂÂÂÂ# there is no use in reading more data.
    ÂÂÂÂopen my $olderr, '>&', \*STDERR;
    ÂÂÂÂopen STDERR, '>/dev/null';
    ÂÂÂÂmy @info;
    ÂÂÂÂforeach my $le (@list) {
    ÂÂÂÂÂÂÂÂ# cleanup of the id
    ÂÂÂÂÂÂÂÂ(my $id= $le)=~ tr/\x00//d;
    ÂÂÂÂÂÂÂÂpush (@info, { id => $id });
    ÂÂÂÂÂÂÂÂnext unless $id;
    ÂÂÂÂÂÂÂÂ
    ÂÂÂÂÂÂÂÂ# There is an id. Read information from its file
    ÂÂÂÂÂÂÂÂmy $info= $info[-1];
    ÂÂÂÂÂÂÂÂmy $c_bin= "$SD/$defaulttitle/$id/content.bin";
    ÂÂÂÂÂÂÂÂif (not -r $c_bin) {
    ÂÂÂÂÂÂÂÂÂÂÂÂ# No content.bin for that id
    ÂÂÂÂÂÂÂÂÂÂÂÂwarn "Can not read $id ($c_bin): $!\n";
    ÂÂÂÂÂÂÂÂÂÂÂÂ$info->{'name'}= '*file missing*';
    ÂÂÂÂÂÂÂÂÂÂÂÂnext;
    ÂÂÂÂÂÂÂÂ}
    ÂÂÂÂÂÂÂÂprint $c_bin if $verbose;
    ÂÂÂÂÂÂÂÂ# decode the file (part of it)
    ÂÂÂÂÂÂÂÂopen my $in, '-|', @decrypt, '-in', $c_bin
    ÂÂÂÂÂÂÂÂÂÂÂÂor die "error running openssl on $c_bin: $!\n";
    ÂÂÂÂÂÂÂÂ# skip what we don't want to read
    ÂÂÂÂÂÂÂÂread($in, $_, $lang_offset{$langs[0]});
    ÂÂÂÂÂÂÂÂ# read the channel names in all languages
    ÂÂÂÂÂÂÂÂread($in, $_, $namebytes);
    ÂÂÂÂÂÂÂÂ# ignore the rest of the file
    ÂÂÂÂÂÂÂÂ#{ local $/; <$in>; }
    ÂÂÂÂÂÂÂÂclose $in;
    ÂÂÂÂÂÂÂÂ
    ÂÂÂÂÂÂÂÂ# unpack the names
    ÂÂÂÂÂÂÂÂmy %titles;
    ÂÂÂÂÂÂÂÂ@titles{@langs}= map {
    ÂÂÂÂÂÂÂÂÂÂÂÂ# decode a name. utf16be might be wrong
    ÂÂÂÂÂÂÂÂÂÂÂÂ# could also well be ucs2be
    ÂÂÂÂÂÂÂÂÂÂÂÂ$_= decode('utf16be', $_);
    ÂÂÂÂÂÂÂÂÂÂÂÂs/\x00+$//;
    ÂÂÂÂÂÂÂÂÂÂÂÂtr/\x00/ /;
    ÂÂÂÂÂÂÂÂÂÂÂÂ$_= undef if /^\s+$/;
    ÂÂÂÂÂÂÂÂÂÂÂÂ$_;
    ÂÂÂÂÂÂÂÂ} unpack $namepattern, $_;
    ÂÂÂÂÂÂÂÂ$info->{'name'}= $titles{$lang} || $titles{'US'} || '* no title found *';
    ÂÂÂÂÂÂÂÂ$info->{'RC'}= $region_info{substr($id,3,1)} || '?';
    ÂÂÂÂÂÂÂÂ$info->{'VC'}= $vc_info{ÂÂÂÂsubstr($id,0,1)} || '?',
    ÂÂÂÂ}
    ÂÂÂÂopen STDERR, '>&', $olderr;
    ÂÂÂÂ
    ÂÂÂÂmy $out;
    ÂÂÂÂif ( $outfile ) {
    ÂÂÂÂÂÂÂÂopen $out, '>:utf8', $outfile
    ÂÂÂÂÂÂÂÂÂÂÂÂor die "Can not write to $outfile: $!\n";
    ÂÂÂÂ}
    ÂÂÂÂelse {
    ÂÂÂÂÂÂÂÂopen $out, '>&:utf8', \*STDOUT
    ÂÂÂÂÂÂÂÂÂÂÂÂor die "Can not dup STDOUT: $!\n";
    ÂÂÂÂ}
    ÂÂÂÂ# Print loc.dat in readable form
    ÂÂÂÂmy $i=0;
    ÂÂÂÂ# Pages from 01..20
    ÂÂÂÂfor my $p ('01'..'20') {
    ÂÂÂÂÂÂÂÂ# rows from 1..3
    ÂÂÂÂÂÂÂÂfor my $r (1..3) {
    ÂÂÂÂÂÂÂÂÂÂÂÂ# columns from A..D
    ÂÂÂÂÂÂÂÂÂÂÂÂfor my $c ('A'..'D') {
    ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ# Cell coordinates as PAGE-ROW-COLUMN
    ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂprint $out "$p-$r-$c:";
    ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂmy $info= $info[$i];
    ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ# Print info if we got any
    ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif ( $info->{'id'} ) {
    ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂprintf $out " [%4s] %-42s %-7s %s",
    ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ@{ $info }{qw( id name RC VC )};
    ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
    ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂprint $out "\n";
    ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ++$i;
    ÂÂÂÂÂÂÂÂÂÂÂÂ}
    ÂÂÂÂÂÂÂÂ}
    ÂÂÂÂÂÂÂÂ# Print a page separator.
    ÂÂÂÂÂÂÂÂ# Cell coordinates are 4-X so that sorting
    ÂÂÂÂÂÂÂÂ# the lines will keep them in place
    ÂÂÂÂÂÂÂÂprint $out "$p-4-X: ",'-' x 49 if $p<20;
    ÂÂÂÂÂÂÂÂprint $out "\n";
    ÂÂÂÂ}
    ÂÂÂÂclose $out;
    }<!--c2--></div><!--ec2-->
     
  2. Skeeve
    OP

    Newcomer Skeeve Advanced Member

    Joined:
    Jan 1, 2010
    Messages:
    67
    Country:
    Germany
    No one interested?
     
  3. makinavaja

    Newcomer makinavaja Newbie

    Joined:
    Dec 14, 2010
    Messages:
    1
    Country:
    Spain
    Thank you very much for this script. For me its really usefull having around 160 channels installed on my sd card.
    The script has been fully tested and it works under linux (ubuntu) [​IMG]

    Makinavaja
     

Share This Page