219 lines
5.0 KiB
Markdown
219 lines
5.0 KiB
Markdown
# Reforger PAK Unpacker (Node.js)
|
|
|
|
Node.js library to extract files from Arma Reforger `.pak` archive files.
|
|
|
|
## Features
|
|
|
|
- Extract all files from PAK archives
|
|
- Extract specific files by name
|
|
- List all files in an archive
|
|
- Support for Zlib compressed files
|
|
- Async and sync APIs
|
|
- Easy integration into Express.js or other Node.js applications
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
npm install
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Basic Example - Extract All Files
|
|
|
|
```javascript
|
|
const { PakUnpacker } = require('./PakUnpacker');
|
|
|
|
async function extractPak() {
|
|
const unpacker = new PakUnpacker('path/to/file.pak');
|
|
await unpacker.load();
|
|
|
|
console.log(`Found ${unpacker.entries.length} files`);
|
|
|
|
await unpacker.extract('./output');
|
|
console.log('Extraction complete!');
|
|
}
|
|
|
|
extractPak();
|
|
```
|
|
|
|
### List Files in Archive
|
|
|
|
```javascript
|
|
const { PakUnpacker } = require('./PakUnpacker');
|
|
|
|
async function listFiles() {
|
|
const unpacker = new PakUnpacker('path/to/file.pak');
|
|
await unpacker.load();
|
|
|
|
const files = unpacker.listFiles();
|
|
files.forEach(file => {
|
|
console.log(`${file.name} - ${file.size} bytes (${file.compressed ? 'Compressed' : 'Uncompressed'})`);
|
|
});
|
|
}
|
|
|
|
listFiles();
|
|
```
|
|
|
|
### Extract a Specific File
|
|
|
|
```javascript
|
|
const { PakUnpacker } = require('./PakUnpacker');
|
|
|
|
async function extractFile() {
|
|
const unpacker = new PakUnpacker('path/to/file.pak');
|
|
await unpacker.load();
|
|
|
|
const fileData = await unpacker.getFile('config.json');
|
|
|
|
if (fileData) {
|
|
console.log('File content:', fileData.toString('utf8'));
|
|
} else {
|
|
console.log('File not found');
|
|
}
|
|
}
|
|
|
|
extractFile();
|
|
```
|
|
|
|
### Synchronous API
|
|
|
|
```javascript
|
|
const { PakUnpacker } = require('./PakUnpacker');
|
|
|
|
function extractPakSync() {
|
|
const unpacker = new PakUnpacker('path/to/file.pak');
|
|
unpacker.loadSync();
|
|
|
|
console.log(`Found ${unpacker.entries.length} files`);
|
|
|
|
unpacker.extractSync('./output');
|
|
console.log('Extraction complete!');
|
|
}
|
|
|
|
extractPakSync();
|
|
```
|
|
|
|
## API Integration Example
|
|
|
|
### Express.js REST API
|
|
|
|
```javascript
|
|
const express = require('express');
|
|
const { PakUnpacker } = require('./PakUnpacker');
|
|
const app = express();
|
|
|
|
// List all files in a PAK archive
|
|
app.get('/api/pak/:pakName/files', async (req, res) => {
|
|
try {
|
|
const unpacker = new PakUnpacker(`./paks/${req.params.pakName}.pak`);
|
|
await unpacker.load();
|
|
|
|
const files = unpacker.listFiles();
|
|
res.json({ success: true, files });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// Extract a specific file
|
|
app.get('/api/pak/:pakName/extract/:fileName(*)', async (req, res) => {
|
|
try {
|
|
const unpacker = new PakUnpacker(`./paks/${req.params.pakName}.pak`);
|
|
await unpacker.load();
|
|
|
|
const fileData = await unpacker.getFile(req.params.fileName);
|
|
|
|
if (fileData) {
|
|
res.setHeader('Content-Type', 'application/octet-stream');
|
|
res.send(fileData);
|
|
} else {
|
|
res.status(404).json({ success: false, error: 'File not found' });
|
|
}
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
app.listen(3000, () => {
|
|
console.log('Server running on port 3000');
|
|
});
|
|
```
|
|
|
|
## PAK File Format
|
|
|
|
Reforger PAK files have the following structure:
|
|
|
|
1. **FORM Block**: Header with size (Big Endian Int32)
|
|
2. **HEAD Block**: 32 bytes header data
|
|
3. **DATA Block**: Size + data block
|
|
4. **Entries Block**: Directory and file entries
|
|
|
|
### Entry Types
|
|
- **Type 0**: Directory (with child count)
|
|
- **Type 1**: File (with offset, size, original size, compression info)
|
|
|
|
### Compression
|
|
- **None** (0x00): No compression
|
|
- **Zlib** (0x106): Deflate compression with 2-byte Zlib header
|
|
|
|
## API Reference
|
|
|
|
### Class: PakUnpacker
|
|
|
|
#### Constructor
|
|
```javascript
|
|
new PakUnpacker(filePath)
|
|
```
|
|
- `filePath` (string): Path to the .pak file
|
|
|
|
#### Methods
|
|
|
|
##### async load()
|
|
Load and parse the PAK file asynchronously.
|
|
|
|
##### loadSync()
|
|
Load and parse the PAK file synchronously (blocking).
|
|
|
|
##### async extract(dstDir)
|
|
Extract all files to the destination directory.
|
|
- `dstDir` (string): Destination directory path
|
|
|
|
##### extractSync(dstDir)
|
|
Extract all files synchronously.
|
|
- `dstDir` (string): Destination directory path
|
|
|
|
##### async getFile(fileName)
|
|
Get a specific file's data by name.
|
|
- `fileName` (string): Name or partial name of the file
|
|
- Returns: `Promise<Buffer|null>`
|
|
|
|
##### getFileSync(fileName)
|
|
Get a specific file's data synchronously.
|
|
- `fileName` (string): Name or partial name of the file
|
|
- Returns: `Buffer|null`
|
|
|
|
##### listFiles()
|
|
List all files in the PAK archive.
|
|
- Returns: Array of objects with `{name, size, originalSize, compressed}` properties
|
|
|
|
#### Properties
|
|
|
|
- `entries` (Array): Array of PakEntryFile objects
|
|
- `formSize` (number): Size of the FORM block
|
|
- `dataSize` (number): Size of the DATA block
|
|
- `entriesSize` (number): Size of the entries block
|
|
|
|
## Requirements
|
|
|
|
- Node.js >= 14.0.0
|
|
- No external dependencies (uses built-in modules only)
|
|
|
|
## License
|
|
|
|
MIT
|
|
|
|
## Credits
|
|
|
|
Converted from the original C# implementation.
|