-
-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathpdb.ksy
935 lines (907 loc) · 29.6 KB
/
pdb.ksy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
meta:
id: pdb_file
title: DeviceSQL database export (probably generated by rekordbox)
application: rekordbox
file-extension:
- pdb
license: EPL-1.0
endian: le
doc: |
This is a relational database format designed to be efficiently used
by very low power devices (there were deployments on 16 bit devices
with 32K of RAM). Today you are most likely to encounter it within
the Pioneer Professional DJ ecosystem, because it is the format that
their rekordbox software uses to write USB and SD media which can be
mounted in DJ controllers and used to play and mix music.
It has been reverse-engineered to facilitate sophisticated
integrations with light and laser shows, videos, and other musical
instruments, by supporting deep knowledge of what is playing and
what is coming next through monitoring the network communications of
the players.
The file is divided into fixed-size blocks. The first block has a
header that establishes the block size, and lists the tables
available in the database, identifying their types and the index of
the first of the series of linked pages that make up that table.
Each table is made up of a series of rows which may be spread across
any number of pages. The pages start with a header describing the
page and linking to the next page. The rest of the page is used as a
heap: rows are scattered around it, and located using an index
structure that builds backwards from the end of the page. Each row
of a given type has a fixed size structure which links to any
variable-sized strings by their offsets within the page.
As changes are made to the table, some records may become unused,
and there may be gaps within the heap that are too small to be used
by other data. There is a bit map in the row index that identifies
which rows are actually present. Rows that are not present must be
ignored: they do not contain valid (or even necessarily well-formed)
data.
The majority of the work in reverse-engineering this format was
performed by @henrybetts and @flesniak, for which I am hugely
grateful. @GreyCat helped me learn the intricacies (and best
practices) of Kaitai far faster than I would have managed on my own.
doc-ref: https://github.com/Deep-Symmetry/dysentery/blob/master/doc/Analysis.pdf
seq:
- contents: [0, 0, 0, 0]
- id: len_page
type: u4
doc: |
The database page size, in bytes. Pages are referred to by
index, so this size is needed to calculate their offset, and
table pages have a row index structure which is built from the
end of the page backwards, so finding that also requires this
value.
- id: num_tables
type: u4
doc: |
Determines the number of table entries that are present. Each
table is a linked list of pages containing rows of a particular
type.
- id: next_unused_page
type: u4
doc: |
@flesinak said: "Not used as any `empty_candidate`, points
past the end of the file."
- type: u4
- id: sequence
type: u4
doc: |
@flesniak said: "Always incremented by at least one,
sometimes by two or three."
- contents: [0, 0, 0, 0]
- id: tables
type: table
repeat: expr
repeat-expr: num_tables
doc: |
Describes and links to the tables present in the database.
types:
table:
doc: |
Each table is a linked list of pages containing rows of a single
type. This header describes the nature of the table and links to
its pages by index.
seq:
- id: type
type: u4
enum: page_type
doc: |
Identifies the kind of rows that are found in this table.
- id: empty_candidate
type: u4
- id: first_page
type: page_ref
doc: |
Links to the chain of pages making up that table. The first
page seems to always contain similar garbage patterns and
zero rows, but the next page it links to contains the start
of the meaningful data rows.
- id: last_page
type: page_ref
doc: |
Holds the index of the last page that makes up this table.
When following the linked list of pages of the table, you
either need to stop when you reach this page, or when you
notice that the `next_page` link you followed took you to a
page of a different `type`.
-webide-representation: '{type}'
page_ref:
doc: |
An index which points to a table page (its offset can be found
by multiplying the index by the `page_len` value in the file
header). This type allows the linked page to be lazy loaded.
seq:
- id: index
type: u4
doc: |
Identifies the desired page number.
instances:
body:
doc: |
When referenced, loads the specified page and parses its
contents appropriately for the type of data it contains.
io: _root._io
pos: _root.len_page * index
size: _root.len_page
type: page
page:
doc: |
A table page, consisting of a short header describing the
content of the page and linking to the next page, followed by a
heap in which row data is found. At the end of the page there is
an index which locates all rows present in the heap via their
offsets past the end of the page header.
seq:
- contents: [0, 0, 0, 0]
- id: page_index
doc: Matches the index we used to look up the page, sanity check?
type: u4
- id: type
type: u4
enum: page_type
doc: |
Identifies the type of information stored in the rows of this page.
- id: next_page
doc: |
Index of the next page containing this type of rows. Points past
the end of the file if there are no more.
type: page_ref
- type: u4
doc: |
@flesniak said: "sequence number (0->1: 8->13, 1->2: 22, 2->3: 27)"
- size: 4
- id: num_rows_small
type: u1
doc: |
Holds the value used for `num_rows` (see below) unless
`num_rows_large` is larger (but not equal to `0x1fff`). This
seems like some strange mechanism to deal with the fact that
lots of tiny entries, such as are found in the
`playlist_entries` table, are too big to count with a single
byte. But why not just always use `num_rows_large`, then?
- type: u1
doc: |
@flesniak said: "a bitmask (1st track: 32)"
- type: u2
doc: |
@flesniak said: "25600 for strange blocks"
- id: free_size
type: u2
doc: |
Unused space (in bytes) in the page heap, excluding the row
index at end of page.
- id: used_size
type: u2
doc: |
The number of bytes that are in use in the page heap.
- type: u2
doc: |
@flesniak said: "(0->1: 2)"
- id: num_rows_large
type: u2
doc: |
Holds the value used for `num_rows` (see below) when that is
too large to fit into `num_rows_small`, and that situation
seems to be indicated when this value is larger than
`num_rows_small`, but not equal to `0x1fff`. This seems like
some strange mechanism to deal with the fact that lots of
tiny entries, such as are found in the `playlist_entries`
table, are too big to count with a single byte. But why not
just always use this value, then?
- type: u2
doc: |
@flesniak said: "1004 for strange blocks, 0 otherwise"
- type: u2
doc: |
@flesniak said: "always 0 except 1 for history pages, num
entries for strange pages?"
instances:
num_rows:
value: |
(num_rows_large > num_rows_small) and (num_rows_large != 0x1fff) ? num_rows_large : num_rows_small
doc: |
The number of rows on this page (controls the number of row
index entries there are, but some of those may not be marked
as present in the table due to deletion).
-webide-parse-mode: eager
num_groups:
value: '(num_rows - 1) / 16 + 1'
doc: |
The number of row groups that are present in the index. Each
group can hold up to sixteen rows. All but the final one
will hold sixteen rows.
row_groups:
type: 'row_group(_index)'
repeat: expr
repeat-expr: num_groups
doc: |
The actual row groups making up the row index. Each group
can hold up to sixteen rows.
row_group:
doc: |
A group of row indices, which are built backwards from the end
of the page. Holds up to sixteen row offsets, along with a bit
mask that indicates whether each row is actually present in the
table.
params:
- id: group_index
type: u2
doc: |
Identifies which group is being generated. They build backwards
from the end of the page.
instances:
base:
value: '_root.len_page - (group_index * 0x24)'
doc: |
The starting point of this group of row indices.
row_present_flags:
pos: base - 4
type: u2
doc: |
Each bit specifies whether a particular row is present. The
low order bit corresponds to the first row in this index,
whose offset immediately precedes these flag bits. The
second bit corresponds to the row whose offset precedes
that, and so on.
-webide-parse-mode: eager
rows:
type: row_ref(_index)
repeat: expr
repeat-expr: '(group_index < (_parent.num_groups - 1)) ? 16 : ((_parent.num_rows - 1) % 16 + 1)'
doc: |
The row offsets in this group.
row_ref:
doc: |
An offset which points to a row in the table, whose actual
presence is controlled by one of the bits in
`row_present_flags`. This instance allows the row itself to be
lazily loaded, unless it is not present, in which case there is
no content to be loaded.
params:
- id: row_index
type: u2
doc: |
Identifies which row within the row index this reference
came from, so the correct flag can be checked for the row
presence and the correct row offset can be found.
instances:
ofs_row:
pos: '_parent.base - (6 + (2 * row_index))'
type: u2
doc: |
The offset of the start of the row (in bytes past the end of
the page header).
present:
value: '(((_parent.row_present_flags >> row_index) & 1) != 0 ? true : false)'
doc: |
Indicates whether the row index considers this row to be
present in the table. Will be `false` if the row has been
deleted.
-webide-parse-mode: eager
body:
pos: ofs_row + 0x28
type:
switch-on: _parent._parent.type
cases:
'page_type::albums': album_row
'page_type::artists': artist_row
'page_type::artwork': artwork_row
'page_type::colors': color_row
'page_type::genres': genre_row
'page_type::keys': key_row
'page_type::labels': label_row
'page_type::playlist_tree': playlist_tree_row
'page_type::playlist_entries': playlist_entry_row
'page_type::tracks': track_row
if: present
doc: |
The actual content of the row, as long as it is present.
-webide-parse-mode: eager
-webide-representation: '{body.name.body.text}{body.title.body.text} ({body.id})'
album_row:
doc: |
A row that holds an album name and ID.
seq:
- id: magic
contents: [0x80, 0x00]
- id: index_shift
type: u2
doc: TODO name from @flesniak, but what does it mean?
- type: u4
- id: artist_id
type: u4
doc: |
Identifies the artist associated with the album.
- id: id
type: u4
doc: |
The unique identifier by which this album can be requested
and linked from other rows (such as tracks).
- type: u4
- type: u1
doc: |
@flesniak says: "alwayx 0x03, maybe an unindexed empty string"
- id: ofs_name
type: u1
doc: |
The location of the variable-length name string, relative to
the start of this row.
instances:
name:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_name
doc: |
The name of this album.
-webide-parse-mode: eager
artist_row:
doc: |
A row that holds an artist name and ID.
seq:
- id: magic
contents: [0x60, 0x00]
- id: index_shift
type: u2
doc: TODO name from @flesniak, but what does it mean?
- id: id
type: u4
doc: |
The unique identifier by which this artist can be requested
and linked from other rows (such as tracks).
- type: u1
doc: |
@flesniak says: "alwayx 0x03, maybe an unindexed empty string"
- id: ofs_name
type: u1
doc: |
The location of the variable-length name string, relative to
the start of this row.
instances:
name:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_name
doc: |
The name of this artist.
-webide-parse-mode: eager
artwork_row:
doc: |
A row that holds the path to an album art image file and the
associated artwork ID.
seq:
- id: id
type: u4
doc: |
The unique identifier by which this art can be requested
and linked from other rows (such as tracks).
- id: path
type: device_sql_string
doc: |
The variable-length file path string at which the art file
can be found.
-webide-representation: '{path.body.text}'
color_row:
doc: |
A row that holds a color name and the associated ID.
seq:
- size: 5
- id: id
type: u2
doc: |
The unique identifier by which this color can be requested
and linked from other rows (such as tracks).
- type: u1
- id: name
type: device_sql_string
doc: |
The variable-length string naming the color.
genre_row:
doc: |
A row that holds a genre name and the associated ID.
seq:
- id: id
type: u4
doc: |
The unique identifier by which this genre can be requested
and linked from other rows (such as tracks).
- id: name
type: device_sql_string
doc: |
The variable-length string naming the genre.
key_row:
doc: |
A row that holds a musical key and the associated ID.
seq:
- id: id
type: u4
doc: |
The unique identifier by which this key can be requested
and linked from other rows (such as tracks).
- id: id2
type: u4
doc: |
Seems to be a second copy of the ID?
- id: name
type: device_sql_string
doc: |
The variable-length string naming the key.
label_row:
doc: |
A row that holds a label name and the associated ID.
seq:
- id: id
type: u4
doc: |
The unique identifier by which this label can be requested
and linked from other rows (such as tracks).
- id: name
type: device_sql_string
doc: |
The variable-length string naming the label.
playlist_tree_row:
doc: |
A row that holds a playlist name, ID, indication of whether it
is an ordinary playlist or a folder of other playlists, a link
to its parent folder, and its sort order.
seq:
- id: parent_id
type: u4
doc: |
The ID of the `playlist_tree_row` in which this one can be
found, or `0` if this playlist exists at the root level.
- size: 4
- id: sort_order
type: u4
doc: |
The order in which the entries of this playlist are sorted.
- id: id
type: u4
doc: |
The unique identifier by which this playlist can be requested
and linked from other rows (such as tracks).
- id: raw_is_folder
type: u4
doc: |
Has a non-zero value if this is actually a folder rather
than a playlist.
- id: name
type: device_sql_string
doc: |
The variable-length string naming the playlist.
instances:
is_folder:
value: raw_is_folder != 0
-webide-parse-mode: eager
playlist_entry_row:
doc: |
A row that associates a track with a position in a playlist.
seq:
- id: entry_index
type: u4
doc: |
The position within the playlist represented by this entry.
- id: track_id
type: u4
doc: |
The track found at this position in the playlist.
- id: playlist_id
type: u4
doc: |
The playlist to which this entry belongs.
track_row:
doc: |
A row that describes a track that can be played, with many
details about the music, and links to other tables like artists,
albums, keys, etc.
seq:
- id: magic
contents: [0x24, 0x00]
- id: index_shift
type: u2
doc: TODO name from @flesniak, but what does it mean?
- id: bitmask
type: u4
doc: TODO what do the bits mean?
- id: sample_rate
type: u4
doc: |
Playback sample rate of the audio file.
- id: composer_id
type: u4
doc: |
References a row in the artist table if the composer is
known.
- id: file_size
type: u4
doc: |
The length of the audio file, in bytes.
- type: u4
doc: |
Some ID? Purpose as yet unknown.
- type: u2
doc: |
From @flesniak: "always 19048?"
- type: u2
doc: |
From @flesniak: "always 30967?"
- id: artwork_id
type: u4
doc: |
References a row in the artwork table if there is album art.
- id: key_id
type: u4
doc: |
References a row in the keys table if the track has a known
main musical key.
- id: original_artist_id
type: u4
doc: |
References a row in the artwork table if this is a cover
performance and the original artist is known.
- id: label_id
type: u4
doc: |
References a row in the labels table if the track has a
known record label.
- id: remixer_id
type: u4
doc: |
References a row in the artists table if the track has a
known remixer.
- id: bitrate
type: u4
doc: |
Playback bit rate of the audio file.
- id: track_number
type: u4
doc: |
The position of the track within an album.
- id: tempo
type: u4
doc: |
The tempo at the start of the track in beats per minute,
multiplied by 100.
- id: genre_id
type: u4
doc: |
References a row in the genres table if the track has a
known musical genre.
- id: album_id
type: u4
doc: |
References a row in the albums table if the track has a
known album.
- id: artist_id
type: u4
doc: |
References a row in the artists table if the track has a
known performer.
- id: id
type: u4
doc: |
The id by which this track can be looked up; players will
report this value in their status packets when they are
playing the track.
- id: disc_number
type: u2
doc: |
The number of the disc on which this track is found, if it
is known to be part of a multi-disc album.
- id: play_count
type: u2
doc: |
The number of times this track has been played.
- id: year
type: u2
doc: |
The year in which this track was released.
- id: sample_depth
type: u2
doc: |
The number of bits per sample of the audio file.
- id: duration
type: u2
doc: |
The length, in seconds, of the track when played at normal
speed.
- type: u2
doc: |
From @flesniak: "always 41?"
- id: color_id
type: u1
doc: |
References a row in the colors table if the track has been
assigned a color.
- id: rating
type: u1
doc: |
The number of stars to display for the track, 0 to 5.
- type: u2
doc: |
From @flesniak: "always 1?"
- type: u2
doc: |
From @flesniak: "alternating 2 or 3"
- id: ofs_strings
type: u2
repeat: expr
repeat-expr: 21
doc: |
The location, relative to the start of this row, of a
variety of variable-length strings.
instances:
unknown_string_1:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[0]
doc: |
A string of unknown purpose, which has so far only been
empty.
-webide-parse-mode: eager
texter:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[1]
doc: |
A string of unknown purpose, which @flesnik named.
-webide-parse-mode: eager
unknown_string_2:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[2]
doc: |
A string of unknown purpose; @flesniak said "thought
tracknumber -> wrong!"
unknown_string_3:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[3]
doc: |
A string of unknown purpose; @flesniak said "strange
strings, often zero length, sometimes low binary values
0x01/0x02 as content"
unknown_string_4:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[4]
doc: |
A string of unknown purpose; @flesniak said "strange
strings, often zero length, sometimes low binary values
0x01/0x02 as content"
-webide-parse-mode: eager
message:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[5]
doc: |
A string of unknown purpose, which @flesnik named.
-webide-parse-mode: eager
unknown_switch:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[6]
doc: |
A string of unknown purpose, whose value is always either
empty or "ON".
-webide-parse-mode: eager
autoload_hotcues:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[7]
doc: |
A string whose value is always either empty or "ON", and
which apparently for some insane reason is used, rather than
a single bit somewhere, to control whether hot-cues are
auto-loaded for the track.
-webide-parse-mode: eager
unknown_string_5:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[8]
doc: |
A string of unknown purpose.
-webide-parse-mode: eager
unknown_string_6:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[9]
doc: |
A string of unknown purpose, usually empty.
-webide-parse-mode: eager
date_added:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[10]
doc: |
A string containing the date this track was added to the collection.
-webide-parse-mode: eager
release_date:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[11]
doc: |
A string containing the date this track was released, if known.
-webide-parse-mode: eager
mix_name:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[12]
doc: |
A string naming the remix of the track, if known.
-webide-parse-mode: eager
unknown_string_7:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[13]
doc: |
A string of unknown purpose, usually empty.
-webide-parse-mode: eager
analyze_path:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[14]
doc: |
The file path of the track analysis, which allows rapid
seeking to particular times in variable bit-rate files,
jumping to particular beats, visual waveform previews, and
stores cue points and loops.
-webide-parse-mode: eager
analyze_date:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[15]
doc: |
A string containing the date this track was analyzed by rekordbox.
-webide-parse-mode: eager
comment:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[16]
doc: |
The comment assigned to the track by the DJ, if any.
-webide-parse-mode: eager
title:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[17]
doc: |
The title of the track.
-webide-parse-mode: eager
unknown_string_8:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[18]
doc: |
A string of unknown purpose, usually empty.
-webide-parse-mode: eager
filename:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[19]
doc: |
The file name of the track audio file.
-webide-parse-mode: eager
file_path:
type: device_sql_string
pos: _parent.ofs_row + 0x28 + ofs_strings[20]
doc: |
The file path of the track audio file.
-webide-parse-mode: eager
device_sql_string:
doc: |
A variable length string which can be stored in a variety of
different encodings.
seq:
- id: length_and_kind
type: u1
doc: |
Mangled length of an ordinary ASCII string if odd, or a flag
indicating another encoding with a longer length value to
follow.
- id: body
type:
switch-on: length_and_kind
cases:
0x40: device_sql_long_ascii
0x90: device_sql_long_utf16be
_: device_sql_short_ascii(length_and_kind)
-webide-parse-mode: eager
-webide-representation: '{body.text}'
device_sql_short_ascii:
doc: |
An ASCII-encoded string up to 127 bytes long.
params:
- id: mangled_length
type: u1
doc: |
Contains the actual length, incremented, doubled, and
incremented again. Go figure.
seq:
- id: text
type: str
size: length
encoding: ascii
doc: |
The content of the string.
instances:
length:
value: '((mangled_length - 1) / 2) - 1'
doc: |
The un-mangled length of the string, in bytes.
-webide-parse-mode: eager
device_sql_long_ascii:
doc: |
An ASCII-encoded string preceded by a two-byte length field.
TODO May need to skip a byte after the length!
Have not found any test data.
seq:
- id: length
type: u2
doc: |
Contains the length of the string in bytes.
- id: text
type: str
size: length
encoding: ascii
doc: |
The content of the string.
device_sql_long_utf16be:
doc: |
A UTF-16BE-encoded string preceded by a two-byte length field.
seq:
- id: length
type: u2
doc: |
Contains the length of the string in bytes, including two trailing nulls.
- id: text
type: str
size: length - 4
encoding: utf-16be
doc: |
The content of the string.
enums:
page_type:
0:
id: tracks
doc: |
Holds rows describing tracks, such as their title, artist,
genre, artwork ID, playing time, etc.
1:
id: genres
doc: |
Holds rows naming musical genres, for reference by tracks and searching.
2:
id: artists
doc: |
Holds rows naming artists, for reference by tracks and searching.
3:
id: albums
doc: |
Holds rows naming albums, for reference by tracks and searching.
4:
id: labels
doc: |
Holds rows naming music labels, for reference by tracks and searching.
5:
id: keys
doc: |
Holds rows naming musical keys, for reference by tracks and searching.
6:
id: colors
doc: |
Holds rows naming color labels, for reference by tracks and searching.
7:
id: playlist_tree
doc: |
Holds rows that describe the hierarchical tree structure of
available playlists and folders grouping them.
8:
id: playlist_entries
doc: |
Holds rows that enumerate the tracks found in playlists and
the playlists they belong to.
9:
id: unknown_9
10:
id: unknown_10
11:
id: unknown_11
12:
id: unknown_12
13:
id: artwork
doc: |
Holds rows pointing to album artwork images.
14:
id: unknown_14
15:
id: unknown_15
16:
id: columns
doc: |
TODO figure out and explain
17:
id: unknown_17
18:
id: unknown_18
19:
id: history
doc: |
Holds rows listing tracks played in performance sessions.