This code creates an index of Quake 3 maps stored in .pk3dir
archives, listing each map and its associated files. It generates a structured JSON manifest for each archive, making it easier to manage and access the maps.
npm run import -- "make pk3 indexes"
var fs = require('fs')
var os = require('os')
var path = require('path')
var glob = require('glob')
var DEFAULT_PATH = path.join(process.env.HOME || process.env.HOMEPATH
|| process.env.USERPROFILE || os.tmpdir(), '/.quake3/bestmaps-cc')
async function makePk3MapIndex(searchPath, prefixPath) {
if(typeof searchPath == 'undefined' || !searchPath) {
searchPath = DEFAULT_PATH
}
if(typeof prefixPath == 'undefined') {
prefixPath = '/base/baseq3-cc'
} else if (!prefixPath) {
prefixPath = '/'
}
// TODO: find files in dir based on alt-filetypes and names listed in pk3 file,
// so a combined directory of files, but separate indexes
var pk3dirs = glob.sync('**/*.pk3dir/', {
nodir: false, cwd: searchPath, nocase: true
})
var allMaps = {}
pk3dirs.forEach(dir => {
var pk3path = path.join(searchPath, dir)
var pk3files = glob.sync('**/*', {
nodir: false, cwd: pk3path, nocase: true
})
var maps = pk3files.filter(file => file.match(/\.bsp$/i))
var pk3Key = path.join(prefixPath, dir).toLowerCase()
var initial = {}
allMaps[pk3Key] = initial[pk3Key] = {
name: path.join('/', dir).replace(/\/$/ig, '')
}
var manifest = pk3files.map(file => {
var stat = fs.statSync(path.join(searchPath, dir, file))
return stat.isDirectory() ? ({
name: path.join('/', dir, file).replace(/\/$/ig, ''),
}) : ({
name: path.join('/', dir, file),
size: stat.size
})
}).reduce((obj, o) => {
var key = path.join(prefixPath, o.name).toLowerCase()
+ (typeof o.size == 'undefined' ? '/' : '')
obj[key] = o
return obj
}, initial)
var manifestJson = JSON.stringify(manifest, null, 2)
maps.forEach(map => {
var mapName = path.basename(map).toLowerCase().replace(/\.bsp/i, '')
var outIndexFile = path.join(searchPath, 'index-' + mapName + '.json')
var key = path.join(prefixPath, path.basename(pk3path), map).toLowerCase()
fs.writeFileSync(outIndexFile, manifestJson)
allMaps[key] = {
name: path.join('/', dir, map),
size: fs.statSync(path.join(searchPath, dir, map)).size
}
})
})
return JSON.stringify(allMaps, null, 2)
}
module.exports = makePk3MapIndex
const fs = require('fs').promises;
const path = require('path');
const glob = require('glob');
const os = require('os');
const DEFAULT_PATH = path.join(
process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE || os.tmpdir(),
'/.quake3/bestmaps-cc'
);
/**
* Creates a JSON index of maps from PK3 directories.
*
* @param {string} searchPath - The path to search for PK3 directories (default: DEFAULT_PATH).
* @param {string} prefixPath - The prefix to use for keys in the output JSON (default: '/base/baseq3-cc').
* @returns {Promise<string>} A JSON string representing the map index.
*/
async function makePk3MapIndex(searchPath = DEFAULT_PATH, prefixPath = '/base/baseq3-cc') {
// Resolve the search path
searchPath = path.resolve(searchPath);
// Find all PK3 directories
const pk3Dirs = glob.sync('**/*.pk3dir/', {
nodir: false,
cwd: searchPath,
nocase: true,
});
const allMaps = {};
// Process each PK3 directory
for (const dir of pk3Dirs) {
// Get the absolute path of the PK3 directory
const pk3Path = path.join(searchPath, dir);
// Find all files in the PK3 directory
const files = await glob('**/*', {
nodir: false,
cwd: pk3Path,
nocase: true,
});
// Filter the files to only include bsp files
const maps = files.filter(file => file.match(/\.bsp$/i));
// Create a key for the PK3 directory in the output JSON
const pk3Key = path.join(prefixPath, dir).toLowerCase();
// Create an object to store the files in the PK3 directory
const manifest = files.reduce((obj, file) => {
const filePath = path.join(pk3Path, file);
const stat = await fs.stat(filePath);
const key = path.join(prefixPath, file).toLowerCase() + (stat.isDirectory()? '/' : '');
obj[key] = {
name: path.join('/', dir, file).replace(/\/$/ig, ''),
...(stat.isDirectory()? {} : { size: stat.size }),
};
return obj;
}, {});
// Create a JSON string for the manifest
const manifestJson = JSON.stringify(manifest, null, 2);
// Process each bsp file
for (const map of maps) {
// Get the name of the bsp file
const mapName = path.basename(map).toLowerCase().replace(/\.bsp/i, '');
// Create a key for the bsp file in the output JSON
const mapKey = path.join(prefixPath, path.basename(pk3Path), map).toLowerCase();
// Create an index file for the bsp file
const indexFile = path.join(searchPath, `index-${mapName}.json`);
await fs.writeFile(indexFile, manifestJson);
// Add the bsp file to the output JSON
allMaps[mapKey] = {
name: path.join('/', dir, map),
size: await fs.stat(path.join(searchPath, dir, map)).size,
};
}
}
// Return the output JSON as a string
return JSON.stringify(allMaps, null, 2);
}
module.exports = makePk3MapIndex;
This code generates a map index for Quake 3 maps stored in .pk3dir
archives.
Here's a breakdown:
Initialization:
makePk3MapIndex
that takes optional searchPath
and prefixPath
arguments.Finding PK3 Directories:
glob.sync
to find all .pk3dir
files within the specified searchPath
.Processing Each PK3 Directory:
.pk3dir
found, it:
glob.sync
..bsp
files (Quake 3 maps)..pk3dir
, storing its name and a list of its files.Generating Manifest:
.pk3dir
, including their names and sizes.Populating Map Index:
.bsp
files found in the .pk3dir
.Outputting Index:
In essence, this code automates the process of creating a structured index of Quake 3 maps stored in .pk3dir
archives, making it easier to manage and access them.