A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 334 lines 12 kB view raw
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")