Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 55 additions & 24 deletions src/processors/obfProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,8 @@ class ObfProcessor extends BaseProcessor {
}

async loadIntoTree(filePathOrBuffer: ProcessorInput): Promise<AACTree> {
const { readBinaryFromInput, readTextFromInput } = this.options.fileAdapter;
const { readBinaryFromInput, readTextFromInput, listDir, join, isDirectory } =
this.options.fileAdapter;
// Detailed logging for debugging input
const bufferLength =
typeof filePathOrBuffer === 'string'
Expand Down Expand Up @@ -467,18 +468,22 @@ class ObfProcessor extends BaseProcessor {
}
}

// Detect likely zip signature first
async function isLikelyZip(input: ProcessorInput): Promise<boolean> {
if (typeof input === 'string') {
const lowered = input.toLowerCase();
return lowered.endsWith('.zip') || lowered.endsWith('.obz');
// Determine if input is ZIP, directory, or OBF JSON string/buffer
let fileType: 'obf' | 'zip' | 'dir' = 'obf';
if (typeof filePathOrBuffer !== 'string') {
const bytes = await readBinaryFromInput(filePathOrBuffer);
if (bytes.length >= 2 && bytes[0] === 0x50 && bytes[1] === 0x4b) fileType = 'zip';
} else {
if (await isDirectory(filePathOrBuffer)) {
fileType = 'dir';
} else {
const lowered = filePathOrBuffer.toLowerCase();
if (lowered.endsWith('.zip') || lowered.endsWith('.obz')) fileType = 'zip';
}
const bytes = await readBinaryFromInput(input);
return bytes.length >= 2 && bytes[0] === 0x50 && bytes[1] === 0x4b;
}

// Check if input is a buffer or string that parses as OBF JSON; throw if neither JSON nor ZIP
if (!(await isLikelyZip(filePathOrBuffer))) {
if (fileType === 'obf') {
const asJson = await tryParseObfJson(filePathOrBuffer);
if (!asJson) throw new Error('Invalid OBF content: not JSON and not ZIP');
console.log('[OBF] Detected buffer/string as OBF JSON');
Expand All @@ -500,20 +505,34 @@ class ObfProcessor extends BaseProcessor {
return tree;
}

try {
this.zipFile = await this.options.zipAdapter(filePathOrBuffer);
} catch (err) {
console.error('[OBF] Error loading ZIP:', err);
throw err;
this.zipFile = {
readFile: async (name: string): Promise<Uint8Array> => {
return await readBinaryFromInput(join(filePathOrBuffer as string, name));
},
listFiles: () => {
throw new Error('Not implemented for directory input');
},
writeFiles: () => {
throw new Error('Not implemented for directory input');
},
};
if (fileType === 'zip') {
try {
this.zipFile = await this.options.zipAdapter(filePathOrBuffer);
} catch (err) {
console.error('[OBF] Error loading ZIP:', err);
throw err;
}
}

// Store the ZIP file reference for image extraction
this.imageCache.clear(); // Clear cache for new file

console.log('[OBF] Detected zip archive, extracting .obf files');
console.log('[OBF] Detected zip archive or directory, extracting .obf files');

// List manifest and OBF files
const filesInZip = this.zipFile.listFiles();
const filesInZip =
fileType === 'zip' ? this.zipFile.listFiles() : await listDir(filePathOrBuffer as string);
const manifestFile = filesInZip.filter((name) => name.toLowerCase() === 'manifest.json');
let obfEntries = filesInZip.filter((name) => name.toLowerCase().endsWith('.obf'));

Expand Down Expand Up @@ -722,7 +741,8 @@ class ObfProcessor extends BaseProcessor {
}

async saveFromTree(tree: AACTree, outputPath: string): Promise<void> {
const { writeTextToPath, writeBinaryToPath, pathExists } = this.options.fileAdapter;
const { writeTextToPath, writeBinaryToPath, pathExists, mkDir, join } =
this.options.fileAdapter;
if (outputPath.endsWith('.obf')) {
// Save as single OBF JSON file
const rootPage = tree.rootId ? tree.getPage(tree.rootId) : Object.values(tree.pages)[0];
Expand Down Expand Up @@ -758,13 +778,24 @@ class ObfProcessor extends BaseProcessor {
name: 'manifest.json',
data: new TextEncoder().encode(JSON.stringify(manifest)),
});
const fileExists = await pathExists(outputPath);
this.zipFile = await this.options.zipAdapter(
fileExists ? outputPath : undefined,
this.options.fileAdapter
);
const zipData = await this.zipFile.writeFiles(files);
await writeBinaryToPath(outputPath, zipData);

if (outputPath.endsWith('.obz') || outputPath.endsWith('.zip')) {
console.log('[OBF] Saving to ZIP file:', outputPath);
const fileExists = await pathExists(outputPath);
this.zipFile = await this.options.zipAdapter(
fileExists ? outputPath : undefined,
this.options.fileAdapter
);
const zipData = await this.zipFile.writeFiles(files);
await writeBinaryToPath(outputPath, zipData);
} else {
console.log('[OBF] Saving to directory:', outputPath);
if (!(await pathExists(outputPath))) await mkDir(outputPath);
for (const file of files) {
const filePath = join(outputPath, file.name);
await writeBinaryToPath(filePath, file.data);
}
}
}
}

Expand Down
Loading