feat: Enhance Anisette API with WASM file handling and provisioning improvements
This commit is contained in:
@@ -2,7 +2,11 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(cat:*)",
|
"Bash(cat:*)",
|
||||||
"Bash(mkdir:*)"
|
"Bash(mkdir:*)",
|
||||||
|
"mcp__ide__getDiagnostics",
|
||||||
|
"Bash(bun run:*)",
|
||||||
|
"Bash(bun:*)",
|
||||||
|
"Bash(NODE_TLS_REJECT_UNAUTHORIZED=0 bun:*)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
@@ -31,18 +31,27 @@ const storeservicesPath = args[0];
|
|||||||
const coreadiPath = args[1];
|
const coreadiPath = args[1];
|
||||||
const libraryPath = args[2] ?? "./anisette/";
|
const libraryPath = args[2] ?? "./anisette/";
|
||||||
|
|
||||||
const wasmModule = await loadWasm();
|
const wasmModule = await loadWasm({ printErr: () => {}});
|
||||||
|
|
||||||
const storeservices = new Uint8Array(await fs.readFile(storeservicesPath));
|
const storeservices = new Uint8Array(await fs.readFile(storeservicesPath));
|
||||||
const coreadi = new Uint8Array(await fs.readFile(coreadiPath));
|
const coreadi = new Uint8Array(await fs.readFile(coreadiPath));
|
||||||
|
|
||||||
|
const readOptional = (p) => fs.readFile(p).then((b) => new Uint8Array(b)).catch(() => {console.warn(`Optional file not found: ${p}`); return null; });
|
||||||
|
const [adiPb, deviceJsonBytes] = await Promise.all([
|
||||||
|
readOptional(path.join(libraryPath, "adi.pb")),
|
||||||
|
readOptional(path.join(libraryPath, "device.json")),
|
||||||
|
]);
|
||||||
|
|
||||||
const anisette = await Anisette.fromSo(storeservices, coreadi, wasmModule, {
|
const anisette = await Anisette.fromSo(storeservices, coreadi, wasmModule, {
|
||||||
init: { libraryPath },
|
init: { libraryPath, adiPb, deviceJsonBytes },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!anisette.isProvisioned) {
|
if (!anisette.isProvisioned) {
|
||||||
console.log("Device not provisioned — running provisioning...");
|
console.log("Device not provisioned — running provisioning...");
|
||||||
await anisette.provision();
|
await anisette.provision();
|
||||||
|
await fs.mkdir(libraryPath, { recursive: true });
|
||||||
|
await fs.writeFile(path.join(libraryPath, "adi.pb"), anisette.getAdiPb());
|
||||||
|
await fs.writeFile(path.join(libraryPath, "device.json"), anisette.getDeviceJson());
|
||||||
console.log("Provisioning complete.");
|
console.log("Provisioning complete.");
|
||||||
} else {
|
} else {
|
||||||
console.log("Device already provisioned.");
|
console.log("Device already provisioned.");
|
||||||
@@ -50,3 +59,6 @@ if (!anisette.isProvisioned) {
|
|||||||
|
|
||||||
const headers = await anisette.getData();
|
const headers = await anisette.getData();
|
||||||
console.log(JSON.stringify(headers, null, 2));
|
console.log(JSON.stringify(headers, null, 2));
|
||||||
|
|
||||||
|
|
||||||
|
console.log(JSON.stringify(await anisette.getData(), null, 2));
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "bun build src/index.ts --outfile dist/anisette.js --target node --format esm --minify",
|
"build": "bun build src/index.ts --outfile ../dist/anisette.js --target node --format esm --minify-syntax --minify-whitespace",
|
||||||
"build:cjs": "bun build src/index.ts --outfile dist/anisette.cjs --target node --format cjs --minify",
|
"build:cjs": "bun build src/index.ts --outfile dist/anisette.cjs --target node --format cjs --minify",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Main Anisette class — the public-facing API
|
// Main Anisette class — the public-facing API
|
||||||
|
|
||||||
import type { AnisetteDeviceConfig, AnisetteHeaders, InitOptions } from "./types.js";
|
import type { AnisetteHeaders, InitOptions } from "./types.js";
|
||||||
import type { HttpClient } from "./http.js";
|
import type { HttpClient } from "./http.js";
|
||||||
import { WasmBridge } from "./wasm-bridge.js";
|
import { WasmBridge } from "./wasm-bridge.js";
|
||||||
import { Device } from "./device.js";
|
import { Device } from "./device.js";
|
||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
detectLocale,
|
detectLocale,
|
||||||
encodeUtf8,
|
encodeUtf8,
|
||||||
} from "./utils.js";
|
} from "./utils.js";
|
||||||
import type { DeviceJson } from "./types.js";
|
|
||||||
|
|
||||||
const DEFAULT_DSID = BigInt(-2);
|
const DEFAULT_DSID = BigInt(-2);
|
||||||
const DEFAULT_LIBRARY_PATH = "./anisette/";
|
const DEFAULT_LIBRARY_PATH = "./anisette/";
|
||||||
@@ -30,25 +29,39 @@ export interface AnisetteOptions {
|
|||||||
export class Anisette {
|
export class Anisette {
|
||||||
private bridge: WasmBridge;
|
private bridge: WasmBridge;
|
||||||
private device: Device;
|
private device: Device;
|
||||||
private libs: LibraryStore;
|
|
||||||
private provisioning: ProvisioningSession;
|
private provisioning: ProvisioningSession;
|
||||||
private dsid: bigint;
|
private dsid: bigint;
|
||||||
|
private provisioningPath: string;
|
||||||
private libraryPath: string;
|
private libraryPath: string;
|
||||||
|
private libs: LibraryStore;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
private wasmModule: any;
|
||||||
|
private identifier: string;
|
||||||
|
private httpClient: HttpClient | undefined;
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
bridge: WasmBridge,
|
bridge: WasmBridge,
|
||||||
device: Device,
|
device: Device,
|
||||||
libs: LibraryStore,
|
|
||||||
provisioning: ProvisioningSession,
|
provisioning: ProvisioningSession,
|
||||||
dsid: bigint,
|
dsid: bigint,
|
||||||
libraryPath: string
|
provisioningPath: string,
|
||||||
|
libraryPath: string,
|
||||||
|
libs: LibraryStore,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
wasmModule: any,
|
||||||
|
identifier: string,
|
||||||
|
httpClient: HttpClient | undefined,
|
||||||
) {
|
) {
|
||||||
this.bridge = bridge;
|
this.bridge = bridge;
|
||||||
this.device = device;
|
this.device = device;
|
||||||
this.libs = libs;
|
|
||||||
this.provisioning = provisioning;
|
this.provisioning = provisioning;
|
||||||
this.dsid = dsid;
|
this.dsid = dsid;
|
||||||
|
this.provisioningPath = provisioningPath;
|
||||||
this.libraryPath = libraryPath;
|
this.libraryPath = libraryPath;
|
||||||
|
this.libs = libs;
|
||||||
|
this.wasmModule = wasmModule;
|
||||||
|
this.identifier = identifier;
|
||||||
|
this.httpClient = httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- factory methods ----
|
// ---- factory methods ----
|
||||||
@@ -83,13 +96,19 @@ export class Anisette {
|
|||||||
const dsid = options.dsid ?? DEFAULT_DSID;
|
const dsid = options.dsid ?? DEFAULT_DSID;
|
||||||
|
|
||||||
// Load or generate device config
|
// Load or generate device config
|
||||||
const device = Device.fromJson(null, initOpts.deviceConfig);
|
const savedDeviceJson = initOpts.deviceJsonBytes
|
||||||
|
? (() => { try { return JSON.parse(new TextDecoder().decode(initOpts.deviceJsonBytes)) as import("./types.js").DeviceJson; } catch { return null; } })()
|
||||||
|
: null;
|
||||||
|
const device = Device.fromJson(savedDeviceJson, initOpts.deviceConfig);
|
||||||
|
|
||||||
// Write device.json into WASM VFS so the emulator can read it
|
// Restore adi.pb into VFS if provided
|
||||||
const deviceJson = device.toJson();
|
if (initOpts.adiPb) {
|
||||||
const deviceJsonBytes = encodeUtf8(JSON.stringify(deviceJson, null, 2));
|
bridge.writeVirtualFile(joinPath(provisioningPath, "adi.pb"), initOpts.adiPb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write device.json into WASM VFS
|
||||||
|
const deviceJsonBytes = initOpts.deviceJsonBytes ?? encodeUtf8(JSON.stringify(device.toJson(), null, 2));
|
||||||
bridge.writeVirtualFile(joinPath(libraryPath, "device.json"), deviceJsonBytes);
|
bridge.writeVirtualFile(joinPath(libraryPath, "device.json"), deviceJsonBytes);
|
||||||
|
|
||||||
// Initialize WASM ADI
|
// Initialize WASM ADI
|
||||||
bridge.initFromBlobs(
|
bridge.initFromBlobs(
|
||||||
libs.storeservicescore,
|
libs.storeservicescore,
|
||||||
@@ -105,58 +124,9 @@ export class Anisette {
|
|||||||
options.httpClient
|
options.httpClient
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Anisette(bridge, device, libs, provisioning, dsid, libraryPath);
|
const identifier = initOpts.identifier ?? device.adiIdentifier;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return new Anisette(bridge, device, provisioning, dsid, provisioningPath, libraryPath, libs, wasmModule, identifier, options.httpClient);
|
||||||
* Load a previously saved session (device.json + adi.pb written back into VFS).
|
|
||||||
* Pass the saved device.json and adi.pb bytes alongside the library blobs.
|
|
||||||
*/
|
|
||||||
static async fromSaved(
|
|
||||||
storeservicescore: Uint8Array,
|
|
||||||
coreadi: Uint8Array,
|
|
||||||
deviceJsonBytes: Uint8Array,
|
|
||||||
adiPbBytes: Uint8Array,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
wasmModule: any,
|
|
||||||
options: AnisetteOptions = {}
|
|
||||||
): Promise<Anisette> {
|
|
||||||
const bridge = new WasmBridge(wasmModule);
|
|
||||||
const initOpts = options.init ?? {};
|
|
||||||
const libraryPath = initOpts.libraryPath ?? DEFAULT_LIBRARY_PATH;
|
|
||||||
const provisioningPath = initOpts.provisioningPath ?? libraryPath;
|
|
||||||
const dsid = options.dsid ?? DEFAULT_DSID;
|
|
||||||
|
|
||||||
// Parse saved device config
|
|
||||||
let deviceJson: DeviceJson | null = null;
|
|
||||||
try {
|
|
||||||
deviceJson = JSON.parse(new TextDecoder().decode(deviceJsonBytes)) as DeviceJson;
|
|
||||||
} catch {
|
|
||||||
// ignore parse errors — will generate fresh device
|
|
||||||
}
|
|
||||||
const device = Device.fromJson(deviceJson, initOpts.deviceConfig);
|
|
||||||
|
|
||||||
// Restore VFS files
|
|
||||||
bridge.writeVirtualFile(joinPath(libraryPath, "device.json"), deviceJsonBytes);
|
|
||||||
bridge.writeVirtualFile(joinPath(libraryPath, "adi.pb"), adiPbBytes);
|
|
||||||
|
|
||||||
const libs = LibraryStore.fromBlobs(storeservicescore, coreadi);
|
|
||||||
|
|
||||||
bridge.initFromBlobs(
|
|
||||||
libs.storeservicescore,
|
|
||||||
libs.coreadi,
|
|
||||||
libraryPath,
|
|
||||||
provisioningPath,
|
|
||||||
initOpts.identifier ?? device.adiIdentifier
|
|
||||||
);
|
|
||||||
|
|
||||||
const provisioning = new ProvisioningSession(
|
|
||||||
bridge,
|
|
||||||
device,
|
|
||||||
options.httpClient
|
|
||||||
);
|
|
||||||
|
|
||||||
return new Anisette(bridge, device, libs, provisioning, dsid, libraryPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- public API ----
|
// ---- public API ----
|
||||||
@@ -171,8 +141,22 @@ export class Anisette {
|
|||||||
await this.provisioning.provision(this.dsid);
|
await this.provisioning.provision(this.dsid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Read adi.pb from the WASM VFS for persistence. */
|
||||||
|
getAdiPb(): Uint8Array {
|
||||||
|
return this.bridge.readVirtualFile(joinPath(this.provisioningPath, "adi.pb"));
|
||||||
|
}
|
||||||
|
|
||||||
/** Generate Anisette headers. Throws if not provisioned. */
|
/** Generate Anisette headers. Throws if not provisioned. */
|
||||||
async getData(): Promise<AnisetteHeaders> {
|
async getData(): Promise<AnisetteHeaders> {
|
||||||
|
// Reinit WASM state before each call to avoid emulator corruption on repeated use
|
||||||
|
const adiPb = this.bridge.readVirtualFile(joinPath(this.provisioningPath, "adi.pb"));
|
||||||
|
const deviceJsonBytes = encodeUtf8(JSON.stringify(this.device.toJson(), null, 2));
|
||||||
|
this.bridge = new WasmBridge(this.wasmModule);
|
||||||
|
this.bridge.writeVirtualFile(joinPath(this.provisioningPath, "adi.pb"), adiPb);
|
||||||
|
this.bridge.writeVirtualFile(joinPath(this.libraryPath, "device.json"), deviceJsonBytes);
|
||||||
|
this.bridge.initFromBlobs(this.libs.storeservicescore, this.libs.coreadi, this.libraryPath, this.provisioningPath, this.identifier);
|
||||||
|
this.provisioning = new ProvisioningSession(this.bridge, this.device, this.httpClient);
|
||||||
|
|
||||||
const { otp, machineId } = this.bridge.requestOtp(this.dsid);
|
const { otp, machineId } = this.bridge.requestOtp(this.dsid);
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ export interface InitOptions {
|
|||||||
identifier?: string;
|
identifier?: string;
|
||||||
/** Override parts of the generated device config */
|
/** Override parts of the generated device config */
|
||||||
deviceConfig?: Partial<AnisetteDeviceConfig>;
|
deviceConfig?: Partial<AnisetteDeviceConfig>;
|
||||||
|
/** Existing adi.pb bytes to restore into the WASM VFS (for resuming a provisioned session) */
|
||||||
|
adiPb?: Uint8Array;
|
||||||
|
/** Existing device.json bytes to restore into the WASM VFS */
|
||||||
|
deviceJsonBytes?: Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Raw device.json structure as stored on disk / in WASM VFS */
|
/** Raw device.json structure as stored on disk / in WASM VFS */
|
||||||
|
|||||||
@@ -100,6 +100,22 @@ export class WasmBridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a file from the WASM virtual filesystem.
|
||||||
|
*/
|
||||||
|
readVirtualFile(filePath: string): Uint8Array {
|
||||||
|
const pathPtr = this.allocCString(filePath);
|
||||||
|
try {
|
||||||
|
const result = this.m._anisette_fs_read_file(pathPtr) as number;
|
||||||
|
this.check(result, `anisette_fs_read_file(${filePath})`);
|
||||||
|
} finally {
|
||||||
|
this.free(pathPtr);
|
||||||
|
}
|
||||||
|
const ptr = this.m._anisette_fs_read_ptr() as number;
|
||||||
|
const len = this.m._anisette_fs_read_len() as number;
|
||||||
|
return this.readBytes(ptr, len);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a file into the WASM virtual filesystem.
|
* Write a file into the WASM virtual filesystem.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,27 +1,7 @@
|
|||||||
// Loads the Emscripten WASM glue bundled alongside this file.
|
// @ts-expect-error — glue file is generated, no types available
|
||||||
// The .wasm binary is resolved relative to this JS file at runtime.
|
|
||||||
|
|
||||||
// @ts-ignore — glue file is generated, no types available
|
|
||||||
import ModuleFactory from "../../dist/anisette_rs.node.js";
|
import ModuleFactory from "../../dist/anisette_rs.node.js";
|
||||||
import { createRequire } from "node:module";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
import path from "node:path";
|
|
||||||
|
|
||||||
// Resolve the .wasm file next to the bundled output JS
|
|
||||||
function resolveWasmPath(outputFile: string): string {
|
|
||||||
// __filename of the *bundled* output — bun sets import.meta.url correctly
|
|
||||||
const dir = path.dirname(fileURLToPath(import.meta.url));
|
|
||||||
return path.join(dir, outputFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export async function loadWasm(): Promise<any> {
|
export async function loadWasm(moduleOverrides?: Record<string, any>): Promise<any> {
|
||||||
return ModuleFactory({
|
return ModuleFactory({ ...moduleOverrides });
|
||||||
locateFile(file: string) {
|
|
||||||
if (file.endsWith(".wasm")) {
|
|
||||||
return resolveWasmPath("anisette_rs.node.wasm");
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build:unicorn": "bash script/rebuild-unicorn.sh",
|
"build:unicorn": "bash script/rebuild-unicorn.sh",
|
||||||
"build:glue": "bash script/build-glue.sh",
|
"build:glue": "bash script/build-glue.sh",
|
||||||
"build:release": "bash script/build-glue.sh --release",
|
|
||||||
"build:js": "cd js && bun install && bun run build",
|
"build:js": "cd js && bun install && bun run build",
|
||||||
"build": "bash script/build-glue.sh && cd js && bun install && bun run build",
|
"build": "bash script/build-glue.sh && cd js && bun install && bun run build",
|
||||||
|
"release": "bash script/build-glue.sh --release && cd js && bun install && bun run build",
|
||||||
"run:node": "node example/run-node.mjs",
|
"run:node": "node example/run-node.mjs",
|
||||||
"run:api": "node example/anisette-api.mjs"
|
"run:api": "node example/anisette-api.mjs"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ NODE_DIST_WASM="${DIST_DIR}/anisette_rs.node.wasm"
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
WEB_EXPORTED_FUNCTIONS='["_malloc","_free","_anisette_init_from_blobs","_anisette_is_machine_provisioned","_anisette_start_provisioning","_anisette_end_provisioning","_anisette_request_otp","_anisette_get_cpim_ptr","_anisette_get_cpim_len","_anisette_get_session","_anisette_get_otp_ptr","_anisette_get_otp_len","_anisette_get_mid_ptr","_anisette_get_mid_len","_anisette_last_error_ptr","_anisette_last_error_len","_anisette_fs_write_file","_anisette_idbfs_init","_anisette_idbfs_sync","_anisette_set_identifier","_anisette_set_provisioning_path"]'
|
WEB_EXPORTED_FUNCTIONS='["_malloc","_free","_anisette_init_from_blobs","_anisette_is_machine_provisioned","_anisette_start_provisioning","_anisette_end_provisioning","_anisette_request_otp","_anisette_get_cpim_ptr","_anisette_get_cpim_len","_anisette_get_session","_anisette_get_otp_ptr","_anisette_get_otp_len","_anisette_get_mid_ptr","_anisette_get_mid_len","_anisette_last_error_ptr","_anisette_last_error_len","_anisette_fs_write_file","_anisette_fs_read_file","_anisette_fs_read_ptr","_anisette_fs_read_len","_anisette_idbfs_sync","_anisette_set_identifier","_anisette_set_provisioning_path"]'
|
||||||
NODE_EXPORTED_FUNCTIONS='["_malloc","_free","_anisette_init_from_blobs","_anisette_is_machine_provisioned","_anisette_start_provisioning","_anisette_end_provisioning","_anisette_request_otp","_anisette_get_cpim_ptr","_anisette_get_cpim_len","_anisette_get_session","_anisette_get_otp_ptr","_anisette_get_otp_len","_anisette_get_mid_ptr","_anisette_get_mid_len","_anisette_last_error_ptr","_anisette_last_error_len","_anisette_fs_write_file","_anisette_set_identifier","_anisette_set_provisioning_path"]'
|
NODE_EXPORTED_FUNCTIONS='["_malloc","_free","_anisette_init_from_blobs","_anisette_is_machine_provisioned","_anisette_start_provisioning","_anisette_end_provisioning","_anisette_request_otp","_anisette_get_cpim_ptr","_anisette_get_cpim_len","_anisette_get_session","_anisette_get_otp_ptr","_anisette_get_otp_len","_anisette_get_mid_ptr","_anisette_get_mid_len","_anisette_last_error_ptr","_anisette_last_error_len","_anisette_fs_write_file","_anisette_fs_read_file","_anisette_fs_read_ptr","_anisette_fs_read_len","_anisette_set_identifier","_anisette_set_provisioning_path"]'
|
||||||
WEB_EXPORTED_RUNTIME_METHODS='["FS","HEAPU8","UTF8ToString","stringToUTF8","lengthBytesUTF8"]'
|
WEB_EXPORTED_RUNTIME_METHODS='["FS","HEAPU8","UTF8ToString","stringToUTF8","lengthBytesUTF8"]'
|
||||||
NODE_EXPORTED_RUNTIME_METHODS='["HEAPU8","UTF8ToString","stringToUTF8","lengthBytesUTF8"]'
|
NODE_EXPORTED_RUNTIME_METHODS='["HEAPU8","UTF8ToString","stringToUTF8","lengthBytesUTF8"]'
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ if command -v bun >/dev/null 2>&1 && [[ -f "${JS_DIR}/src/index.ts" ]]; then
|
|||||||
--outfile "${DIST_DIR}/anisette.js" \
|
--outfile "${DIST_DIR}/anisette.js" \
|
||||||
--target node \
|
--target node \
|
||||||
--format esm \
|
--format esm \
|
||||||
--minify
|
--minify-syntax --minify-whitespace
|
||||||
echo " ${DIST_DIR}/anisette.js"
|
echo " ${DIST_DIR}/anisette.js"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::ffi::{CStr, c_char};
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::{Adi, AdiInit, init_idbfs_for_path, sync_idbfs};
|
use crate::{Adi, AdiInit, sync_idbfs};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ExportState {
|
struct ExportState {
|
||||||
@@ -13,6 +13,7 @@ struct ExportState {
|
|||||||
session: u32,
|
session: u32,
|
||||||
otp: Vec<u8>,
|
otp: Vec<u8>,
|
||||||
mid: Vec<u8>,
|
mid: Vec<u8>,
|
||||||
|
read_buf: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
@@ -407,15 +408,15 @@ pub extern "C" fn anisette_fs_write_file(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn anisette_idbfs_init(path: *const c_char) -> i32 {
|
pub extern "C" fn anisette_fs_read_file(path: *const c_char) -> i32 {
|
||||||
let result = (|| -> Result<(), String> {
|
let result = (|| -> Result<Vec<u8>, String> {
|
||||||
let path = unsafe { c_string(path)? };
|
let path = unsafe { c_string(path)? };
|
||||||
init_idbfs_for_path(&path)?;
|
fs::read(&path).map_err(|e| format!("failed to read '{path}': {e}"))
|
||||||
Ok(())
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(()) => {
|
Ok(data) => {
|
||||||
|
STATE.with(|state| state.borrow_mut().read_buf = data);
|
||||||
clear_last_error();
|
clear_last_error();
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@@ -426,6 +427,16 @@ pub extern "C" fn anisette_idbfs_init(path: *const c_char) -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn anisette_fs_read_ptr() -> *const u8 {
|
||||||
|
STATE.with(|state| state.borrow().read_buf.as_ptr())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn anisette_fs_read_len() -> usize {
|
||||||
|
STATE.with(|state| state.borrow().read_buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn anisette_idbfs_sync(populate_from_storage: i32) -> i32 {
|
pub extern "C" fn anisette_idbfs_sync(populate_from_storage: i32) -> i32 {
|
||||||
let result = sync_idbfs(populate_from_storage != 0);
|
let result = sync_idbfs(populate_from_storage != 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user