pyrekordbox 0.2.1__py3-none-any.whl → 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- docs/source/formats/anlz.md +178 -7
- docs/source/formats/db6.md +1 -1
- docs/source/index.md +2 -6
- docs/source/quickstart.md +68 -45
- docs/source/tutorial/index.md +1 -1
- pyrekordbox/__init__.py +1 -1
- pyrekordbox/_version.py +2 -2
- pyrekordbox/anlz/file.py +39 -0
- pyrekordbox/anlz/structs.py +3 -5
- pyrekordbox/config.py +71 -27
- pyrekordbox/db6/database.py +260 -33
- pyrekordbox/db6/registry.py +22 -0
- pyrekordbox/db6/tables.py +3 -4
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/METADATA +12 -11
- pyrekordbox-0.2.2.dist-info/RECORD +80 -0
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/top_level.txt +0 -2
- tests/test_config.py +175 -0
- tests/test_db6.py +78 -0
- build/lib/build/lib/docs/source/conf.py +0 -178
- build/lib/build/lib/pyrekordbox/__init__.py +0 -22
- build/lib/build/lib/pyrekordbox/__main__.py +0 -204
- build/lib/build/lib/pyrekordbox/_version.py +0 -16
- build/lib/build/lib/pyrekordbox/anlz/__init__.py +0 -127
- build/lib/build/lib/pyrekordbox/anlz/file.py +0 -186
- build/lib/build/lib/pyrekordbox/anlz/structs.py +0 -299
- build/lib/build/lib/pyrekordbox/anlz/tags.py +0 -508
- build/lib/build/lib/pyrekordbox/config.py +0 -596
- build/lib/build/lib/pyrekordbox/db6/__init__.py +0 -45
- build/lib/build/lib/pyrekordbox/db6/aux_files.py +0 -213
- build/lib/build/lib/pyrekordbox/db6/database.py +0 -1808
- build/lib/build/lib/pyrekordbox/db6/registry.py +0 -304
- build/lib/build/lib/pyrekordbox/db6/tables.py +0 -1618
- build/lib/build/lib/pyrekordbox/logger.py +0 -23
- build/lib/build/lib/pyrekordbox/mysettings/__init__.py +0 -32
- build/lib/build/lib/pyrekordbox/mysettings/file.py +0 -369
- build/lib/build/lib/pyrekordbox/mysettings/structs.py +0 -282
- build/lib/build/lib/pyrekordbox/utils.py +0 -162
- build/lib/build/lib/pyrekordbox/xml.py +0 -1294
- build/lib/build/lib/tests/__init__.py +0 -3
- build/lib/build/lib/tests/test_anlz.py +0 -206
- build/lib/build/lib/tests/test_db6.py +0 -1039
- build/lib/build/lib/tests/test_mysetting.py +0 -203
- build/lib/build/lib/tests/test_xml.py +0 -629
- build/lib/docs/source/conf.py +0 -178
- build/lib/pyrekordbox/__init__.py +0 -22
- build/lib/pyrekordbox/__main__.py +0 -204
- build/lib/pyrekordbox/_version.py +0 -16
- build/lib/pyrekordbox/anlz/__init__.py +0 -127
- build/lib/pyrekordbox/anlz/file.py +0 -186
- build/lib/pyrekordbox/anlz/structs.py +0 -299
- build/lib/pyrekordbox/anlz/tags.py +0 -508
- build/lib/pyrekordbox/config.py +0 -596
- build/lib/pyrekordbox/db6/__init__.py +0 -45
- build/lib/pyrekordbox/db6/aux_files.py +0 -213
- build/lib/pyrekordbox/db6/database.py +0 -1808
- build/lib/pyrekordbox/db6/registry.py +0 -304
- build/lib/pyrekordbox/db6/tables.py +0 -1618
- build/lib/pyrekordbox/logger.py +0 -23
- build/lib/pyrekordbox/mysettings/__init__.py +0 -32
- build/lib/pyrekordbox/mysettings/file.py +0 -369
- build/lib/pyrekordbox/mysettings/structs.py +0 -282
- build/lib/pyrekordbox/utils.py +0 -162
- build/lib/pyrekordbox/xml.py +0 -1294
- build/lib/tests/__init__.py +0 -3
- build/lib/tests/test_anlz.py +0 -206
- build/lib/tests/test_db6.py +0 -1039
- build/lib/tests/test_mysetting.py +0 -203
- build/lib/tests/test_xml.py +0 -629
- pyrekordbox-0.2.1.dist-info/RECORD +0 -129
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/LICENSE +0 -0
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/WHEEL +0 -0
@@ -1,127 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# Author: Dylan Jones
|
3
|
-
# Date: 2023-02-01
|
4
|
-
|
5
|
-
import re
|
6
|
-
from pathlib import Path
|
7
|
-
from typing import Union
|
8
|
-
from . import structs
|
9
|
-
from .file import AnlzFile
|
10
|
-
|
11
|
-
RE_ANLZ = re.compile("ANLZ[0-9]{4}.(DAT|EXT|2EX)")
|
12
|
-
|
13
|
-
|
14
|
-
def is_anlz_file(path: Union[str, Path]) -> bool:
|
15
|
-
"""Checks if the name of a file matches the ANLZ file name pattern.
|
16
|
-
|
17
|
-
Parameters
|
18
|
-
----------
|
19
|
-
path : str or Path
|
20
|
-
The file path to check
|
21
|
-
|
22
|
-
Returns
|
23
|
-
-------
|
24
|
-
is_anlz : bool
|
25
|
-
True if the file is an ANLZ file.
|
26
|
-
|
27
|
-
Examples
|
28
|
-
--------
|
29
|
-
>>> is_anlz_file("ANLZ0000.DAT")
|
30
|
-
True
|
31
|
-
|
32
|
-
>>> is_anlz_file("/path/to/ANLZ0000.DAT")
|
33
|
-
False
|
34
|
-
|
35
|
-
>>> is_anlz_file("ANLZ.DAT")
|
36
|
-
False
|
37
|
-
"""
|
38
|
-
path = Path(path)
|
39
|
-
if not path.exists() or not path.is_file():
|
40
|
-
return False
|
41
|
-
return bool(RE_ANLZ.match(path.name))
|
42
|
-
|
43
|
-
|
44
|
-
def get_anlz_paths(root: Union[str, Path]) -> dict:
|
45
|
-
"""Returns the paths of all existing ANLZ files in a directory.
|
46
|
-
|
47
|
-
Parameters
|
48
|
-
----------
|
49
|
-
root : str or Path
|
50
|
-
The path of the directory containing ANLZ files.
|
51
|
-
|
52
|
-
Returns
|
53
|
-
-------
|
54
|
-
anlz_paths : dict[str, Path]
|
55
|
-
The file paths stored as dictionary with the type of ANLZ file as keys
|
56
|
-
("DAT", "EXT", "EX2")
|
57
|
-
|
58
|
-
Examples
|
59
|
-
--------
|
60
|
-
>>> p = get_anlz_paths("directory/")
|
61
|
-
>>> p["DAT"]
|
62
|
-
directory/ANLZ0000.DAT
|
63
|
-
"""
|
64
|
-
paths = {"DAT": None, "EXT": None, "2EX": None}
|
65
|
-
for path in Path(root).iterdir():
|
66
|
-
if RE_ANLZ.match(path.name):
|
67
|
-
paths[path.suffix[1:].upper()] = path
|
68
|
-
return paths
|
69
|
-
|
70
|
-
|
71
|
-
def walk_anlz_dirs(root_dir: Union[str, Path]):
|
72
|
-
"""Finds all ANLZ directory paths recursively.
|
73
|
-
|
74
|
-
Parameters
|
75
|
-
----------
|
76
|
-
root_dir : str or Path
|
77
|
-
The path of the root directory.
|
78
|
-
|
79
|
-
Yields
|
80
|
-
-------
|
81
|
-
anlz_dir : str
|
82
|
-
The path of a directory containing ANLZ files
|
83
|
-
"""
|
84
|
-
for path in Path(root_dir).rglob("*"):
|
85
|
-
if path.is_dir():
|
86
|
-
if any(is_anlz_file(f) for f in path.iterdir()):
|
87
|
-
yield path
|
88
|
-
|
89
|
-
|
90
|
-
def walk_anlz_paths(root_dir: Union[str, Path]):
|
91
|
-
"""Finds all ANLZ directory paths and the containing file paths recursively.
|
92
|
-
|
93
|
-
Parameters
|
94
|
-
----------
|
95
|
-
root_dir : str or Path
|
96
|
-
The path of the root directory.
|
97
|
-
|
98
|
-
Yields
|
99
|
-
-------
|
100
|
-
anlz_dir : str
|
101
|
-
The path of a directory containing ANLZ files.
|
102
|
-
anlz_files : Sequence of str
|
103
|
-
The file paths of the ANLZ files in `anlz_dir`.
|
104
|
-
"""
|
105
|
-
root_dir = Path(root_dir)
|
106
|
-
assert root_dir.exists()
|
107
|
-
for anlz_dir in walk_anlz_dirs(root_dir):
|
108
|
-
files = {k: path for k, path in get_anlz_paths(anlz_dir).items() if path}
|
109
|
-
yield anlz_dir, files
|
110
|
-
|
111
|
-
|
112
|
-
def read_anlz_files(root: Union[str, Path] = "") -> dict:
|
113
|
-
"""Open all ANLZ files in the given root directory.
|
114
|
-
|
115
|
-
Parameters
|
116
|
-
----------
|
117
|
-
root : str or Path, optional
|
118
|
-
The root directory of the ANLZ files to open.
|
119
|
-
|
120
|
-
Returns
|
121
|
-
-------
|
122
|
-
anlz_files : dict
|
123
|
-
The opened ANLZ files stored in a dict with the corresponding file paths
|
124
|
-
as keys.
|
125
|
-
"""
|
126
|
-
paths = get_anlz_paths(root)
|
127
|
-
return {path: AnlzFile.parse_file(path) for path in paths.values() if path}
|
@@ -1,186 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# Author: Dylan Jones
|
3
|
-
# Date: 2023-02-01
|
4
|
-
|
5
|
-
import logging
|
6
|
-
from collections import abc
|
7
|
-
from pathlib import Path
|
8
|
-
from typing import Union
|
9
|
-
from .tags import TAGS
|
10
|
-
from . import structs
|
11
|
-
|
12
|
-
logger = logging.getLogger(__name__)
|
13
|
-
|
14
|
-
|
15
|
-
class BuildFileLengthError(Exception):
|
16
|
-
def __init__(self, struct, len_data):
|
17
|
-
super().__init__(
|
18
|
-
f"`len_file` ({struct.len_file}) of '{struct.type}' does not "
|
19
|
-
f"match the data-length ({len_data})!"
|
20
|
-
)
|
21
|
-
|
22
|
-
|
23
|
-
class AnlzFile(abc.Mapping):
|
24
|
-
"""Rekordbox `ANLZnnnn.xxx` binary file handler."""
|
25
|
-
|
26
|
-
def __init__(self):
|
27
|
-
self._path = ""
|
28
|
-
self.file_header = None
|
29
|
-
self.tags = list()
|
30
|
-
|
31
|
-
@property
|
32
|
-
def num_tags(self):
|
33
|
-
return len(self.tags)
|
34
|
-
|
35
|
-
@property
|
36
|
-
def tag_types(self):
|
37
|
-
return [tag.type for tag in self.tags]
|
38
|
-
|
39
|
-
@property
|
40
|
-
def path(self):
|
41
|
-
return self._path
|
42
|
-
|
43
|
-
@classmethod
|
44
|
-
def parse(cls, data: bytes):
|
45
|
-
"""Parses the in-memory data of a Rekordbox analysis binary file.
|
46
|
-
|
47
|
-
Parameters
|
48
|
-
----------
|
49
|
-
data : bytes
|
50
|
-
The in-memory binary contents of a Rekordbox analysis file.
|
51
|
-
|
52
|
-
Returns
|
53
|
-
-------
|
54
|
-
self : AnlzFile
|
55
|
-
The new instance with the parsed file content.
|
56
|
-
"""
|
57
|
-
self = cls()
|
58
|
-
self._parse(data)
|
59
|
-
return self
|
60
|
-
|
61
|
-
@classmethod
|
62
|
-
def parse_file(cls, path: Union[str, Path]):
|
63
|
-
"""Reads and parses a Rekordbox analysis binary file.
|
64
|
-
|
65
|
-
Parameters
|
66
|
-
----------
|
67
|
-
path : str or Path
|
68
|
-
The path of a Rekordbox analysis file which is used to read
|
69
|
-
the file contents before parsing the binary data.
|
70
|
-
|
71
|
-
Returns
|
72
|
-
-------
|
73
|
-
self : AnlzFile
|
74
|
-
The new instance with the parsed file content.
|
75
|
-
|
76
|
-
See Also
|
77
|
-
--------
|
78
|
-
AnlzFile.parse: Parses the data of a Rekordbox analysis file.
|
79
|
-
"""
|
80
|
-
path = Path(path)
|
81
|
-
ext = path.suffix.upper()
|
82
|
-
if ext not in (".DAT", ".EXT", ".2EX"):
|
83
|
-
raise ValueError(f"File type '{ext}' not supported!")
|
84
|
-
|
85
|
-
logger.debug(f"Reading file {path.name}")
|
86
|
-
with open(path, "rb") as fh:
|
87
|
-
data = fh.read()
|
88
|
-
|
89
|
-
self = cls.parse(data)
|
90
|
-
self._path = path
|
91
|
-
return self
|
92
|
-
|
93
|
-
def _parse(self, data: bytes):
|
94
|
-
file_header = structs.AnlzFileHeader.parse(data)
|
95
|
-
tag_type = file_header.type
|
96
|
-
assert tag_type == "PMAI"
|
97
|
-
|
98
|
-
tags = list()
|
99
|
-
i = file_header.len_header
|
100
|
-
while i < file_header.len_file:
|
101
|
-
# Get data starting from struct
|
102
|
-
tag_data = data[i:]
|
103
|
-
# Get the four byte struct type
|
104
|
-
tag_type = tag_data[:4].decode("ascii")
|
105
|
-
|
106
|
-
try:
|
107
|
-
# Parse the struct
|
108
|
-
tag = TAGS[tag_type](tag_data)
|
109
|
-
tags.append(tag)
|
110
|
-
len_header = tag.struct.len_header
|
111
|
-
len_tag = tag.struct.len_tag
|
112
|
-
logger.debug(
|
113
|
-
"Parsed struct '%s' (len_header=%s, len_tag=%s)",
|
114
|
-
tag_type,
|
115
|
-
len_header,
|
116
|
-
len_tag,
|
117
|
-
)
|
118
|
-
except KeyError:
|
119
|
-
logger.warning("Tag '%s' not supported!", tag_type)
|
120
|
-
tag = structs.AnlzTag.parse(tag_data)
|
121
|
-
len_tag = tag.len_tag
|
122
|
-
i += len_tag
|
123
|
-
|
124
|
-
self.file_header = file_header
|
125
|
-
self.tags = tags
|
126
|
-
|
127
|
-
def update_len(self):
|
128
|
-
# Update struct lengths
|
129
|
-
tags_len = 0
|
130
|
-
for tag in self.tags:
|
131
|
-
tag.update_len()
|
132
|
-
tags_len += tag.struct.len_tag
|
133
|
-
# Update file length
|
134
|
-
len_file = self.file_header.len_header + tags_len
|
135
|
-
self.file_header.len_file = len_file
|
136
|
-
|
137
|
-
def build(self):
|
138
|
-
self.update_len()
|
139
|
-
header_data = structs.AnlzFileHeader.build(self.file_header)
|
140
|
-
section_data = b"".join(tag.build() for tag in self.tags)
|
141
|
-
data = header_data + section_data
|
142
|
-
# Check `len_file`
|
143
|
-
len_file = self.file_header.len_file
|
144
|
-
len_data = len(data)
|
145
|
-
if len_file != len_data:
|
146
|
-
raise BuildFileLengthError(self.file_header, len_file)
|
147
|
-
|
148
|
-
return data
|
149
|
-
|
150
|
-
def save(self, path=""):
|
151
|
-
path = path or self._path
|
152
|
-
|
153
|
-
data = self.build()
|
154
|
-
with open(path, "wb") as fh:
|
155
|
-
fh.write(data)
|
156
|
-
|
157
|
-
def get_tag(self, key):
|
158
|
-
return self.__getitem__(key)[0]
|
159
|
-
|
160
|
-
def getall_tags(self, key):
|
161
|
-
return self.__getitem__(key)
|
162
|
-
|
163
|
-
def get(self, key):
|
164
|
-
return self.__getitem__(key)[0].get()
|
165
|
-
|
166
|
-
def getall(self, key):
|
167
|
-
return [tag.get() for tag in self.__getitem__(key)]
|
168
|
-
|
169
|
-
def __len__(self):
|
170
|
-
return len(self.keys())
|
171
|
-
|
172
|
-
def __iter__(self):
|
173
|
-
return iter(set(tag.type for tag in self.tags))
|
174
|
-
|
175
|
-
def __getitem__(self, item):
|
176
|
-
if item.isupper() and len(item) == 4:
|
177
|
-
return [tag for tag in self.tags if tag.type == item]
|
178
|
-
else:
|
179
|
-
return [tag for tag in self.tags if tag.name == item]
|
180
|
-
|
181
|
-
def __repr__(self):
|
182
|
-
return f"{self.__class__.__name__}({self.tag_types})"
|
183
|
-
|
184
|
-
def set_path(self, path):
|
185
|
-
tag = self.get_tag("PPTH")
|
186
|
-
tag.set(path)
|
@@ -1,299 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# Author: Dylan Jones
|
3
|
-
# Date: 2022-10-24
|
4
|
-
|
5
|
-
"""Binary structures of Rekordbox ANLZ-files.
|
6
|
-
|
7
|
-
References
|
8
|
-
----------
|
9
|
-
.. [1] Rekordbox Export Structure Analysis: Analysis Files,
|
10
|
-
https://djl-analysis.deepsymmetry.org/rekordbox-export-analysis/anlz.html
|
11
|
-
"""
|
12
|
-
|
13
|
-
from construct import Int8ub, Int16ub, Int32ub, PaddedString, Int32sb, StopIf
|
14
|
-
from construct import Const, Array, Padding, Bytes
|
15
|
-
from construct import Default, Enum, GreedyRange, Struct, Switch, this
|
16
|
-
|
17
|
-
|
18
|
-
# -- Beat Grid Tag (PQTZ) --------------------------------------------------------------
|
19
|
-
|
20
|
-
AnlzQuantizeTick = Struct(
|
21
|
-
"beat" / Int16ub,
|
22
|
-
"tempo" / Int16ub,
|
23
|
-
"time" / Int32ub # in ms from start
|
24
|
-
)
|
25
|
-
|
26
|
-
# len_header: 24
|
27
|
-
PQTZ = Struct(
|
28
|
-
"u1" / Padding(4),
|
29
|
-
"u2" / Const(0x80000, Int32ub),
|
30
|
-
"entry_count" / Int32ub,
|
31
|
-
"entries" / Array(this.entry_count, AnlzQuantizeTick),
|
32
|
-
)
|
33
|
-
|
34
|
-
|
35
|
-
# Extended Beat Grid Tag (PQT2)
|
36
|
-
|
37
|
-
AnlzQuantizeTick2 = Struct(
|
38
|
-
"beat" / Int8ub, # 1 byte
|
39
|
-
"unkown" / Int8ub, # 1 byte
|
40
|
-
)
|
41
|
-
|
42
|
-
# len_header: 56
|
43
|
-
PQT2 = Struct(
|
44
|
-
Padding(4), # -> 16
|
45
|
-
"u1" / Const(0x01000002, Int32ub), # -> 20, count of "bpm" objects?
|
46
|
-
Padding(4), # -> 24
|
47
|
-
"bpm" / Array(2, AnlzQuantizeTick), # -> 40 (2 * 8 bytes = 16 bytes)
|
48
|
-
"entry_count" / Int32ub, # -> 44 number of entries of 2 bytes
|
49
|
-
"u3" / Int32ub, # -> 48
|
50
|
-
"u4" / Int32ub, # -> 52
|
51
|
-
"u5" / Int32ub, # -> 56
|
52
|
-
# End header
|
53
|
-
StopIf(this.entry_count == 0),
|
54
|
-
# "entries" / Array(this.entry_count, Bytes(2)),
|
55
|
-
"entries" / Array(this.entry_count, AnlzQuantizeTick2),
|
56
|
-
)
|
57
|
-
|
58
|
-
|
59
|
-
# -- Cue List Tag (PCOB) ---------------------------------------------------------------
|
60
|
-
|
61
|
-
AnlzCuePointType = Enum(Int8ub, single=1, loop=2)
|
62
|
-
|
63
|
-
AnlzCuePointStatus = Enum(Int32ub, disabled=0, enabled=4)
|
64
|
-
|
65
|
-
AnlzTagCueObjectType = Enum(Int32ub, memory=0, hotcue=1)
|
66
|
-
|
67
|
-
AnlzCuePoint = Struct(
|
68
|
-
"type" / Const("PCPT", PaddedString(4, encoding="ascii")),
|
69
|
-
"len_header" / Int32ub,
|
70
|
-
"len_entry" / Int32ub,
|
71
|
-
"hot_cue" / Int32ub, # 0 for memory
|
72
|
-
"status" / AnlzCuePointStatus,
|
73
|
-
"u1" / Const(0x10000, Int32ub),
|
74
|
-
"order_first" / Int16ub, # 0xffff for first cue, 0,1,3 for next
|
75
|
-
"order_last" / Int16ub, # 1,2,3 for first, second, third cue, 0xffff for last
|
76
|
-
"type" / AnlzCuePointType,
|
77
|
-
Padding(1),
|
78
|
-
"u2" / Const(1000, Int16ub),
|
79
|
-
"time" / Int32ub,
|
80
|
-
"loop_time" / Default(Int32ub, -1),
|
81
|
-
Padding(16),
|
82
|
-
)
|
83
|
-
|
84
|
-
# len_header: 24
|
85
|
-
PCOB = Struct(
|
86
|
-
"cue_type" / AnlzTagCueObjectType,
|
87
|
-
"unk" / Int16ub,
|
88
|
-
"count" / Int16ub,
|
89
|
-
"memory_count" / Int32sb,
|
90
|
-
"entries" / Array(this.count, AnlzCuePoint),
|
91
|
-
)
|
92
|
-
|
93
|
-
|
94
|
-
# Extended (nxs2) Cue List Tag (PCO2)
|
95
|
-
|
96
|
-
AnlzCuePoint2 = Struct(
|
97
|
-
"type" / Const("PCP2", PaddedString(4, encoding="ascii")),
|
98
|
-
"len_header" / Int32ub,
|
99
|
-
"len_entry" / Int32ub,
|
100
|
-
"hot_cue" / Int32ub, # 0 for memory
|
101
|
-
"type" / Int8ub, # spotted: 0x010003e8 0x020003e8
|
102
|
-
Padding(3),
|
103
|
-
"time" / Int32ub,
|
104
|
-
"loop_time" / Default(Int32ub, -1),
|
105
|
-
"color_id" / Int8ub,
|
106
|
-
Padding(7),
|
107
|
-
"loop_enumerator" / Int16ub,
|
108
|
-
"loop_denominator" / Int16ub,
|
109
|
-
"len_comment" / Int32ub,
|
110
|
-
"comment" / PaddedString(this.len_comment, encoding="utf-16-be"),
|
111
|
-
"color_code" / Int8ub,
|
112
|
-
"color_red" / Int8ub,
|
113
|
-
"color_green" / Int8ub,
|
114
|
-
"color_blue" / Int8ub,
|
115
|
-
Padding(this.len_entry - 48 - this.len_comment),
|
116
|
-
)
|
117
|
-
|
118
|
-
# len_header: 20
|
119
|
-
PCO2 = Struct(
|
120
|
-
"type" / AnlzTagCueObjectType,
|
121
|
-
"count" / Int16ub,
|
122
|
-
"unknown" / Int16ub,
|
123
|
-
"entries" / Array(this.count, AnlzCuePoint2),
|
124
|
-
)
|
125
|
-
|
126
|
-
|
127
|
-
# -- Path Tag (PPTH) -------------------------------------------------------------------
|
128
|
-
|
129
|
-
# len_header: 16
|
130
|
-
PPTH = Struct(
|
131
|
-
"len_path" / Int32ub, # is 0 for some tag types
|
132
|
-
"path" / PaddedString(this.len_path - 2, encoding="utf-16-be"),
|
133
|
-
Padding(2),
|
134
|
-
)
|
135
|
-
|
136
|
-
|
137
|
-
# -- VBR Tag (PVBR) --------------------------------------------------------------------
|
138
|
-
|
139
|
-
# len_header: 16
|
140
|
-
PVBR = Struct(
|
141
|
-
"u1" / Int32ub,
|
142
|
-
"idx" / Array(400, Int32ub),
|
143
|
-
"u2" / Int32ub
|
144
|
-
)
|
145
|
-
|
146
|
-
|
147
|
-
# -- (Tiny) Waveform Preview Tag (PWAV / PWV2) -----------------------------------------
|
148
|
-
|
149
|
-
# len_header: 20
|
150
|
-
PWAV = Struct(
|
151
|
-
"len_preview" / Int32ub, # is 0 for some tag types
|
152
|
-
"unknown" / Const(0x10000, Int32ub),
|
153
|
-
"entries" / Array(this.len_preview, Int8ub),
|
154
|
-
)
|
155
|
-
|
156
|
-
# len_header: 20
|
157
|
-
PWV2 = Struct(
|
158
|
-
"len_preview" / Int32ub, # is 0 for some tag types
|
159
|
-
"unknown" / Const(0x10000, Int32ub),
|
160
|
-
"entries" / Array(this.len_preview, Int8ub),
|
161
|
-
)
|
162
|
-
|
163
|
-
|
164
|
-
# -- Waveform Detail Tag (PWV3) --------------------------------------------------------
|
165
|
-
|
166
|
-
# len_header: 24
|
167
|
-
PWV3 = Struct(
|
168
|
-
"len_entry_bytes" / Const(1, Int32ub),
|
169
|
-
"len_entries" / Int32ub,
|
170
|
-
"u1" / Const(0x00960000, Int32ub),
|
171
|
-
"entries" / Array(this.len_entries, Int8ub),
|
172
|
-
)
|
173
|
-
|
174
|
-
|
175
|
-
# -- Waveform Color Preview Tag (PWV4) -------------------------------------------------
|
176
|
-
|
177
|
-
# len_header: 24
|
178
|
-
PWV4 = Struct(
|
179
|
-
"len_entry_bytes" / Const(0x00000006, Int32ub),
|
180
|
-
"len_entries" / Int32ub,
|
181
|
-
"unknown" / Int32ub,
|
182
|
-
"entries" / Bytes(this.len_entry_bytes * this.len_entries),
|
183
|
-
)
|
184
|
-
|
185
|
-
|
186
|
-
# -- Waveform Color Detail Tag (PWV5) --------------------------------------------------
|
187
|
-
|
188
|
-
# len_header: 24
|
189
|
-
PWV5 = Struct(
|
190
|
-
"len_entry_bytes" / Const(0x00000002, Int32ub),
|
191
|
-
"len_entries" / Int32ub,
|
192
|
-
"unknown" / Int32ub,
|
193
|
-
"entries" / Array(this.len_entries, Int16ub),
|
194
|
-
)
|
195
|
-
|
196
|
-
# -- Song Structure Tag (PSSI) ---------------------------------------------------------
|
197
|
-
|
198
|
-
# FixMe: Implement reverse XOR mask to descramble values
|
199
|
-
|
200
|
-
SongStructureEntry = Struct(
|
201
|
-
"index" / Int16ub,
|
202
|
-
"beat" / Int16ub,
|
203
|
-
"kind" / Int16ub,
|
204
|
-
"u1" / Int8ub,
|
205
|
-
"k1" / Int8ub,
|
206
|
-
"u2" / Int8ub,
|
207
|
-
"k2" / Int8ub,
|
208
|
-
"u3" / Int8ub,
|
209
|
-
"b" / Int8ub,
|
210
|
-
"beat_2" / Int16ub,
|
211
|
-
"beat_3" / Int16ub,
|
212
|
-
"beat_4" / Int16ub,
|
213
|
-
"u4" / Int8ub,
|
214
|
-
"k3" / Int8ub,
|
215
|
-
"u5" / Int8ub,
|
216
|
-
"fill" / Int8ub,
|
217
|
-
"beat_fill" / Int16ub,
|
218
|
-
)
|
219
|
-
|
220
|
-
# len_header: 32
|
221
|
-
PSSI = Struct(
|
222
|
-
"len_entry_bytes" / Const(24, Int32ub),
|
223
|
-
"len_entries" / Int16ub,
|
224
|
-
"mood" / Bytes(2),
|
225
|
-
"u1" / Bytes(6),
|
226
|
-
"end_beat" / Bytes(2),
|
227
|
-
"u2" / Bytes(2),
|
228
|
-
"bank" / Bytes(1),
|
229
|
-
"u3" / Bytes(1),
|
230
|
-
"entries" / Array(this.len_entries, SongStructureEntry),
|
231
|
-
)
|
232
|
-
|
233
|
-
# -- PWV6 ------------------------------------------------------------------------------
|
234
|
-
|
235
|
-
# len_header: 20
|
236
|
-
PWV6 = Struct(
|
237
|
-
"len_entry_bytes" / Const(0x00000003, Int32ub),
|
238
|
-
"len_entries" / Int32ub,
|
239
|
-
"entries" / Bytes(this.len_entry_bytes * this.len_entries),
|
240
|
-
)
|
241
|
-
|
242
|
-
# -- PWV7 ------------------------------------------------------------------------------
|
243
|
-
|
244
|
-
# len_header: 24
|
245
|
-
PWV7 = Struct(
|
246
|
-
"len_entry_bytes" / Const(0x00000003, Int32ub),
|
247
|
-
"len_entries" / Int32ub,
|
248
|
-
"unknown" / Const(0x00960000, Int32ub),
|
249
|
-
"entries" / Bytes(this.len_entry_bytes * this.len_entries),
|
250
|
-
)
|
251
|
-
|
252
|
-
# -- PWVC ------------------------------------------------------------------------------
|
253
|
-
|
254
|
-
# len_header: 14
|
255
|
-
PWVC = Struct(
|
256
|
-
"unknown" / Int16ub,
|
257
|
-
"data" / Array(3, Int16ub)
|
258
|
-
)
|
259
|
-
|
260
|
-
|
261
|
-
# -- Main Items ------------------------------------------------------------------------
|
262
|
-
|
263
|
-
AnlzFileHeader = Struct(
|
264
|
-
"type" / PaddedString(4, encoding="ascii"),
|
265
|
-
"len_header" / Int32ub,
|
266
|
-
"len_file" / Int32ub,
|
267
|
-
"u1" / Int32ub,
|
268
|
-
"u2" / Int32ub,
|
269
|
-
"u3" / Int32ub,
|
270
|
-
"u4" / Int32ub,
|
271
|
-
)
|
272
|
-
|
273
|
-
|
274
|
-
AnlzTag = Struct(
|
275
|
-
"type" / PaddedString(4, encoding="ascii"),
|
276
|
-
"len_header" / Int32ub,
|
277
|
-
"len_tag" / Int32ub,
|
278
|
-
"content" / Switch(
|
279
|
-
this.type,
|
280
|
-
{
|
281
|
-
"PQTZ": PQTZ,
|
282
|
-
"PQT2": PQT2,
|
283
|
-
"PCOB": PCOB, # seen in both DAT and EXT files
|
284
|
-
"PCO2": PCO2, # seen in EXT files
|
285
|
-
"PPTH": PPTH,
|
286
|
-
"PVBR": PVBR,
|
287
|
-
"PSSI": PSSI, # seen in EXT files
|
288
|
-
"PWAV": PWAV,
|
289
|
-
"PWV2": PWV2,
|
290
|
-
"PWV3": PWV3, # seen in EXT files
|
291
|
-
"PWV4": PWV4, # seen in EXT files
|
292
|
-
"PWV5": PWV5, # seen in EXT files
|
293
|
-
"PWV6": PWV6, # seen in 2EX files
|
294
|
-
"PWV7": PWV7, # seen in 2EX files
|
295
|
-
"PWVC": PWVC, # seen in 2EX files
|
296
|
-
},
|
297
|
-
default=Bytes(this.len_tag - 12),
|
298
|
-
),
|
299
|
-
)
|