mirror of
https://github.com/dualshock-tools/dualshock-tools.github.io.git
synced 2026-03-01 11:19:54 +03:00
264 lines
7.3 KiB
JavaScript
264 lines
7.3 KiB
JavaScript
'use strict';
|
|
|
|
import DS5Controller from './ds5-controller.js';
|
|
import { sleep, dec2hex32, la, lf } from '../utils.js';
|
|
import { l } from "../translations.js";
|
|
|
|
/**
|
|
* DualSense Edge (DS5 Edge) Controller implementation
|
|
*/
|
|
class DS5EdgeController extends DS5Controller {
|
|
constructor(device) {
|
|
super(device);
|
|
this.model = "DS5_Edge";
|
|
this.finetuneMaxValue = 4095; // 12-bit max value for DS5 Edge
|
|
}
|
|
|
|
async getInfo() {
|
|
// DS5 Edge uses the same info structure as DS5 but with is_edge=true
|
|
const result = await this._getInfo(true);
|
|
|
|
if (result.ok) {
|
|
// DS Edge extra module info
|
|
const empty = Array(17).fill('\x00').join('');
|
|
try {
|
|
const sticks_barcode = (await this.getBarcode()).map(barcode => barcode === empty ? l("Unknown") : barcode);
|
|
result.infoItems.push({ key: l("Left Module Barcode"), value: sticks_barcode[1], cat: "fw" });
|
|
result.infoItems.push({ key: l("Right Module Barcode"), value: sticks_barcode[0], cat: "fw" });
|
|
} catch(_e) {
|
|
// ignore module read errors here
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
async flash(progressCallback = null) {
|
|
la("ds5_edge_flash");
|
|
try {
|
|
const ret = await this.flashModules(progressCallback);
|
|
if(ret) {
|
|
return {
|
|
success: true,
|
|
message: "<b>" + l("Changes saved successfully") + "</b>.<br><br>" + l("If the calibration is not stored permanently, please double-check the wirings of the hardware mod."),
|
|
isHtml: true
|
|
};
|
|
}
|
|
} catch(error) {
|
|
throw new Error(l("Error while saving changes"), { cause: error });
|
|
}
|
|
}
|
|
|
|
async getBarcode() {
|
|
try {
|
|
const td = new TextDecoder();
|
|
|
|
await this.sendFeatureReport(0x80, [21,34,0]);
|
|
await sleep(100);
|
|
|
|
const r_data = await this.receiveFeatureReport(0x81);
|
|
const r_bc = td.decode(r_data.buffer.slice(21, 21+17));
|
|
|
|
await this.sendFeatureReport(0x80, [21,34,1]);
|
|
await sleep(100);
|
|
|
|
const l_data = await this.receiveFeatureReport(0x81);
|
|
const l_bc = td.decode(l_data.buffer.slice(21, 21+17));
|
|
return [l_bc, r_bc];
|
|
} catch(error) {
|
|
la("ds5_edge_barcode_modules_failed", {"r": error});
|
|
throw new Error(l("Cannot read module barcodes"), { cause: error });
|
|
}
|
|
}
|
|
|
|
async unlockModule(i) {
|
|
const m_name = i == 0 ? "left module" : "right module";
|
|
|
|
await this.sendFeatureReport(0x80, [21, 6, i, 11]);
|
|
await sleep(200);
|
|
const ret = await this.waitUntilWritten([21, 6, 2]);
|
|
if(!ret) {
|
|
throw new Error(l("Cannot unlock") + " " + l(m_name));
|
|
}
|
|
}
|
|
|
|
async lockModule(i) {
|
|
const m_name = i == 0 ? "left module" : "right module";
|
|
|
|
await this.sendFeatureReport(0x80, [21, 4, i, 8]);
|
|
await sleep(200);
|
|
const ret = await this.waitUntilWritten([21, 4, 2]);
|
|
if(!ret) {
|
|
throw new Error(l("Cannot lock") + " " + l(m_name));
|
|
}
|
|
}
|
|
|
|
async storeDataInto(i) {
|
|
const m_name = i == 0 ? "left module" : "right module";
|
|
|
|
await this.sendFeatureReport(0x80, [21, 5, i]);
|
|
await sleep(200);
|
|
const ret = await this.waitUntilWritten([21, 3, 2]);
|
|
if(!ret) {
|
|
throw new Error(l("Cannot store data into") + " " + l(m_name));
|
|
}
|
|
}
|
|
|
|
async flashModules(progressCallback) {
|
|
la("ds5_edge_flash_modules");
|
|
try {
|
|
progressCallback(0);
|
|
|
|
// Reload data, this ensures correctly writing data in the controller
|
|
await sleep(100);
|
|
progressCallback(10);
|
|
|
|
// Unlock modules
|
|
await this.unlockModule(0);
|
|
progressCallback(15);
|
|
await this.unlockModule(1);
|
|
progressCallback(30);
|
|
|
|
// Unlock NVS
|
|
await this.nvsUnlock();
|
|
await sleep(50);
|
|
progressCallback(45);
|
|
|
|
// This should trigger write into modules
|
|
const data = await this.getInMemoryModuleData();
|
|
await sleep(50);
|
|
progressCallback(60);
|
|
await this.writeFinetuneData(data);
|
|
|
|
// Extra delay
|
|
await sleep(100);
|
|
|
|
// Lock back modules
|
|
await this.lockModule(0);
|
|
progressCallback(80);
|
|
await this.lockModule(1);
|
|
progressCallback(100);
|
|
|
|
// Lock back NVS
|
|
await sleep(100);
|
|
const lockRes = await this.nvsLock();
|
|
if(!lockRes.ok) throw (lockRes.error || new Error("NVS lock failed"));
|
|
|
|
await sleep(250);
|
|
|
|
return true;
|
|
} catch(error) {
|
|
la("ds5_edge_flash_modules_failed", {"r": error});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async waitUntilWritten(expected) {
|
|
let attempts = 0;
|
|
const maxAttempts = 10;
|
|
|
|
while (attempts < maxAttempts) {
|
|
const data = await this.receiveFeatureReport(0x81);
|
|
|
|
// Check if all expected bytes match
|
|
const allMatch = expected.every((expectedByte, i) =>
|
|
data.getUint8(1 + i, true) === expectedByte
|
|
);
|
|
|
|
if (allMatch) {
|
|
return true;
|
|
}
|
|
|
|
attempts++;
|
|
await sleep(50);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
async calibrateSticksEnd() {
|
|
la("ds5_calibrate_sticks_end");
|
|
try {
|
|
// Write
|
|
await this.sendFeatureReport(0x82, [2,1,1]);
|
|
|
|
let data = await this.receiveFeatureReport(0x83);
|
|
|
|
if(data.getUint32(0, false) != 0x83010101) {
|
|
const d1 = dec2hex32(data.getUint32(0, false));
|
|
la("ds5_calibrate_sticks_failed", {"s": 3, d1});
|
|
return { ok: false, code: 4, d1 };
|
|
}
|
|
|
|
await this.sendFeatureReport(0x82, [2,1,1]);
|
|
data = await this.receiveFeatureReport(0x83);
|
|
if(data.getUint32(0, false) != 0x83010103 && data.getUint32(0, false) != 0x83010312) {
|
|
const d1 = dec2hex32(data.getUint32(0, false));
|
|
la("ds5_calibrate_sticks_failed", {"s": 3, d1});
|
|
return { ok: false, code: 5, d1 };
|
|
}
|
|
|
|
return { ok: true };
|
|
} catch(error) {
|
|
la("ds5_calibrate_sticks_end_failed", {"r": error});
|
|
return { ok: false, error };
|
|
}
|
|
}
|
|
|
|
async calibrateRangeEnd() {
|
|
la("ds5_calibrate_range_end");
|
|
try {
|
|
// Write
|
|
await this.sendFeatureReport(0x82, [2,1,2]);
|
|
|
|
// Assert
|
|
let data = await this.receiveFeatureReport(0x83);
|
|
|
|
if(data.getUint32(0, false) != 0x83010201) {
|
|
const d1 = dec2hex32(data.getUint32(0, false));
|
|
la("ds5_calibrate_range_end_failed", {d1});
|
|
return { ok: false, code: 4, d1 };
|
|
}
|
|
|
|
await this.sendFeatureReport(0x82, [2,1,2]);
|
|
data = await this.receiveFeatureReport(0x83)
|
|
if(data.getUint32(0, false) != 0x83010203) {
|
|
const d1 = dec2hex32(data.getUint32(0, false));
|
|
la("ds5_calibrate_range_end_failed", {d1});
|
|
return { ok: false, code: 5, d1 };
|
|
}
|
|
|
|
return { ok: true };
|
|
} catch(error) {
|
|
la("ds5_calibrate_range_end_failed", {"r": error});
|
|
return { ok: false, error };
|
|
}
|
|
}
|
|
|
|
async getInMemoryModuleData() {
|
|
// DualSense Edge
|
|
await this.sendFeatureReport(0x80, [12, 4]);
|
|
await sleep(100);
|
|
const data = await this.receiveFeatureReport(0x81);
|
|
const cmd = data.getUint8(0, true);
|
|
const [p1, p2, p3] = [1, 2, 3].map(i => data.getUint8(i, true));
|
|
|
|
if(cmd != 129 || p1 != 12 || (p2 != 2 && p2 != 4) || p3 != 2)
|
|
return null;
|
|
|
|
return Array.from({ length: 12 }, (_, i) => data.getUint16(4 + i * 2, true));
|
|
}
|
|
|
|
async writeFinetuneData(data) {
|
|
const pkg = data.reduce((acc, val) => acc.concat([val & 0xff, val >> 8]), [12, 1]);
|
|
await this.sendFeatureReport(0x80, pkg)
|
|
}
|
|
|
|
getNumberOfSticks() {
|
|
return 2;
|
|
}
|
|
|
|
}
|
|
|
|
export default DS5EdgeController;
|