site 1.7.0: builder covers every mod in any combination
Stage 1 (arnoldasr's code) still ships as checked data; stage 2 (our mods) runs the patcher's own Python in the browser via self-hosted Pyodide 0.26.4, byte-identical to the patcher. Added cuenames, played, endwarn, quicksearch, usbautoload; phasemeter REVERSE and WAVEFORM modes included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2f9c182279
commit
f955e37927
19 files changed
+566
-93
No files matched your search
+62
-17
@@ -3,9 +3,11 @@
|
||||
// a new one with the chosen mods out. Nothing is uploaded or downloaded.
|
||||
//
|
||||
// Ports of the patcher's upd.py (update file), the emulator checkout's
|
||||
// lzss_encode.py (packer) and patch_cdj900nxs.py's asset blob; the mods'
|
||||
// bytes are in builder/data.js (tools/make_builder_data.py), with the
|
||||
// SHA-256 of the finished application for every combination.
|
||||
// lzss_encode.py (packer) and patch_cdj900nxs.py's asset blob. Stage 1,
|
||||
// arnoldasr's mods, is in builder/data.js (tools/make_builder_data.py) with
|
||||
// the SHA-256 of its result; stage 2, ours, is the patcher's own Python
|
||||
// (builder/py/bundle.json) run here in Pyodide (builder/pyodide/, served
|
||||
// from this site), so the result is the patcher's byte for byte.
|
||||
(function () {
|
||||
'use strict';
|
||||
var D = window.BUILDER_DATA;
|
||||
@@ -14,7 +16,7 @@
|
||||
var ADDRESS_BYTES = { 0: 2, 1: 2, 2: 3, 3: 4, 5: 2, 6: 3, 7: 4, 8: 3, 9: 2 };
|
||||
var LABEL = '1.32';
|
||||
var UPSTREAM = ['extended_bpm', 'phasemeter', 'advanced_waveforms'];
|
||||
var OURS = ['beatjump', 'cuecolours', 'hotcuekeys', 'ledsaver', 'keynotation', 'credit'];
|
||||
var PY_BASE = 'builder/';
|
||||
|
||||
function fail(msg) { throw new Error(msg); }
|
||||
|
||||
@@ -450,25 +452,68 @@
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------- stage 2: our mods, the patcher's Python in Pyodide ----------
|
||||
var pyReady = null;
|
||||
function loadPython(log) {
|
||||
if (pyReady) return pyReady;
|
||||
log('loading Python for our mods (the first time takes a few seconds)...');
|
||||
pyReady = new Promise(function (resolve, reject) {
|
||||
var sc = document.createElement('script');
|
||||
sc.src = PY_BASE + 'pyodide/pyodide.js';
|
||||
sc.onload = resolve;
|
||||
sc.onerror = function () { reject(new Error('could not load builder/pyodide/pyodide.js')); };
|
||||
document.head.appendChild(sc);
|
||||
}).then(function () {
|
||||
return window.loadPyodide({ indexURL: new URL(PY_BASE + 'pyodide/', document.baseURI).href });
|
||||
}).then(function (py) {
|
||||
return fetch(PY_BASE + 'py/bundle.json').then(function (r) {
|
||||
if (!r.ok) fail('could not load builder/py/bundle.json');
|
||||
return r.json();
|
||||
}).then(function (files) {
|
||||
py.FS.mkdirTree('/cdj/mods/cdj900nxs');
|
||||
Object.keys(files).forEach(function (rel) { py.FS.writeFile('/cdj/' + rel, files[rel]); });
|
||||
py.runPython('import sys\nsys.path[:0] = ["/cdj/mods", "/cdj/mods/cdj900nxs"]\nimport patch_cdj900nxs');
|
||||
return py;
|
||||
});
|
||||
});
|
||||
pyReady.catch(function () { pyReady = null; });
|
||||
return pyReady;
|
||||
}
|
||||
|
||||
function patchOurs(app, stock, names, log) {
|
||||
return loadPython(log).then(function (py) {
|
||||
var t = Date.now();
|
||||
py.globals.set('js_app', app);
|
||||
py.globals.set('js_stock', stock);
|
||||
py.globals.set('js_names', names.join(','));
|
||||
var out = py.runPython([
|
||||
'import patch_cdj900nxs as p',
|
||||
'_names = p.check_names(js_names.split(","))',
|
||||
'_out = p.patch_ours(bytes(js_app.to_bytes()), bytes(js_stock.to_bytes()), _names)',
|
||||
'_out'].join('\n'));
|
||||
var u8 = out.toJs();
|
||||
out.destroy();
|
||||
py.runPython('del js_app, js_stock, _out');
|
||||
log('our mods applied by the patcher\'s own code (' + Math.round((Date.now() - t) / 100) / 10 + ' s)');
|
||||
return u8;
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- the whole build ----------
|
||||
function patchApp(stock, names, log) {
|
||||
var up = UPSTREAM.filter(function (n) { return names.indexOf(n) >= 0; });
|
||||
var ours = OURS.filter(function (n) { return names.indexOf(n) >= 0; });
|
||||
var app = stock;
|
||||
var app = stock, first;
|
||||
if (up.length) {
|
||||
app = applyVariant(app, stock, D.upstream[up.join(',')]);
|
||||
log('arnoldasr\'s mods applied (' + up.join(', ') + ')');
|
||||
first = sha256(app).then(function (h) {
|
||||
if (h !== D.expect[up.join(',')]) fail('arnoldasr\'s part is not the one the patcher makes (SHA-256 ' + h + ')');
|
||||
log('arnoldasr\'s part is exactly the patcher\'s (SHA-256 checked)');
|
||||
});
|
||||
} else {
|
||||
first = Promise.resolve();
|
||||
}
|
||||
if (ours.length) {
|
||||
app = applyVariant(app, stock, D.ours[ours.join(',')]);
|
||||
log('our mods applied (' + ours.join(', ') + ')');
|
||||
}
|
||||
var key = up.concat(ours).join(',');
|
||||
return sha256(app).then(function (h) {
|
||||
if (h !== D.expect[key]) fail('the patched application is not the one the patcher makes (SHA-256 ' + h + ')');
|
||||
log('the patched application is exactly the patcher\'s (SHA-256 checked)');
|
||||
return app;
|
||||
});
|
||||
return first.then(function () { return patchOurs(app, stock, names, log); });
|
||||
}
|
||||
|
||||
function build(raw, names, log) {
|
||||
@@ -496,5 +541,5 @@
|
||||
});
|
||||
}
|
||||
|
||||
window.CDJBuilder = { build: build, UPSTREAM: UPSTREAM, OURS: OURS, lzssPack: lzssPack, unpackLzss: unpackLzss };
|
||||
window.CDJBuilder = { build: build, UPSTREAM: UPSTREAM, lzssPack: lzssPack, unpackLzss: unpackLzss, patchApp: patchApp };
|
||||
})();
|
||||
Reference in new issue
Block a user