initial commit
This commit is contained in:
218
README.md
Normal file
218
README.md
Normal file
@@ -0,0 +1,218 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user