Class: Palm::PDB

Inherits:
Object
  • Object
show all
Defined in:
lib/palm/pdb.rb

Overview

PDB handles reading and writing raw Palm PDB records and resources. For most cases, users will probably want to extend this class class, overriding pack_entry and unpack_entry to support their record types.

Records are simply stored as an array in data, so polish up on your enumerable tricks. The created_at, modified_at, and backed_up_at attributes are all stored as Times. Note that modified_at is not automatically updated.

Direct Known Subclasses

WabaDB

Constant Summary collapse

HEADER_LENGTH =

Size of database header

32+2+2+(9*4)
RECORD_INDEX_HEADER_LEN =

Size of record index header

6
INDEX_RECORD_LENGTH =

Length of record index entry

8
INDEX_RESOURCE_LENGTH =

Length of resource index entry

10
ATTRIBUTE_CODES =
{
  "resource" =>      0x0001,
  "read-only" =>     0x0002,
  "AppInfo dirty" => 0x0004,
  "backup" =>        0x0008,
  "OK newer" =>      0x0010,
  "reset"   =>          0x0020,
  "launchable" =>    0x0200,
  "open" =>          0x8000,
  
  # PalmOS 5.0 attribute names

  "ResDB" =>           0x0001,
  "ReadOnly" =>          0x0002,
  "AppInfoDirty" =>      0x0004,
  "Backup" =>          0x0008,
  "OKToInstallNewer" =>  0x0010,
  "ResetAfterInstall"=> 0x0020,
  "LaunchableData" =>  0x0200,
  "Recyclable" =>      0x0400,
  "Bundle" =>          0x0800,
  "Open" =>            0x8000,
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(from = nil) ⇒ PDB

Creates a new PDB. If from is passed a String, a file will be loaded from that path (see load_file). If a IO object is passed in, then it will be used to load the palm data (see load).



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/palm/pdb.rb', line 68

def initialize(from = nil)
  @attributes = {}
  @data = []
  @appinfo_block = nil
  @sort_block = nil
  @backed_up_at = @created_at = @modified_at = Time.now
  
  case from
  when NilClass
    now = Time.now
    @created_at   = now
    @modified_at  = now
    @version  = 0
    @modnum   = 0
    @type   = "\0\0\0\0"
    @creator  = "\0\0\0\0"
    @unique_id_seed = 0
  when String
    load(open(from))
  when IO
    load(from)
  else
    raise ArgumentError.new("Unknown value to load from #{from.inspect}.  Use a String or IO object.")
  end
end

Instance Attribute Details

#attributesObject

Returns the value of attribute attributes.



31
32
33
# File 'lib/palm/pdb.rb', line 31

def attributes
  @attributes
end

#backed_up_atObject

Returns the value of attribute backed_up_at.



32
33
34
# File 'lib/palm/pdb.rb', line 32

def backed_up_at
  @backed_up_at
end

#created_atObject

Returns the value of attribute created_at.



32
33
34
# File 'lib/palm/pdb.rb', line 32

def created_at
  @created_at
end

#creatorObject

Returns the value of attribute creator.



33
34
35
# File 'lib/palm/pdb.rb', line 33

def creator
  @creator
end

#dataObject

Returns the value of attribute data.



35
36
37
# File 'lib/palm/pdb.rb', line 35

def data
  @data
end

#modified_atObject

Returns the value of attribute modified_at.



32
33
34
# File 'lib/palm/pdb.rb', line 32

def modified_at
  @modified_at
end

#modnumObject

Returns the value of attribute modnum.



33
34
35
# File 'lib/palm/pdb.rb', line 33

def modnum
  @modnum
end

#nameObject

Returns the value of attribute name.



31
32
33
# File 'lib/palm/pdb.rb', line 31

def name
  @name
end

#typeObject

Returns the value of attribute type.



33
34
35
# File 'lib/palm/pdb.rb', line 33

def type
  @type
end

#unique_id_seedObject

Returns the value of attribute unique_id_seed.



34
35
36
# File 'lib/palm/pdb.rb', line 34

def unique_id_seed
  @unique_id_seed
end

#versionObject

Returns the value of attribute version.



31
32
33
# File 'lib/palm/pdb.rb', line 31

def version
  @version
end

Instance Method Details

#load(io) ⇒ Object

Loads the PDB from the given IO source.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/palm/pdb.rb', line 107

def load(io)
  # Set to binary mode for windows environment

  io.binmode if io.respond_to? :binmode
  
  start_postion = io.pos
  io.seek(0, IO::SEEK_END)
  io_size = io.pos
  io.seek(start_postion)
  
  appinfo_offset, sort_offset = unpack_header(io.read(HEADER_LENGTH))
  
  # parse the record index

  record_index = io.read(RECORD_INDEX_HEADER_LEN)
  next_index, record_count = record_index.unpack("N n")
  
  # load the indexes, gather information about offsets and

  # record lengths

  indexes = nil
  if resource?
    indexes = load_resource_index(io, next_index, record_count)
  else
    indexes = load_record_index(io, next_index, record_count)
  end
  # Add the final offset as a Datablock for the end of the file

  indexes << DataBlock.new(io_size, 0)
  # Fill in the lengths for each of these index entries 

  indexes.each_cons(2){|starts, ends| starts.record_length = ends.offset - starts.offset }
  # Calculate where the data starts (or end of file if empty)

  data_offset = indexes.first.offset
  
  # Pop the last entry back off.  We pushed it on make it easier to calculate the lengths

  # of each entry.

  indexes.pop
  
  # Load optional chunks

  load_appinfo_block(io, appinfo_offset, sort_offset, data_offset) if appinfo_offset > 0
  load_sort_block(io, sort_offset, data_offset) if sort_offset > 0
  
  # Load data

  load_data(io, indexes)
  io.close
end

#load_file(path) ⇒ Object

Loads the PDB from a file path



100
101
102
103
104
# File 'lib/palm/pdb.rb', line 100

def load_file(path)
  open path, "r" do |io|
    load io
  end
end

#resource?Boolean

Returns true if the PDB is a set of resources, false if it is a set of records

Returns:

  • (Boolean)


95
96
97
# File 'lib/palm/pdb.rb', line 95

def resource?
  @attributes['resource'] || @attributes['ResDB']
end

#write(io) ⇒ Object

Writes PDB to an IO object



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/palm/pdb.rb', line 270

def write(io)   
  io.binmode if io.respond_to? :binmode
  
  # Track the current offset for each section

  offset_position = HEADER_LENGTH + 2 #(2: Index Header length)

  
  index_length = RECORD_INDEX_HEADER_LEN + 
    @data.length * (resource? ? INDEX_RESOURCE_LENGTH : INDEX_RECORD_LENGTH )
  
  offset_position += index_length  # Advance for the index

  
  packed_entries = @data.map{|e| pack_entry(e)}
  
  packed_app_info = pack_app_info_block()
  packed_sort =    pack_sort_block()
  
  # Calculate AppInfo block offset

  app_info_offset = 0
  if packed_app_info and !packed_app_info.empty?
    app_info_offset = offset_position
    offset_position += packed_app_info.length # Advance for the app_info_block

  end
  
  # Calculate sort block offset

  sort_offset = 0
  if packed_sort and !packed_sort.empty?
    sort_offset = offset_position
    offset_position += packed_sort.length # Advance for the sort_block

  end
  
  packed_header = pack_header(app_info_offset, sort_offset)
  
  index_header = [0, @data.length ].pack "N n"

  packed_index = @data.zip(packed_entries).map do |entry, packed|
    index = nil
    if resource?
      index = [entry.record_type, entry.record_id, offset_position].pack "a4 n N"
    else
      index = [
        offset_position, entry.packed_attributes,
        (entry.record_id >> 16) & 0xff,
        (entry.record_id >> 8) & 0xff,
        entry.record_id & 0xff
      ].pack "N C C3"
    end
    offset_position += packed.length
    index
  end
  
  # Write to IO stream

  io << packed_header
  io << index_header
  io << packed_index.join
  io << "\0\0" # 2 null byte separator

  io << @app_info_block unless app_info_offset == 0
  io << @sort_block unless sort_offset == 0
  io << packed_entries.join
end

#write_file(path) ⇒ Object

Writes to the given path



263
264
265
266
267
# File 'lib/palm/pdb.rb', line 263

def write_file(path)
  open(path, "w") do |io| 
    write io
  end
end