mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
277 lines
11 KiB
Python
Executable File
277 lines
11 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# pylint: disable=redefined-outer-name,too-many-branches
|
|
'''
|
|
Update autogenerated source files from yaml database.
|
|
|
|
Copyright (c) 2019, vit9696
|
|
'''
|
|
|
|
import fnmatch
|
|
import json
|
|
import operator
|
|
import os
|
|
import sys
|
|
import unicodedata
|
|
import yaml
|
|
import update_products
|
|
|
|
|
|
def remove_accents(input_str):
|
|
nfkd_form = unicodedata.normalize('NFKD', input_str)
|
|
return ''.join([c for c in nfkd_form if not unicodedata.combining(c)])
|
|
|
|
|
|
def load_db(dbpath):
|
|
'''
|
|
Load yaml database and return in a list.
|
|
'''
|
|
|
|
if not os.path.exists(dbpath):
|
|
print(f'Cannot find {dbpath} directory, rerun from AppleModels directory!')
|
|
sys.exit(1)
|
|
|
|
db = []
|
|
|
|
for root, _, files in os.walk(dbpath):
|
|
for file in fnmatch.filter(files, '*.yaml'):
|
|
path = os.path.join(root, file)
|
|
with open(path, 'r', encoding='utf-8') as fh:
|
|
try:
|
|
r = yaml.safe_load(fh)
|
|
if r.get('SystemProductName', None) is None:
|
|
print(f'WARN: Missing SystemProductName in {path}, skipping!')
|
|
continue
|
|
db.append(r)
|
|
except yaml.YAMLError as e:
|
|
print(f'Failed to parse file {path} - {e}')
|
|
sys.exit(1)
|
|
|
|
if len(db) == 0:
|
|
print('Empty database!')
|
|
sys.exit(1)
|
|
|
|
# Sorting is required for fast lookup.
|
|
return sorted(db, key=operator.itemgetter('SystemProductName'))
|
|
|
|
|
|
def gather_products(db, ptype='AppleModelCode', empty_valid=False, shared_valid=False, fatal=True):
|
|
'''
|
|
Obtain all product codes from the database
|
|
'''
|
|
products = []
|
|
for info in db:
|
|
pp = info.get(ptype, None)
|
|
if pp is None:
|
|
continue
|
|
for p in pp:
|
|
if p == '':
|
|
if not empty_valid:
|
|
print(f"ERROR: {info['SystemProductName']} in contains empty {ptype}, skipping!")
|
|
if fatal:
|
|
sys.exit(1)
|
|
continue
|
|
if p in ['000', '0000']:
|
|
print(f"WARN: {info['SystemProductName']} in contains zero {ptype}, skipping!")
|
|
continue
|
|
if p in products:
|
|
if not shared_valid:
|
|
print(f"ERROR: {info['SystemProductName']} shares {ptype} {p} with other model!")
|
|
if fatal:
|
|
sys.exit(1)
|
|
continue
|
|
products.append(p)
|
|
return products
|
|
|
|
|
|
def validate_products(db, dbpd):
|
|
usedproducts = gather_products(db)
|
|
|
|
# FIXME: Empty is not valid, but we let it be for now.
|
|
gather_products(db, 'AppleBoardCode', True, True, False)
|
|
|
|
knownproducts = dbpd
|
|
for product in usedproducts:
|
|
if knownproducts.get(product, None) is None:
|
|
print(f'ERROR: Model {product} is used in DataBase but not present in Products!')
|
|
sys.exit(1)
|
|
if knownproducts[product][update_products.KEY_STATUS] != update_products.STATUS_OK:
|
|
print(f'ERROR: Model {product} is used in DataBase but not valid in Products!')
|
|
sys.exit(1)
|
|
|
|
to_add = {}
|
|
|
|
for product in knownproducts:
|
|
if knownproducts[product][update_products.KEY_STATUS] != update_products.STATUS_OK:
|
|
continue
|
|
|
|
name = knownproducts[product][update_products.KEY_NAME]
|
|
if name.find('Mac') < 0 and name.find('Xserve') < 0:
|
|
continue
|
|
if name.find('M1') >= 0:
|
|
continue
|
|
|
|
if len(product) > 3 and product not in usedproducts:
|
|
print(f'WARN: Model {product} ({name}) is known but is not used in DataBase!')
|
|
|
|
if to_add.get(name, None) is None:
|
|
to_add[name] = []
|
|
to_add[name].append(product)
|
|
continue
|
|
|
|
if len(to_add) > 0:
|
|
for sysname in to_add.items():
|
|
for info in db:
|
|
if sysname in info['Specifications']['SystemReportName']:
|
|
print(f"New AppleModelCode for {info['SystemProductName']}:")
|
|
for model in to_add[sysname]:
|
|
print(f" - \"{model}\"")
|
|
|
|
|
|
def export_db_macinfolib(db, path, year=0):
|
|
'''
|
|
Export yaml database to MacInfoLib format.
|
|
TODO: use jinja2?
|
|
'''
|
|
|
|
with open(path, 'w', encoding='utf-8') as fh:
|
|
print('// DO NOT EDIT! This is an autogenerated file.', file=fh)
|
|
print('#include "MacInfoInternal.h"', file=fh)
|
|
print('CONST MAC_INFO_INTERNAL_ENTRY gMacInfoModels[] = {', file=fh)
|
|
|
|
for info in db:
|
|
if max(info['AppleModelYear']) < year:
|
|
continue
|
|
|
|
sb_model = info.get('AppleModelId')
|
|
sb_model = f'"{sb_model.lower()}"' if sb_model else 'NULL'
|
|
|
|
print(
|
|
' {\n'
|
|
f''' .SystemProductName = "{info['SystemProductName']}",\n'''
|
|
f''' .BoardProduct = "{info['BoardProduct'][0] if isinstance(info['BoardProduct'], list) else info['BoardProduct']}",\n'''
|
|
f''' .BoardRevision = {f"0x{info['BoardRevision']:X}" if 'BoardRevision' in info else 'MAC_INFO_BOARD_REVISION_MISSING'},\n'''
|
|
f''' .SmcRevision = {{{', '.join(map(str, info.get('SmcRevision', [0x00])))}}},\n'''
|
|
f''' .SmcBranch = {{{', '.join(map(str, info.get('SmcBranch', [0x00])))}}},\n'''
|
|
f''' .SmcPlatform = {{{', '.join(map(str, info.get('SmcPlatform', [0x00])))}}},\n'''
|
|
f''' .BIOSVersion = "{info['BIOSVersion']}",\n'''
|
|
f''' .BIOSReleaseDate = "{info['BIOSReleaseDate']}",\n'''
|
|
f''' .SystemVersion = "{info['SystemVersion']}",\n'''
|
|
f''' .SystemSKUNumber = "{info['SystemSKUNumber']}",\n'''
|
|
f''' .SystemFamily = "{info['SystemFamily']}",\n'''
|
|
f''' .BoardVersion = "{info['BoardVersion']}",\n'''
|
|
f''' .BoardAssetTag = "{info['BoardAssetTag']}",\n'''
|
|
f''' .BoardLocationInChassis = "{info['BoardLocationInChassis']}",\n'''
|
|
f''' .SmcGeneration = 0x{info['SmcGeneration']:X},\n'''
|
|
f''' .BoardType = 0x{info['BoardType']:X},\n'''
|
|
f''' .ChassisType = 0x{info['ChassisType']:X},\n'''
|
|
f''' .MemoryFormFactor = 0x{info['MemoryFormFactor']:X},\n'''
|
|
f''' .PlatformFeature = {f"0x{info['PlatformFeature']:X}" if 'PlatformFeature' in info else 'MAC_INFO_PLATFORM_FEATURE_MISSING'},\n'''
|
|
f''' .ChassisAssetTag = "{info['ChassisAssetTag']}",\n'''
|
|
f''' .FirmwareFeatures = 0x{info.get('ExtendedFirmwareFeatures', info.get('FirmwareFeatures', 0)):X}ULL,\n'''
|
|
f''' .FirmwareFeaturesMask = 0x{info.get('ExtendedFirmwareFeaturesMask', info.get('FirmwareFeaturesMask', 0)):X}ULL,\n'''
|
|
f' .SecureBootModel = {sb_model},\n'
|
|
f' }},', file=fh)
|
|
|
|
print('};', file=fh)
|
|
|
|
print('CONST UINTN gMacInfoModelCount = ARRAY_SIZE (gMacInfoModels);', file=fh)
|
|
print('CONST UINTN gMacInfoDefaultModel = 0;', file=fh)
|
|
|
|
|
|
def export_db_macserial(db, dbpd, path, year=0):
|
|
'''
|
|
Export yaml database to macserial format.
|
|
TODO: use jinja2?
|
|
'''
|
|
|
|
with open(path, 'w', encoding='utf-8') as fh:
|
|
print('#ifndef GENSERIAL_MODELINFO_AUTOGEN_H', file=fh)
|
|
print('#define GENSERIAL_MODELINFO_AUTOGEN_H\n', file=fh)
|
|
print('// DO NOT EDIT! This is an autogenerated file.\n', file=fh)
|
|
print('#include "macserial.h"\n', file=fh)
|
|
|
|
print('typedef enum {', file=fh)
|
|
|
|
for info in db:
|
|
print(f" {info['SystemProductName'].replace(',', '_')}, // {info['Specifications']['CPU'][0]}", file=fh)
|
|
|
|
print('} AppleModel;\n', file=fh)
|
|
print(f'#define APPLE_MODEL_MAX {len(db)}\n', file=fh)
|
|
|
|
print('static PLATFORMDATA ApplePlatformData[] = {', file=fh)
|
|
for info in db:
|
|
print(f''' {{ "{info['SystemProductName']}", "{info['SystemSerialNumber']}" }},''', file=fh)
|
|
|
|
print('};\n', file=fh)
|
|
|
|
print(f"#define APPLE_MODEL_CODE_MAX {max(len(info['AppleModelCode']) for info in db)}", file=fh)
|
|
print('static const char *AppleModelCode[][APPLE_MODEL_CODE_MAX] = {', file=fh)
|
|
|
|
for info in db:
|
|
print(f""" /* {info['SystemProductName']:14} */ {{"{'", "'.join(info['AppleModelCode'])}"}},""", file=fh)
|
|
|
|
print('};\n', file=fh)
|
|
|
|
print(f"#define APPLE_BOARD_CODE_MAX {max(len(info['AppleBoardCode']) for info in db)}", file=fh)
|
|
print('static const char *AppleBoardCode[][APPLE_BOARD_CODE_MAX] = {', file=fh)
|
|
|
|
for info in db:
|
|
print(f""" /* {info['SystemProductName']:14} */ {{"{'", "'.join(info['AppleBoardCode'])}"}},""", file=fh)
|
|
|
|
print('};\n', file=fh)
|
|
|
|
print(f"#define APPLE_MODEL_YEAR_MAX {max(len(info['AppleModelYear']) for info in db)}", file=fh)
|
|
print('static uint32_t AppleModelYear[][APPLE_MODEL_YEAR_MAX] = {', file=fh)
|
|
for info in db:
|
|
print(f" /* {info['SystemProductName']:14} */ {{{', '.join(str(year) for year in info['AppleModelYear'])}}},", file=fh)
|
|
|
|
print('};\n', file=fh)
|
|
|
|
print('static uint32_t ApplePreferredModelYear[] = {', file=fh)
|
|
for info in db:
|
|
print(f" /* {info['SystemProductName']:14} */ {info.get('MacserialModelYear', 0)},", file=fh)
|
|
|
|
print('};\n', file=fh)
|
|
|
|
print('static APPLE_MODEL_DESC AppleModelDesc[] = {', file=fh)
|
|
|
|
models = sorted(dbpd.keys())
|
|
models.sort(key=len)
|
|
|
|
for model in models:
|
|
if dbpd[model][update_products.KEY_STATUS] == update_products.STATUS_OK:
|
|
print(f""" {{"{model}", "{remove_accents(dbpd[model][update_products.KEY_NAME])}"}},""", file=fh)
|
|
|
|
print('};\n', file=fh)
|
|
|
|
print('#endif // GENSERIAL_MODELINFO_AUTOGEN_H', file=fh)
|
|
|
|
|
|
def export_mlb_boards(db, boards):
|
|
|
|
mlb = {}
|
|
for info in db:
|
|
if len(info['SystemSerialNumber']) == 12:
|
|
models = [info['BoardProduct']] if not isinstance(info['BoardProduct'], list) else info['BoardProduct']
|
|
|
|
for model in models:
|
|
mlb[model] = 'latest' if not info['MaximumOSVersion'] else info['MaximumOSVersion']
|
|
|
|
with open(boards, 'w', encoding='utf-8') as fh:
|
|
json.dump(mlb, fh, indent=1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
db = load_db('DataBase')
|
|
dbpd = update_products.load_products()
|
|
# Run test phase to validate the library
|
|
validate_products(db, dbpd)
|
|
export_db_macinfolib(db, os.devnull)
|
|
export_db_macserial(db, dbpd, os.devnull)
|
|
# Export new models
|
|
export_db_macinfolib(db, '../Library/OcMacInfoLib/AutoGenerated.c')
|
|
export_db_macserial(db, dbpd, '../Utilities/macserial/modelinfo_autogen.h')
|
|
# Export MLB models
|
|
export_mlb_boards(db, '../Utilities/macrecovery/boards.json')
|