A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1#!/usr/bin/python3
2import glob
3import os
4import re
5import subprocess
6import hashlib
7import sys
8
9# arguments
10if len(sys.argv) != 2:
11 print("Usage: %s output_directory" % sys.argv[0])
12 exit(1)
13output_directory = sys.argv[1]
14# check path is valid
15if not os.path.isdir(output_directory):
16 print("Error: '%s' is not a valid directory" % output_directory)
17 exit(1)
18
19# parse models.txt
20g_models = []
21with open('models.txt') as fp:
22 for line in fp:
23 # we unpack and repack 1) to make the format obvious 2) to catch errors
24 mid,name = line.rstrip().split(",")
25 g_models.append({'mid': int(mid, 0), 'name': name})
26# parse series.txt
27g_series = []
28g_series_codename = set()
29with open('series.txt') as fp:
30 for line in fp:
31 # we unpack and repack 1) to make the format obvious 2) to catch errors
32 arr = line.rstrip().split(",")
33 codename = arr[0]
34 name = arr[1]
35 models = arr[2:]
36 # handle empty list
37 if len(models) == 1 and models[0] == "":
38 models = []
39 models = [int(mid,0) for mid in models]
40 g_series.append({'codename': codename, 'name': name, 'models': models})
41 g_series_codename.add(codename)
42# parse all maps in nvp/
43# since most nvps are the same, what we actually do is to compute the md5sum hash
44# of all files, to identify groups and then each entry in the name is in fact the
45# hash, and we only parse one file per hash group
46g_hash_nvp = dict() # hash -> nvp
47g_nvp_hash = dict() # codename -> hash
48HASH_SIZE=6
49map_files = glob.glob('nvp/nw*.txt') + glob.glob('nvp/dmp*.txt')
50for f in map_files:
51 h = hashlib.md5()
52 h.update(open(f, "rb").read())
53 hash = h.hexdigest()
54 codename = re.search('nvp/([^\.]*)\.txt', f).group(1)
55 # sanity check
56 if not (codename in g_series_codename):
57 print("Warning: file %s does not have a match series in series.txt" % f)
58 hash = hash[:HASH_SIZE]
59 # only keep one file
60 if not (hash in g_hash_nvp):
61 g_hash_nvp[hash] = set()
62 g_hash_nvp[hash].add(codename);
63 g_nvp_hash[codename] = hash
64# we have some file nodes (nodes-*) but not necessarily for all series
65# so for each hash group, try to find at least one
66for hash in g_hash_nvp:
67 # look at all codename and see if we can find one with a node file
68 node_codename = ""
69 for codename in g_hash_nvp[hash]:
70 if os.path.isfile("nvp/nodes-%s.txt" % codename):
71 node_codename = codename
72 break
73 # if we didn't find one, we just keep the first one
74 # otherwise keep the one we found
75 if node_codename == "":
76 node_codename = g_hash_nvp[hash].pop()
77 g_hash_nvp[hash] = node_codename
78# for each entry in g_hash_nvp, replace the file name by the actual table
79# that we parse, and compute all nvp names at the same time
80g_nvp_names = set() # set of all nvp names
81g_nvp_desc = dict() # name -> set of all description of a node
82g_nvp_size = dict() # name -> set of all possible sizes of a node
83for hash in g_hash_nvp:
84 codename = g_hash_nvp[hash]
85 # extract codename from file
86 # parse file
87 map = dict()
88 with open("nvp/%s.txt" % codename) as fp:
89 for line in fp:
90 # we unpack and repack 1) to make the format obvious 2) to catch errors
91 name,index = line.rstrip().split(",")
92 # convert node to integer but be careful of leading 0 (ie 010 is actually
93 # 10 in decimal, it is not in octal)
94 index = int(index, 10)
95 map[index] = name
96 g_nvp_names.add(name)
97 # parse node map if any
98 node_map = dict()
99 if os.path.isfile("nvp/nodes-%s.txt" % codename):
100 with open("nvp/nodes-%s.txt" % codename) as fp:
101 for line in fp:
102 # we unpack and repack 1) to make the format obvious 2) to catch errors
103 index,size,desc = line.rstrip().split(",")
104 # convert node to integer but be careful of leading 0 (ie 010 is actually
105 # 10 in decimal, it is not in octal)
106 index = int(index, 10)
107 desc = desc.rstrip()
108 node_map[index] = {'size': size, 'desc': desc}
109 # compute final nvp
110 nvp = dict()
111 for index in map:
112 size = 0
113 desc = ""
114 name = map[index]
115 if index in node_map:
116 size = node_map[index]["size"]
117 desc = node_map[index]["desc"]
118 nvp[name] = index
119 if not (name in g_nvp_desc):
120 g_nvp_desc[name] = set()
121 if len(desc) != 0:
122 g_nvp_desc[name].add(desc)
123 if not (name in g_nvp_size):
124 g_nvp_size[name] = set()
125 if size != 0:
126 g_nvp_size[name].add(size)
127 g_hash_nvp[hash] = nvp
128
129#
130# generate header
131#
132header_begin = \
133"""\
134/***************************************************************************
135 * __________ __ ___.
136 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
137 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
138 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
139 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \\
140 * \/ \/ \/ \/ \/
141 *
142 * Copyright (C) 2016 Amaury Pouly
143 *
144 * This program is free software; you can redistribute it and/or
145 * modify it under the terms of the GNU General Public License
146 * as published by the Free Software Foundation; either version 2
147 * of the License, or (at your option) any later version.
148 *
149 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
150 * KIND, either express or implied.
151 *
152 ****************************************************************************/
153#ifndef __NWZ_DB_H__
154#define __NWZ_DB_H__
155
156/** /!\ This file was automatically generated, DO NOT MODIFY IT DIRECTLY /!\ */
157
158/* List of all known NVP nodes */
159enum nwz_nvp_node_t
160{
161"""
162
163header_end = \
164"""\
165 NWZ_NVP_COUNT /* Number of nvp nodes */
166};
167
168/* Invalid NVP index */
169#define NWZ_NVP_INVALID -1 /* Non-existent entry */
170/* Number of models */
171#define NWZ_MODEL_COUNT %s
172/* Number of series */
173#define NWZ_SERIES_COUNT %s
174
175/* NVP node info */
176struct nwz_nvp_info_t
177{
178 const char *name; /* Sony's name: "bti" */
179 unsigned long size; /* Size in bytes */
180 const char *desc; /* Description: "bootloader image" */
181};
182
183/* NVP index map (nwz_nvp_node_t -> index) */
184typedef int nwz_nvp_index_t[NWZ_NVP_COUNT];
185
186/* Model info */
187struct nwz_model_info_t
188{
189 unsigned long mid; /* Model ID: first 4 bytes of the NVP mid entry */
190 const char *name; /* Human name: "NWZ-E463" */
191};
192
193/* Series info */
194struct nwz_series_info_t
195{
196 const char *codename; /* Rockbox codename: nwz-e460 */
197 const char *name; /* Human name: "NWZ-E460 Series" */
198 int mid_count; /* number of entries in mid_list */
199 unsigned long *mid; /* List of model IDs */
200 /* Pointer to a name -> index map, nonexistent entries map to NWZ_NVP_INVALID */
201 nwz_nvp_index_t *nvp_index;
202};
203
204/* List of all NVP entries, indexed by nwz_nvp_node_t */
205extern struct nwz_nvp_info_t nwz_nvp[NWZ_NVP_COUNT];
206/* List of all models, sorted by increasing values of model ID */
207extern struct nwz_model_info_t nwz_model[NWZ_MODEL_COUNT];
208/* List of all series */
209extern struct nwz_series_info_t nwz_series[NWZ_SERIES_COUNT];
210
211#endif /* __NWZ_DB_H__ */
212"""
213
214with open(os.path.join(output_directory, "nwz-db.h"), "w") as fp:
215 fp.write(header_begin)
216 # generate list of all nvp nodes
217 for name in sorted(g_nvp_names):
218 # create comment to explain the meaning, gather several meaning together
219 # if there are more than one (sorted to keep a stable order when we update)
220 explain = ""
221 if name in g_nvp_desc:
222 explain = " | ".join(sorted(list(g_nvp_desc[name])))
223 # overwrite desc set with a single string for later
224 g_nvp_desc[name] = explain
225 fp.write(" NWZ_NVP_%s, /* %s */\n" % (name.upper(), explain))
226 fp.write(header_end % (len(g_models), len(g_series)))
227
228#
229# generate tables
230#
231impl_begin = \
232"""\
233/***************************************************************************
234 * __________ __ ___.
235 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
236 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
237 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
238 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \\
239 * \/ \/ \/ \/ \/
240 *
241 * Copyright (C) 2016 Amaury Pouly
242 *
243 * This program is free software; you can redistribute it and/or
244 * modify it under the terms of the GNU General Public License
245 * as published by the Free Software Foundation; either version 2
246 * of the License, or (at your option) any later version.
247 *
248 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
249 * KIND, either express or implied.
250 *
251 ****************************************************************************/
252
253/** /!\ This file was automatically generated, DO NOT MODIFY IT DIRECTLY /!\ */
254
255#include "nwz-db.h"
256
257struct nwz_model_info_t nwz_model[NWZ_MODEL_COUNT] =
258{
259"""
260
261def by_mid(model):
262 return model["mid"]
263
264def by_name(nvp_entry):
265 return nvp_entry["name"]
266
267def codename_to_c(codename):
268 return re.sub('[^a-zA-Z0-9]', '_', codename, 0)
269
270with open(os.path.join(output_directory, "nwz-db.c"), "w") as fp:
271 fp.write(impl_begin)
272 # generate model list (sort by mid)
273 for model in sorted(g_models, key = by_mid):
274 fp.write(" { %s, \"%s\" },\n" % (hex(model["mid"]), model["name"]))
275 fp.write("};\n")
276 # generate nvps
277 for hash in sorted(g_hash_nvp):
278 nvp = g_hash_nvp[hash]
279 fp.write("\nstatic int nvp_index_%s[NWZ_NVP_COUNT] =\n" % hash)
280 fp.write("{\n")
281 for name in sorted(g_nvp_names):
282 index = "NWZ_NVP_INVALID"
283 if name in nvp:
284 index = nvp[name]
285 fp.write(" [NWZ_NVP_%s] = %s,\n" % (name.upper(), index))
286 fp.write("};\n")
287 # generate nvp info
288 fp.write("\nstruct nwz_nvp_info_t nwz_nvp[NWZ_NVP_COUNT] =\n")
289 fp.write("{\n")
290 for name in sorted(g_nvp_names):
291 size = 0
292 if name in g_nvp_size:
293 size_set = g_nvp_size[name]
294 if len(size_set) == 0:
295 size = 0
296 elif len(size_set) == 1:
297 size = next(iter(size_set))
298 else:
299 print("Warning: nvp node \"%s\" has several possible sizes: %s"
300 % (name, size_set))
301 size = 0
302 desc = ""
303 if name in g_nvp_desc:
304 desc = g_nvp_desc[name]
305 fp.write(" [NWZ_NVP_%s] = { \"%s\", %s, \"%s\" },\n" % (name.upper(),
306 name, size, desc))
307 fp.write("};\n")
308 # generate list of models for each series
309 for series in g_series:
310 c_codename = codename_to_c(series["codename"])
311 list = [hex(mid) for mid in series["models"]]
312 limit = 3
313 c_list = ""
314 while len(list) != 0:
315 if len(list) <= limit:
316 c_list = c_list + ", ".join(list)
317 list = []
318 else:
319 c_list = c_list + ", ".join(list[:limit]) + ",\n "
320 list = list[limit:]
321 limit = 6
322 fp.write("\nstatic unsigned long models_%s[] = { %s };\n" % (c_codename, c_list))
323 # generate series list
324 fp.write("\nstruct nwz_series_info_t nwz_series[NWZ_SERIES_COUNT] =\n{\n")
325 for series in g_series:
326 name = series["name"]
327 codename = series["codename"]
328 c_codename = codename_to_c(codename)
329 nvp = "0"
330 if codename in g_nvp_hash:
331 nvp = "&nvp_index_%s" % g_nvp_hash[codename]
332 fp.write(" { \"%s\", \"%s\", %s, models_%s, %s },\n" % (codename,
333 name, len(series["models"]), c_codename, nvp))
334 fp.write("};\n")