[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[mhc:01575] Re: Palm
On Fri, 07 Jun 2002 10:07:01 +0900,
小関 吉則 (KOSEKI Yoshinori) <kose@xxxxxxxxxxxxxxxxxx> said:
> ふと思ったのですが、
>
> CLIE だと datebook のデータは
> c:/Program Files/SonyPDA/kose/datebook/datebook.dat
> です。これを直接読み書きして HotSync すればいいんじゃないで
> しょうか?
じゃないのかな〜と思って,昨晩
フランス戦を見ながらハックしてみていました.
ColdSync はこの方式のようですね.
とりあえず,構造が簡単なメモ帳から..
pilot-xfer で吸った MemoDB.pdb を
./pdb-dump.rb MemoDB.pdb すると,メモ帳の
内容がだらだら見えると思います.
c:/Program Files/SonyPDA/kose/
にあるメモ帳のデータではどうでしょうか?
--
nom
#!/usr/local/bin/ruby
require 'kconv'
require 'palm-packer'
$DEBUG = true
## DUMP 時の appinfo と sortinfo の長さが 0 だった場合におかしくなりそう.
################################################################
class PalmAppInfo
################################################################
include PalmUnpacker
include PalmPacker
def initialize(s = nil)
@category, @id, @modflag, @trailer = [], [], 0, ''
load(s) if s
end
def load(s)
ptr = 0
@modflag = UInt16(s[ptr, 2]); ptr += 2
for i in 0 .. 15
@category [i] = Asciiz16(s[ptr, 16]); ptr += 16
print "category[#{i}] = #{@category[i]}\n" if $DEBUG
end
for i in 0 .. 15
id = UInt8(s[ptr, 1]); ptr += 1
print "category-id[#{i}] = #{id}\n"
@id[i] = id
end
lastid = UInt8(s[ptr, 1]); ptr += 1
@trailer = s[ptr .. (s .length - 1)]
print "appinfo lastid: #{lastid}\n" if $DEBUG
print "appinfo trailer: #{@trailer}\n" if $DEBUG
if lastid != category_last_id()
STDERR .print "Warn: appinfo damaged ?\n"
end
debug_report() if $DEBUG
end
def dump
ret = revUInt16(@modflag)
for i in 0 .. 15
ret += revAsciiz16(@category[i] || '')
end
for i in 0 .. 15
ret += revUInt8(@id[i])
end
ret += revUInt8(category_last_id())
ret += @trailer
return ret
end
def debug_report
print "appinfo_category: @modflag = #{@modflag}\n"
for i in 0 .. 15
print "appinfo_category: @category[#{i}] = #{@category[i]}\n"
end
for i in 0 .. 15
print "appinfo_category: @id[#{i}] = #{@id[i]}\n"
end
print "appinfo_category: last_id: #{category_last_id}\n"
print "appinfo_category: @trailer: #{@trailer}\n"
end
def length
return dump .length
end
def add_category(category)
if !category_exist?(category) and (i = category_new_index())
@category[i], @id[i], @modflag[i] = category, category_new_id(), 1
return true
end
return nil
end
def del_category(category)
if i = category_name_to_index(category)
id = @id[i]
@category[i], @id[i], @modflag[i] = nil, 0, 1
return true
end
return nil
end
def ren_category(old, new)
if i = category_name_to_index(old)
@category[i], @modflag[i] = new, 1
return true
end
return nil
end
def each_category
@category .each {|c| yield c}
end
private
def category_name_to_index(category)
return @category .index(category)
end
alias category_exits? category_name_to_index
def category_last_id()
return (@id .max || 0)
end
def category_new_id()
return category_last_id() + 1
end
def category_new_index()
for i in 0 .. 15
return i if !@category[i]
end
return nil
end
end
################################################################
class PalmSortInfo
################################################################
def initialize(s)
@str = s
end
def length
return @str .length
end
def dump
return @str
end
end
################################################################
class PalmPdbRecord
################################################################
def initialize(rid, attr_and_cat, data)
@rid = rid
@attr = attr_and_cat
@data = data
end
def rid ; return @rid ;end
def attr ; return @attr ;end
def data ; return @data ;end # xxx debug
def set_id(rid)
raise "Integer required." if !(id .is_a?(Integer))
@rid = rid
return self
end
def length
dump .length
end
def deleted? ; return (@attr & 0x80 != 0) ;end
def dirty? ; return (@attr & 0x40 != 0) ;end
def busy? ; return (@attr & 0x20 != 0) ;end
def secret? ; return (@attr & 0x10 != 0) ;end
# def archived? ; return (@attr & 0x08 != 0) ;end
# archived flag is for deleted data.
# so it can be overlap with category data.
def set_deleted ; @attr |= 0x80; return self ;end
def set_dirty ; @attr |= 0x40; return self ;end
def set_busy ; @attr |= 0x20; return self ;end
def set_secret ; @attr |= 0x10; return self ;end
# def set_archived ; @attr |= 0x08; return self ;end
def reset_deleted ; @attr &= ~0x80; return self ;end
def reset_dirty ; @attr &= ~0x40; return self ;end
def reset_busy ; @attr &= ~0x20; return self ;end
def reset_secret ; @attr &= ~0x10; return self ;end
# def reset_archived ; @attr &= ~0x08; return self ;end
def category ; return (@attr & 0x0f) ;end
def set_category(c); @attr = (@attr & 0xf0) | (0x0f & c); return self ;end
def attr_as_string
attr_str = []
attr_str << 'Deleted' if deleted?
attr_str << 'Dirty' if dirty?
attr_str << 'Busy' if busy?
attr_str << 'Secret' if secret?
# attr_str << 'Archived' if archived?
return attr_str .join (' ')
end
################
## suposed to be be modified in sub class.
def dump; return @data .to_s; end
end
################################################################
class PalmPdb
################################################################
include PalmUnpacker
include PalmPacker
attr :name, true
attr :attr, true
attr :version, true
attr :ctime, true
attr :mtime, true
attr :btime, true
attr :modnum, true
attr :type, true
attr :creator, true
attr :idseed, true
### for debug
attr :records
attr :gap
attr :appinfo
attr :sortinfo
attr :num_records
attr :s
def file_length
@s .length
end
def length
len = 0
@records .each{|r|
len += r .data .length
}
return 0x4e + len +
(@records .length) * 8 +
(@appinfo || '').length +
(@sortinfo || '') .length +
@gap .length
end
def initialize(filename)
file = File .open(filename)
@s = file .gets(nil)
s = @s
@record_class = PalmPdbRecord
@name = Asciiz32 (s[0x00, 32])
@attr = UInt16 (s[0x20, 2])
@version = UInt16 (s[0x22, 2])
@ctime = PalmDate (s[0x24, 4])
@mtime = PalmDate (s[0x28, 4])
@btime = PalmDate (s[0x2c, 4])
@modnum = UInt32 (s[0x30, 4])
appinfo_id = LocalID (s[0x34, 4])
sortinfo_id = LocalID (s[0x38, 4])
@type = Ascii4 (s[0x3c, 4])
@creator = Ascii4 (s[0x40, 4])
@idseed = UInt32 (s[0x44, 4])
next_reclist_id = LocalID (s[0x48, 4])
num_records = UInt16 (s[0x4c, 2])
@num_records = num_records # for debug
if next_reclist_id != 0
raise "Unsupported format."
end
################
## make array of records.
@records = []
for i in 1 .. num_records
ptr = 0x4e + (i - 1) * 8
print format("rexlist offset %d %x -> %x\n", i, ptr, ptr + 8)
rec_b = LocalID(s[ptr + 0, 4]) # 4e 4f 50 51
attr = UInt8 (s[ptr + 4, 1]) # 52
rid = UInt24 (s[ptr + 5, 3]) # 53
rec_e = if i == num_records then
s .length - 1
else
LocalID(s[ptr + 8, 4]) - 1
end
print format("record offset %x -> %x\n", rec_b, rec_e)
@records << @record_class .new(rid, attr, s[rec_b .. rec_e])
# print "#{rec_b} -> #{rec_e} (#{rec_e - rec_b + 1})\n"
# @records << s[rec_b .. rec_e]
end
################
## set each start and end point.
app_b, sor_b, rec_b, length = nil, nil, nil, s .length
gap_b = if length > 0x4e then 0x4e + num_records * 8 end
app_b = if appinfo_id != 0 then appinfo_id end
sor_b = if sortinfo_id != 0 then sortinfo_id end
rec_b = if num_records > 0 then LocalID(s[0x4e, 4]) end
gap_e = (app_b || sor_b || rec_b || out) - 1
app_e = ( sor_b || rec_b || out) - 1
sor_e = ( rec_b || out) - 1
@gap = s[gap_b .. gap_e] if gap_b && gap_b < gap_e
@appinfo = PalmAppInfo .new(s[app_b .. app_e]) if app_b
@sortinfo = PalmSortInfo .new(s[sor_b .. sor_e]) if sor_b
end
################################################################
## functions for dump
## whole DB is made up by
## header + rec_list + gap + appinfo + sortinfo + records
def dump_header
print "xxxxxxxxxxxxxxxxxxxxxxxxx\n"
p @ctime
p @mtime
p @btime
print "xxxxxxxxxxxxxxxxxxxxxxxxx\n"
if @sortinfo and @sortinfo .length > 0
sortinfooffset = (appinfooffset + @appinfo .length)
else
sortinfooffset = 0
end
return revAsciiz32(@name) +
revUInt16(@attr) +
revUInt16(@version) +
revPalmDate(@ctime) +
revPalmDate(@mtime) +
revPalmDate(@btime) +
revUInt32(@modnum) +
revLocalID(appinfooffset =
(0x4e + (@records .length * 8) + @gap .length)) +
revLocalID(sortinfooffset) +
revAscii4(@type) +
revAscii4(@creator) +
revUInt32(@idseed) +
revUInt32(nextRecordListID = 0) +
revUInt16(@records .length)
end
def dump_reclist
ret = ''
offset = 0x4e + (@records .length * 8) +
@gap .length +
@appinfo .length +
(@sortinfo || '') .length
@records .each{|r|
print format("offset of reclist = %x\n", offset) if $DEBUG
ret += revLocalID(offset) + revUInt8(r .attr) + revUInt24(r .rid)
offset += r .length
}
return ret
end
def dump_gap
return @gap
end
def dump_appinfo
return @appinfo .dump
end
def dump_sortinfo
if @sortinfo
return @sortinfo .dump
else
return ''
end
end
def dump_records
ret = ''
@records .each{|r| ret += r .dump}
return ret
end
def dump
return dump_header + dump_reclist + dump_gap + dump_appinfo +
dump_sortinfo + dump_records
end
################################################################
## manupulate records in DB.
def record_by_index(i)
return @records[i]
end
def record_by_id(id)
return @records[id_to_index[id]]
end
def each_record
i = 0
while (rec = record_by_index(i)) != nil
yield rec
i += 1
end
end
def delete_by_id(id)
@records .delete_at(id_to_index(id))
return self
end
def delete_all
@records .clear
return self
end
## Delete all records which are marked as archived or deleted.
def cleanup_record
@records .each{|r|
if r .archived? or r .deleted?
@records .delete_by_id(r .id)
end
}
return self
end
## Reset all dirty flags. set the last sync time to now.
def reset_sync_flags
## xxx
return self
end
private
def id_to_index(id)
@records .each_index{|i| return i if @records[i] .id == id}
return nil
end
end
pdb = PalmPdb.new(ARGV[0])
print Kconv::toeuc("Name: ``#{pdb .name}''\n")
print "Version: #{pdb .version}\n"
print "Ctime: #{pdb .ctime}\n"
print "Mtime: #{pdb .mtime}\n"
print "Btime: #{pdb .btime}\n"
print "Modnum: #{pdb .modnum}\n"
print "Type: #{pdb .type}\n"
print "Creator: #{pdb .creator}\n"
print "IDseed: #{pdb .idseed}\n"
print "NumRecords: #{pdb .num_records}(info) / #{pdb .records .length}(net)\n"
print "RecordsLen: #{pdb .records .to_s .length}\n"
print "AppinfoLen: #{pdb .appinfo .to_s .length}\n"
print "SortinfoLen: #{pdb .sortinfo .to_s .length}\n"
print "GapLength: #{pdb .gap .length}\n"
print "FileLength: #{pdb .file_length}\n"
print "DBLength: #{pdb .length}\n"
pdb .records .each_with_index{|r,i|
print "****************************************************************\n"
print "Record #{i}: length: #{r .length} ID: #{r .rid}\n"
print "Attr: #{r .attr_as_string}\n"
print "Category: #{r .category}\n"
print r .data .gsub("\0", "\n"), "\n"
print "****************************************************************\n\n"
}
x = pdb .dump
file = File .open(ARGV[0])
s = file .gets(nil)
print "S = #{s .length}\n"
print "X = #{x .length}\n"
copy_file = File .open(ARGV[0] + '.bak', "w")
copy_file .print x
copy_file .close
require 'kconv'
################################################################
## modules
module PalmPacker
def revUIntN(i, n)
bytes, ret = n / 8, []
if i .kind_of?(Integer) and 0 <= i and i <= 256 ** bytes - 1
for c in 0 .. bytes - 1
ret[bytes - 1 - c] = (i / (256 ** c)) % 256
end
return ret .pack('C' * bytes)
end
raise "Wrong value #{i .inspect} for revUInt#{n}."
end
def revUInt32(i); return revUIntN(i, 32) ; end
def revUInt24(i); return revUIntN(i, 24) ; end
def revUInt16(i); return revUIntN(i, 16) ; end
def revUInt8(i) ; return revUIntN(i, 8) ; end
def revPalmDate(d)
if d .kind_of?(Time)
return revUInt32(d .to_i + 2082844800)
else d .kind_of?(Integer)
return revUInt32(d)
end
raise format("Wrong value #{d .inspect} for PalmDate.")
end
def revAscii4(s)
if s .kind_of?(String) and s .length == 4
return s
end
raise format("Wrong value '#{s}' for Ascii4.")
end
def revAsciiz16(s)
if s .kind_of?(String) and s .length <= 15
return (s + "\0" * 16)[0, 16]
end
raise format("Wrong value '#{s}' for Asciiz16.")
end
def revAsciiz32(s)
if s .kind_of?(String) and s .length <= 31
return (s + "\0" * 32)[0, 32]
end
raise format("Wrong value '#{s}' for Asciiz32.")
end
alias revLocalID revUInt32
end
module PalmUnpacker
def UIntN(s, n)
bytes, ret = n / 8, 0
s .unpack('C' * bytes) .each_with_index{|c, i|
ret += c * (256 ** (bytes - 1 - i))
}
return ret
end
def UInt32(s); return UIntN(s, 32) ; end
def UInt24(s); return UIntN(s, 24) ; end
def UInt16(s); return UIntN(s, 16) ; end
def UInt8(s) ; return UIntN(s, 8) ; end
def PalmDate(s)
sec_from_epoc = UInt32(s) - 2082844800
if sec_from_epoc >= 0
return Time .at(sec_from_epoc)
else
return UInt32(s)
end
end
def Ascii4(s)
return s[0,4]
end
def AsciizN(s, n)
return s[0, n] .sub(/\0+$/, '')
end
def Asciiz32(s)
AsciizN(s, 32)
end
def Asciiz16(s)
AsciizN(s, 16)
end
alias LocalID UInt32
end