rockbox/utils/nwztools/database/gen_db.py
Amaury Pouly 698779e3e8 sonynwz: add nvp nodes for NW-A40/ZX300, various fixes for tools
We still miss the model IDS for those device so scsitool won't be able to
recognize them automatically.

Change-Id: I17ae0f0d95c011cea8e289def63c7673b6c4b667
2017-10-06 12:10:33 +02:00

334 lines
12 KiB
Python
Executable file

#!/usr/bin/python3
import glob
import os
import re
import subprocess
import hashlib
import sys
# arguments
if len(sys.argv) != 2:
print("Usage: %s output_directory" % sys.argv[0])
exit(1)
output_directory = sys.argv[1]
# check path is valid
if not os.path.isdir(output_directory):
print("Error: '%s' is not a valid directory" % output_directory)
exit(1)
# parse models.txt
g_models = []
with open('models.txt') as fp:
for line in fp:
# we unpack and repack 1) to make the format obvious 2) to catch errors
mid,name = line.rstrip().split(",")
g_models.append({'mid': int(mid, 0), 'name': name})
# parse series.txt
g_series = []
g_series_codename = set()
with open('series.txt') as fp:
for line in fp:
# we unpack and repack 1) to make the format obvious 2) to catch errors
arr = line.rstrip().split(",")
codename = arr[0]
name = arr[1]
models = arr[2:]
# handle empty list
if len(models) == 1 and models[0] == "":
models = []
models = [int(mid,0) for mid in models]
g_series.append({'codename': codename, 'name': name, 'models': models})
g_series_codename.add(codename)
# parse all maps in nvp/
# since most nvps are the same, what we actually do is to compute the md5sum hash
# of all files, to identify groups and then each entry in the name is in fact the
# hash, and we only parse one file per hash group
g_hash_nvp = dict() # hash -> nvp
g_nvp_hash = dict() # codename -> hash
HASH_SIZE=6
map_files = glob.glob('nvp/nw*.txt')
for f in map_files:
h = hashlib.md5()
h.update(open(f, "rb").read())
hash = h.hexdigest()
codename = re.search('(nw.*)\.txt', f).group(1)
# sanity check
if not (codename in g_series_codename):
print("Warning: file %s does not have a match series in series.txt" % f)
hash = hash[:HASH_SIZE]
# only keep one file
if not (hash in g_hash_nvp):
g_hash_nvp[hash] = set()
g_hash_nvp[hash].add(codename);
g_nvp_hash[codename] = hash
# we have some file nodes (nodes-*) but not necessarily for all series
# so for each hash group, try to find at least one
for hash in g_hash_nvp:
# look at all codename and see if we can find one with a node file
node_codename = ""
for codename in g_hash_nvp[hash]:
if os.path.isfile("nvp/nodes-%s.txt" % codename):
node_codename = codename
break
# if we didn't find one, we just keep the first one
# otherwise keep the one we found
if node_codename == "":
node_codename = g_hash_nvp[hash].pop()
g_hash_nvp[hash] = node_codename
# for each entry in g_hash_nvp, replace the file name by the actual table
# that we parse, and compute all nvp names at the same time
g_nvp_names = set() # set of all nvp names
g_nvp_desc = dict() # name -> set of all description of a node
g_nvp_size = dict() # name -> set of all possible sizes of a node
for hash in g_hash_nvp:
codename = g_hash_nvp[hash]
# extract codename from file
# parse file
map = dict()
with open("nvp/%s.txt" % codename) as fp:
for line in fp:
# we unpack and repack 1) to make the format obvious 2) to catch errors
name,index = line.rstrip().split(",")
# convert node to integer but be careful of leading 0 (ie 010 is actually
# 10 in decimal, it is not in octal)
index = int(index, 10)
map[index] = name
g_nvp_names.add(name)
# parse node map if any
node_map = dict()
if os.path.isfile("nvp/nodes-%s.txt" % codename):
with open("nvp/nodes-%s.txt" % codename) as fp:
for line in fp:
# we unpack and repack 1) to make the format obvious 2) to catch errors
index,size,desc = line.rstrip().split(",")
# convert node to integer but be careful of leading 0 (ie 010 is actually
# 10 in decimal, it is not in octal)
index = int(index, 10)
desc = desc.rstrip()
node_map[index] = {'size': size, 'desc': desc}
# compute final nvp
nvp = dict()
for index in map:
size = 0
desc = ""
name = map[index]
if index in node_map:
size = node_map[index]["size"]
desc = node_map[index]["desc"]
nvp[name] = index
if not (name in g_nvp_desc):
g_nvp_desc[name] = set()
if len(desc) != 0:
g_nvp_desc[name].add(desc)
if not (name in g_nvp_size):
g_nvp_size[name] = set()
if size != 0:
g_nvp_size[name].add(size)
g_hash_nvp[hash] = nvp
#
# generate header
#
header_begin = \
"""\
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \\
* \/ \/ \/ \/ \/
*
* Copyright (C) 2016 Amaury Pouly
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __NWZ_DB_H__
#define __NWZ_DB_H__
/** /!\ This file was automatically generated, DO NOT MODIFY IT DIRECTLY /!\ */
/* List of all known NVP nodes */
enum nwz_nvp_node_t
{
"""
header_end = \
"""\
NWZ_NVP_COUNT /* Number of nvp nodes */
};
/* Invalid NVP index */
#define NWZ_NVP_INVALID -1 /* Non-existent entry */
/* Number of models */
#define NWZ_MODEL_COUNT %s
/* Number of series */
#define NWZ_SERIES_COUNT %s
/* NVP node info */
struct nwz_nvp_info_t
{
const char *name; /* Sony's name: "bti" */
unsigned long size; /* Size in bytes */
const char *desc; /* Description: "bootloader image" */
};
/* NVP index map (nwz_nvp_node_t -> index) */
typedef int nwz_nvp_index_t[NWZ_NVP_COUNT];
/* Model info */
struct nwz_model_info_t
{
unsigned long mid; /* Model ID: first 4 bytes of the NVP mid entry */
const char *name; /* Human name: "NWZ-E463" */
};
/* Series info */
struct nwz_series_info_t
{
const char *codename; /* Rockbox codename: nwz-e460 */
const char *name; /* Human name: "NWZ-E460 Series" */
int mid_count; /* number of entries in mid_list */
unsigned long *mid; /* List of model IDs */
/* Pointer to a name -> index map, nonexistent entries map to NWZ_NVP_INVALID */
nwz_nvp_index_t *nvp_index;
};
/* List of all NVP entries, indexed by nwz_nvp_node_t */
extern struct nwz_nvp_info_t nwz_nvp[NWZ_NVP_COUNT];
/* List of all models, sorted by increasing values of model ID */
extern struct nwz_model_info_t nwz_model[NWZ_MODEL_COUNT];
/* List of all series */
extern struct nwz_series_info_t nwz_series[NWZ_SERIES_COUNT];
#endif /* __NWZ_DB_H__ */
"""
with open(os.path.join(output_directory, "nwz-db.h"), "w") as fp:
fp.write(header_begin)
# generate list of all nvp nodes
for name in sorted(g_nvp_names):
# create comment to explain the meaning, gather several meaning together
# if there are more than one (sorted to keep a stable order when we update)
explain = ""
if name in g_nvp_desc:
explain = " | ".join(sorted(list(g_nvp_desc[name])))
# overwrite desc set with a single string for later
g_nvp_desc[name] = explain
fp.write(" NWZ_NVP_%s, /* %s */\n" % (name.upper(), explain))
fp.write(header_end % (len(g_models), len(g_series)))
#
# generate tables
#
impl_begin = \
"""\
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \\
* \/ \/ \/ \/ \/
*
* Copyright (C) 2016 Amaury Pouly
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/** /!\ This file was automatically generated, DO NOT MODIFY IT DIRECTLY /!\ */
#include "nwz-db.h"
struct nwz_model_info_t nwz_model[NWZ_MODEL_COUNT] =
{
"""
def by_mid(model):
return model["mid"]
def by_name(nvp_entry):
return nvp_entry["name"]
def codename_to_c(codename):
return re.sub('[^a-zA-Z0-9]', '_', codename, 0)
with open(os.path.join(output_directory, "nwz-db.c"), "w") as fp:
fp.write(impl_begin)
# generate model list (sort by mid)
for model in sorted(g_models, key = by_mid):
fp.write(" { %s, \"%s\" },\n" % (hex(model["mid"]), model["name"]))
fp.write("};\n")
# generate nvps
for hash in sorted(g_hash_nvp):
nvp = g_hash_nvp[hash]
fp.write("\nstatic int nvp_index_%s[NWZ_NVP_COUNT] =\n" % hash)
fp.write("{\n")
for name in sorted(g_nvp_names):
index = "NWZ_NVP_INVALID"
if name in nvp:
index = nvp[name]
fp.write(" [NWZ_NVP_%s] = %s,\n" % (name.upper(), index))
fp.write("};\n")
# generate nvp info
fp.write("\nstruct nwz_nvp_info_t nwz_nvp[NWZ_NVP_COUNT] =\n")
fp.write("{\n")
for name in sorted(g_nvp_names):
size = 0
if name in g_nvp_size:
size_set = g_nvp_size[name]
if len(size_set) == 0:
size = 0
elif len(size_set) == 1:
size = next(iter(size_set))
else:
print("Warning: nvp node \"%s\" has several possible sizes: %s"
% (name, size_set))
size = 0
desc = ""
if name in g_nvp_desc:
desc = g_nvp_desc[name]
fp.write(" [NWZ_NVP_%s] = { \"%s\", %s, \"%s\" },\n" % (name.upper(),
name, size, desc))
fp.write("};\n")
# generate list of models for each series
for series in g_series:
c_codename = codename_to_c(series["codename"])
list = [hex(mid) for mid in series["models"]]
limit = 3
c_list = ""
while len(list) != 0:
if len(list) <= limit:
c_list = c_list + ", ".join(list)
list = []
else:
c_list = c_list + ", ".join(list[:limit]) + ",\n "
list = list[limit:]
limit = 6
fp.write("\nstatic unsigned long models_%s[] = { %s };\n" % (c_codename, c_list))
# generate series list
fp.write("\nstruct nwz_series_info_t nwz_series[NWZ_SERIES_COUNT] =\n{\n")
for series in g_series:
name = series["name"]
codename = series["codename"]
c_codename = codename_to_c(codename)
nvp = "0"
if codename in g_nvp_hash:
nvp = "&nvp_index_%s" % g_nvp_hash[codename]
fp.write(" { \"%s\", \"%s\", %s, models_%s, %s },\n" % (codename,
name, len(series["models"]), c_codename, nvp))
fp.write("};\n")