QL.túra

Kulturális smörgåsbord Esterházytól Wass Albertig, a gumicsizmától az iPad-ig, a Teletabiktól Sexpírig, a makrofágtól a mikrokontrollerig, miazmás...

Címkék

2000 (20) 2001 (6) 2002 (16) 2003 (164) 2004 (61) 2005 (40) 2006 (31) 2007 (28) 2008 (33) 2009 (175) 2010 (188) 2011 (201) 2012 (86) 2013 (40) 2014 (36) 2015 (26) 2016 (10) adáshiba (91) android (1) animáció (93) cygwin (3) film (410) gezarol (13) hájtek (159) hangoskönyv (32) ipad (17) klip (12) könyv (191) linux (29) színház (169) vers (17) windows (37) zene (111) Címkefelhő

+jegyzések

Most ...

... múlok .osan

bmi_tiny.png


... hallgatom
Szabó Magda: Régimódi történet
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSqTmZEqlCRFgojyt52Q2n_qiMTyVlt-zJu-DpbNKVY6OQbBh4u


... olvasom
Alexander Steele (szerk.): Kezdő írók kézikönyve


... (Kik ezek? Kik ezek az embek? Kik ezek?)
profile for TrueY on Stack Exchange, a network of free, community-driven Q&A sites
free counters

[Hájtek] Palm m515 adatbázisok konverziója #1

2011.11.29. 11:00 | TrueY | Szólj hozzá!

Címkék: 2011 hájtek

Nagyon szeret(t)em a Palm-omat. Nekem egy m515-ös jutott. Anno direkt olyan PDA-t kerestem, amit (közel) kézírással lehet vezérelni (részletesebb értékelés). Használtan vettem. Sajnos "El hulla! Virág! El iram! Lik az élet..." és 10+ év alatt az aksija erősen elpunnyadt és az okostelefonok világában már felesleges még egy kütyü. Bár még bírja pár 1-2 órát világítás nélkül, de nem az igazi. Infra porton keresztül szinkronizálom és ehhez egy olyan laptop-ot kell üzemben tartanom, ami szintén a szétesés határán van. Egy remek Win98 fut rajta (mondjuk ezzel a ATMEL mikrokontrollereket is jó programozni, mert még van párhuzamos és soros port is rajt).

A feladat (és a poszt tulajdonképpeni témája) az lenne, hogyan tudjuk lementeni a kütyüre felhalmozott adatokat és hogyan tudjuk valami olvasható formátumra konvertálni.

Első körben kell csinálni egy szinkronizációt. Ezzel az adatok átkerülnek a laptopra. Keressük meg, hogy hol is vannak? Hopp, ide pakolja: C:\Palm\<user>\Backup. Az Archive alá pedig a már letörölt app-ok és adatbázisai kerülnek. Az alap appok, úgymint address, datebook, memopad, todo pedig ezekbe a könyvtárakba kerülnek. Két speciális app-pot használok. Az egyik a Cryptopad. Ez lényegében megegyezik a memopad-dal, csak a tartalmakat jelszóval lehet titkosítani. Itt tárolom az összes nem publikus infót. A másik a Pilot-DB. Ez egy egyszerű adatbázis felület. Hasznos kis táblázatokat dobhatunk össze, illetve írtam egy perl szkriptet, ami több XLS táblázatot alakít át Pilot-DB formátumba és így pl. a könyveimet, CD-imet, filmjeimet tudom beinjektálni a szisztémába.

Első nekifutásra a beépített adatbázisok konverziójával próbálkoztam. Némi borzolás után találtam majdnem teljesen jó leírást az adatbázisokról (általános infó, address, datebook, memopad, todo). Ahhoz, hogy a Cryptopad-ot is teljesen le tudjam menteni, leszedtem a védett doksikról a jelszóvédelmet. Erre egyből megjelentek, mint sima memopad bejegyzésekként.

Sajnos nem teljesen komplett, illetve egyértelmű a leírás. Leltem pár példa programot, ami segített az első lépésekben, de ezek mindegyike speciálisan egy típusú adatbázisra volt kihegyezve. Pedig sok a közös az egyes adatbázisok felépítésében. Megírtam hát saját dekódoló programomat perl-ben. És ezt most be is pakolom...

Jöjjön hát a szokásos perl header:

#!/usr/bin/perl
#...

use strict;
use warnings;
use POSIX;
Az adatbázis lényegében egymás után dobált adatmezőkből áll. Van pár függvény, ami az adatokat beolvassa.

sub CString(); # Read a CString
sub Short();   # Read a short (2 bytes)
sub Long();    # Read a long (4 bytes)
sub Char($);   # Read n bytes
sub Dt($);     # Convert unix time to date
A Short és Long egyszerűen beolvas 2, vagy 4 byte-ot. A CString egy Pascal szerű string, aminek az első egy, vagy első 3 byte-ja írja le a hosszát. A Char pár byte-ot olvas be. A Dt pedig csak beformáz egy unix időt.

Most jön az egyes adatbázisok leírása

my @field_types = ("None", "Integer", "Float", "Date", "Alpha",
    "CString", "Boolean", "Bitflag", "RepeatEvent");

# '_' common fields, '#' date (Date or Integer)
my %field_names = (
    ADDRESS => [ "_Record ID", "_Status", "_Position",
        "Name", "First", "Title", "Company",
        "Phone 1 Label ID", "Phone 1",
        "Phone 2 Label ID", "Phone 2",
        "Phone 3 Label ID", "Phone 3",
        "Phone 4 Label ID", "Phone 4",
        "Phone 5 Label ID", "Phone 5",
        "Address", "City", "State", "Zip", "Country",
        "Note", "_Private", "_Category",
        "Custom 1", "Custom 2", "Custom 3", "Custom 4",
        "Display Phone" ],
    DATEBOOK => [ "_Record ID", "_Status", "_Position",
        "#Start Time", "#End Time", "Description", "Duration",
        "Note", "Untimed", "_Private", "_Category", "Alarm Set",
        "Alarm Adv Units", "Alarm Adv Type", "Repeat Event" ],
    MEMOPAD => [ "_Record ID", "_Status", "_Position",
        "Memo Text", "_Private", "_Category" ],
    TODO => [ "_Record ID", "_Status", "_Position",
        "Description", "#Due Date", "Completed", "Priority",
        "_Private", "_Category", "Note" ]
);
A '_' jellel a közös adatmezőket jelölöm (csak, hogy lássam). A '#'-kal pedig a dátumokat jelölő mezőket. Sajnos nem minden dátum mező dátum formátumú (nem értem, hogy miért).

Most pedig definiáljunk pár parancssori argumentumot. A '-a' address, '-d' datebook, '-m' memopad, '-t' todo adatbázis. Alapban ilyenkor a file neve és alapértelmezett elérési útja is adott. Persze megadhatunk közvetlenül egy adatbázis file-t. Ilyenkor azt nyitja meg.if ($#ARGV == -1) { die "No argument"; }
my $input_file;
if ($ARGV[0] =~ /^-(.)/) {
    my $name;
    if ($1 eq "a") { $name = "address";
    } elsif ($1 eq "d") { $name = "datebook";
    } elsif ($1 eq "m") { $name = "memopad";
    } elsif ($1 eq "t") { $name = "todo";
    } else { die "Bad argument ($1)";
    }
    $input_file = "$name/$name.dat";
} else { $input_file = $ARGV[0];
}
Végre megnyitható a file. Az első 4 byte tartalmazza a file típusát. open(CF, "<$input_file") or die "Can't open $input_file: $!\n";
binmode (CF);
my $byte = 0; # Keep track of data offset. Just in case.

my $TYPE;
my $tag = Char(4);
if ($tag eq "\x00\x01BA") { $TYPE = "ADDRESS";
} elsif ($tag eq "\x00\x01BD") { $TYPE = "DATEBOOK";
} elsif ($tag eq "\x00\x01PM") { $TYPE = "MEMOPAD";
} elsif ($tag eq "\x00\x01DT") { $TYPE = "TODO";
} else { die "Bad file tag info ($tag)";
}

if (!exists $field_names{$TYPE}) { die "Field names not filled in"; }
Akkor először dekódolni kell a fejlécet.

#
# Header
#

my $x;
print "=" x 30, "\n";
$x = CString; printf "* Filename: %s\n", $x;
$x = CString; printf "* Custom names: %s\n", $x;
$x = Long; printf "* Next Free Category ID: %d\n", $x;
my $count = Long; printf "* Number of category entries: %d\n", $count;

print "* Category (count: $count)\n";
my %category_names;
for (my $i = 0; $i < $count; ++$i) {
    my ($seq, $id, $flag, $long, $short) =
    (Long, Long, Long, CString, CString);
    printf "  #%2d, ID: %2d, Flag: %d, lName: %s, sName: %s\n",
    $seq, $id, $flag, $long, $short;
    if (exists $category_names{$id}) { die "Duplicated category name"; }
    $category_names{$seq} = $long;
}

$x = Long; printf "* Schema Resource ID: %d\n", $x;
$x = Long; printf "* Schema Fields per row: %d\n", $x;
$x = Long; printf "* Schema Record ID Position: %d\n", $x;
$x = Long; printf "* Schema Record Status Position: %d\n", $x;
$x = Long; printf "* Schema Placement Position: %d\n", $x;
my $field_count = Short;
printf "* Schema Field Count: %d\n", $field_count;
printf "* Schema field entries\n";
my @schema_entries;
for (my $i = 0; $i < $field_count; ++$i) { push @schema_entries, Short; }
print "  ", join(",", @schema_entries), "\n";
my $all_field_count = Long; printf "* Entries count: %d\n", $all_field_count;
if ($all_field_count % $field_count != 0) { die "Bad field count"; }
my $rec_count = $all_field_count / $field_count;
printf "[ Record count: %d ]\n", $rec_count;
És a fejléc után az adat rekordokat is.

#
# Processing Records
#

print "=" x 30, "\n";
for (my $j = 0; $j < $rec_count; $j++) {
    printf "\n* Record: %d / $rec_count\n", $j + 1;
    for (my $i = 0; $i < $field_count; ++$i) {
        my $type = $schema_entries[$i];
        if ($type != Long) { die "Bad type ($i, $type)"; }
        my $v;

        # Int, Date, Boolean
        if ($type == 1 || $type == 3 || $type == 6) { $v = Long;
        } elsif ($type == 5) { # CString
            if (Long != 0) { die "Not zero string padding"; }
            $v = CString;
        } elsif ($type == 8) { # RepeatEvent
            $v = "See record above";
            printf "  Repeat Event (%d:%s)\n", $type,
                    $field_types[$type];
            my $count = Short; printf "    Date exception: %d\n",
                    $count;
            my $x;
            for (my $k = 0; $k < $count; ++$k) {
                $x = Long;
                printf "    Exception entry: %s\n", Dt $x;
            }
            $x = Short;
            if ($x != 0) {
                if ($x == 0xFFFF) {
                    printf "    CLASS-ENTRY\n", $x;
                    $x = Short; printf "      1: %d\n", $x;
                    my $len = Short;
                    printf "      Length: %d\n", $len;
                    $x = Char($len);
                    printf "      Class name: %s\n", $x;
                } else {
                    printf "    Repeat Event Flag: %d\n",
                            $x & 0x7FFF;
                }
                my $brand = Long;
                printf "    Brand: %d (%s)\n", $brand,
                    ("Daily", "Weekly", "Monthly-by-Day",
                     "Monthly-by-Date",
                     "Yearly-by-Date", "Yearly-by-Day")[$brand];
                $x = Long; printf "    Interval: %d\n", $x;
                $x = Long; printf "    End Date: %s\n", Dt $x;
                $x = Long; printf "    First Day of Week: %d\n", $x;
                if ($brand < 0 || $brand > 6) {
                    die "Bad brand ($brand)";
                }
                printf "    Brand data\n";
                if ($brand >= 1 && $brand <= 3) {
                    $x = Long; printf "      Day Index: %d\n", $x;
                }
                if ($brand == 2) {
                    $x = Char(1);
                    printf "      Days Mask: %d\n", unpack "c", $x;
                }
                if ($brand == 3) {
                    $x = Long; printf "      Week Index: %d\n", $x;
                }
                if ($brand == 4 || $brand == 5) {
                    $x = Long; printf "      Day Number: %d\n", $x;
                }
                if ($brand == 5) {
                    $x = Long; printf "      Month Index: %d\n", $x; 
                }
            }
        } else { die "Not handled value";
        }
   
        my $fld_name = $field_names{$TYPE}[$i];
        if ($fld_name eq "_Category") {
            if (!exists $category_names{$v}) { $v .= " (N/A)";
            } else { $v .= " (".$category_names{$v}.")";
            }
        } elsif ($fld_name eq "_Status") {
            my @status;
            if ($v & 0x01) { push @status, "Add"; }
            if ($v & 0x02) { push @status, "Update"; }
            if ($v & 0x04) { push @status, "Delete"; }
            if ($v & 0x08) { push @status, "Pending"; }
            if ($v & 0x80) { push @status, "Pending"; }
            $v .= " (".join(",", @status).")";
        } elsif ($fld_name eq "Alarm Adv Type") {
            $v = $v." (".("Minutes", "Hours", "Days")[$v].")";
        }
        if (substr($fld_name, 0, 1) eq '#') { $v = Dt $v; }
        printf "  %s (%d:%s): %s\n", $fld_name, $type,
                $field_types[$type], $v;
    }
}

exit;
És végül a beolvasó segéd függvények.

sub CString() {
    my $pad = shift;

    read (CF, $_, 1) or die "Unable to get CString length: $!";
    ++$byte;
    my $x = unpack "C", $_;
    if ($x == 0) { return ""; }
    if ($x == 0xFF) {
        read (CF, $_, 2) or
            die "Unable to get CString long length: $!";
        $byte += 2;
        $x = unpack "s", $_;
    };
    read (CF, $_, $x) or
        die "Unable to get CString: $!" unless ($x == 0);
    $byte += $x;
    return $_;
} # End of CString

sub Short() {
    read (CF, $_, 2) or die "Unable to get Short: $!\n";
    $byte += 2;
    #return unpack "s", $_; # signed
    return unpack "S", $_; # unsigned
} # End of Short

sub Long() {
    read (CF, $_, 4) or die "Unable to get Long: $!\n";
    $byte += 4;
    #return unpack "l", $_; # signed
    return unpack "L", $_; # unsigned
} # End of Long

sub Char($) {
    my $n = shift;
    read (CF, $_, $n) or die "unable to get Char: $!";
    $byte += $n;
    return $_;
}

sub Dt($) { my $t = shift; return $t.strftime(" (%Y-%m-%d %T)", localtime $t);

A második eset a Pilot-DB adatbázisok konverziója. Ennek mikéntjét egy másik posztban fogom a világ szeme elé tárni.

Tárogassunk minden nap!

 

· 1 trackback

A bejegyzés trackback címe:

https://qltura.blog.hu/api/trackback/id/tr783283784

Trackbackek, pingbackek:

Trackback: Rablóhókuszpókusz a keresőoptimalizálás? 2011.11.30. 11:36:46

Számos weboldalt megvizsgáltunk, az okokat keresve az alacsony vagy nem megfelelő számú látogatottságra.

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása