quake 3 | list bsps in a pak | Cell 5 | Search

This code provides a utility for extracting files from Quake 3 game archives (.pk3 and .zip) by defining functions to handle individual archive extraction (readPak) and recursive extraction of multiple archives within a directory (extractPaks). This utility enables users to access Quake 3 game assets and resources.

Run example

npm run import -- "unpack pk3s"

unpack pk3s

var StreamZip = require('node-stream-zip');
var fs = require('fs');
var path = require('path');
var importer = require('../Core');
var {globSync} = require('glob')
var mkdirpSync = importer.import("mkdirp")

async function readPak(zipFile, outdir) {
    if(!outdir) {
        outdir = '/Users/briancullinan/.q3a/unpacked/' + path.basename(zipFile) + 'dir'
    }
    const zip = new StreamZip.async({ file: zipFile, skipEntryNameValidation: true })

    const entries = await zip.entries()
    console.log(entries)
    for (const entry of Object.values(entries)) {
        var levelPath = path.join(outdir, entry.name).replace(/\\/g, '/')
        if(fs.existsSync(levelPath)) continue
        if(entry.isDirectory) continue
        mkdirpSync(path.dirname(levelPath))
        await zip.extract(entry.name, levelPath);
    }

    await zip.close();
}


async function extractPaks(inpaks, outdir) {
    let root = path.resolve(path.dirname(inpaks))
    var results = globSync('**', {cwd: root})
    results.sort()
    console.log(path.resolve(path.dirname(inpaks)), results)
    for(let i = 0; i < results.length; i++) {
        if(!results[i].match(/(\.pk3|\.zip)/))
            continue
        if(results[i].match(/\/|\\/))
            await readPak(path.join(root, results[i]), path.join(outdir, path.dirname(results[i])))
        else
            await readPak(path.join(root, results[i]), outdir)
    }

}


module.exports = extractPaks;

What the code could have been:

const fs = require('fs');
const path = require('path');
const glob = require('glob');
const mkdirp = require('mkdirp');
const StreamZip = require('node-stream-zip');

// Function to read a PK3 file and extract its contents to a specified output directory
async function readPak(zipFile, outdir) {
    // If output directory is not specified, generate a default one based on the zip file name
    outdir = outdir || path.join('/Users/briancullinan/.q3a/unpacked/', path.basename(zipFile) + 'dir');

    try {
        // Create a new StreamZip instance with the zip file and skip entry name validation
        const zip = new StreamZip.async({ file: zipFile, skipEntryNameValidation: true });

        // Get the entries in the zip file
        const entries = await zip.entries();

        // Iterate over the entries
        for (const entry of Object.values(entries)) {
            // Construct the full path for the entry
            const levelPath = path.join(outdir, entry.name).replace(/\\/g, '/');

            // If the directory already exists, skip it
            if (fs.existsSync(levelPath)) continue;

            // If the entry is a directory, skip it
            if (entry.isDirectory) continue;

            // Create the directory for the entry if it doesn't exist
            await mkdirp(path.dirname(levelPath));

            // Extract the entry to the output directory
            await zip.extract(entry.name, levelPath);
        }

        // Close the zip file
        await zip.close();
    } catch (error) {
        // Log any errors that occur during the process
        console.error(`Error reading PK3 file: ${error.message}`);
    }
}

// Function to extract all PK3 and ZIP files in a directory and its subdirectories
async function extractPaks(inpaks, outdir) {
    // Get the root directory of the input files
    const root = path.resolve(path.dirname(inpaks));

    // Get all files in the directory and its subdirectories
    const results = glob.sync('**', { cwd: root });

    // Sort the results
    results.sort();

    // Iterate over the results
    for (const file of results) {
        // Check if the file is a PK3 or ZIP file
        if (!file.match(/(\.pk3|\.zip)/)) continue;

        // If the file is a subdirectory, extract its PK3 files
        if (file.match(/\/|\\/)) {
            await readPak(path.join(root, file), path.join(outdir, path.dirname(file)));
        } else {
            // Otherwise, extract its PK3 files to the output directory
            await readPak(path.join(root, file), outdir);
        }
    }
}

module.exports = extractPaks;

This code snippet defines two asynchronous functions, readPak and extractPaks, designed to extract files from Quake 3 game archives (.pk3 and .zip files).

readPak Function:

  1. Initialization:

  2. Extraction:

  3. Cleanup:

extractPaks Function:

  1. Initialization:

  2. Extraction Loop:

  3. Export:

Purpose:

This code snippet provides a utility for extracting files from Quake 3 game archives, allowing users to access individual game assets and resources.