This code analyzes Quake map files to extract and list the textures used within them.
npm run import -- "list textures in quake 3 map"
var fs = require('fs')
var path = require('path')
var importer = require('../Core')
var {BitStream} = importer.import("bit buffer")
var MAX_QPATH = 64
var dshader_t = function () {
this.shaderName = null; // byte * MAX_QPATH (string)
this.surfaceFlags = 0; // int32
this.contents = 0; // int32
};
dshader_t.size = 72;
function loadShaders(buffer, fileofs, filelen) {
var bb = new BitStream(buffer);
bb.byteIndex = fileofs;
var shaders = new Array(filelen / dshader_t.size);
for (var i = 0; i < shaders.length; i++) {
var shader = shaders[i] = new dshader_t();
shader.shaderName = bb.readASCIIString(MAX_QPATH);
shader.surfaceFlags = bb.readInt32();
shader.contents = bb.readInt32();
}
return shaders
}
function listTextures(file, dirsOnly) {
if(file.includes('.bsp')) {
file = fs.readFileSync(file)
let bb = new BitStream(file);
bb.readASCIIString(4);
bb.readInt32();
// lump 0
bb.readInt32()
bb.readInt32()
// lump 1 shaders
let fileofs = bb.readInt32()
let filelen = bb.readInt32()
let shaders = loadShaders(file, fileofs, filelen)
return shaders.filter((s, i, arr) => s.shaderName && arr.indexOf(s) == i).map(s => s.shaderName)
}
if(typeof file === 'string' && fs.existsSync(file)) {
file = fs.readFileSync(file).toString('utf-8')
}
var used = []
var brushes = importer.regexToArray(/\{[\s\S^\}\{]*?\}/ig, file, -1) // returns indexes
console.log(`found ${brushes.length} brushes.`)
for(var i = 0; i < brushes.length; i++) {
var b = file.substr(brushes[i][0], brushes[i][1])
var texts = importer.regexToArray(/\)\s+([^\)\(]*?)\s+((\s*[0-9\.-]+){5,8})/igm, b, 1)
texts.forEach($1 => {
if(dirsOnly) {
if(!used.includes(path.dirname($1)))
used.push(path.dirname($1))
} else {
if(!used.includes($1))
used.push($1)
}
})
}
return used
}
module.exports = listTextures
// Import required modules
const fs = require('fs');
const path = require('path');
const { importer } = require('../Core');
const { BitStream } = importer.import('bit buffer');
// Define constants
const MAX_QPATH = 64;
const MAX_SHADER_SIZE = 72;
// Define shader structure
class Shader {
/**
* @param {string} shaderName - Shader name
* @param {number} surfaceFlags - Surface flags
* @param {number} contents - Contents
*/
constructor(shaderName = null, surfaceFlags = 0, contents = 0) {
this.shaderName = shaderName;
this.surfaceFlags = surfaceFlags;
this.contents = contents;
}
}
// Define function to load shaders from binary file
function loadShaders(file, fileofs, filelen) {
// TODO: Add error handling for file I/O operations
const bb = new BitStream(file);
bb.byteIndex = fileofs;
// Initialize array to store shaders
const shaders = new Array(filelen / MAX_SHADER_SIZE);
// Iterate over each shader in the file
for (let i = 0; i < shaders.length; i++) {
// Create a new shader object
const shader = shaders[i] = new Shader();
// Read shader name, surface flags, and contents from the file
shader.shaderName = bb.readASCIIString(MAX_QPATH);
shader.surfaceFlags = bb.readInt32();
shader.contents = bb.readInt32();
}
return shaders;
}
// Define function to list textures from a BSP file
function listTextures(file, dirsOnly = false) {
// Check if the file is a BSP file
if (file.includes('.bsp')) {
// Read the BSP file into a buffer
const fileBuffer = fs.readFileSync(file);
// Initialize a BitStream object to read from the file buffer
const bb = new BitStream(fileBuffer);
// Skip over the BSP file header
bb.readASCIIString(4);
bb.readInt32();
bb.readInt32();
bb.readInt32();
bb.readInt32();
// Get the file offset and length for the shaders lump
const fileofs = bb.readInt32();
const filelen = bb.readInt32();
// Load shaders from the file
const shaders = loadShaders(fileBuffer, fileofs, filelen);
// Return a list of unique shader names
return shaders.filter((s, i, arr) => s.shaderName && arr.indexOf(s) === i).map(s => s.shaderName);
}
// Check if the file is a string
if (typeof file ==='string') {
// Check if the file exists on disk
if (!fs.existsSync(file)) {
throw new Error(`File not found: ${file}`);
}
// Read the file contents into a string
const fileContents = fs.readFileSync(file, 'utf-8');
}
// Initialize an array to store used texture paths
const usedTextures = [];
// Use regular expressions to find all brushes in the file contents
const brushes = importer.regexToArray(/\{[\s\S^\}\{]*?\}/ig, fileContents, -1);
// Log the number of brushes found
console.log(`Found ${brushes.length} brushes.`);
// Iterate over each brush
for (let i = 0; i < brushes.length; i++) {
// Extract the brush contents
const brush = fileContents.substr(brushes[i][0], brushes[i][1]);
// Use regular expressions to find all texture paths in the brush
const texturePaths = importer.regexToArray(/\)\s+([^\)\(]*?)\s+((\s*[0-9\.-]+){5,8})/igm, brush, 1);
// Iterate over each texture path
texturePaths.forEach((texturePath) => {
// Check if the dirsOnly flag is set
if (dirsOnly) {
// Check if the texture path's directory has already been added to the usedTextures array
if (!usedTextures.includes(path.dirname(texturePath))) {
// Add the directory to the usedTextures array
usedTextures.push(path.dirname(texturePath));
}
} else {
// Check if the texture path has already been added to the usedTextures array
if (!usedTextures.includes(texturePath)) {
// Add the texture path to the usedTextures array
usedTextures.push(texturePath);
}
}
});
}
// Return the usedTextures array
return usedTextures;
}
// Export the listTextures function
module.exports = listTextures;
This code snippet analyzes Quake map files to extract and list texture references used within the map.
Here's a breakdown:
Initialization:
fs
for file system operations, path
for path manipulation, and importer
(presumably a custom module for parsing Quake files) and BitStream
for binary data handling.dshader_t
, a structure representing a shader definition, and sets its size.loadShaders
Function:
BitStream
object to read data from the buffer.listTextures
Function:
loadShaders
.Purpose:
This code likely serves as a tool for analyzing Quake map files and identifying the textures used within them. It can be used to generate lists of textures, identify missing textures, or analyze texture usage patterns.