mirror of
https://github.com/mwpenny/portal64-still-alive.git
synced 2024-10-19 22:27:36 -04:00
1d0b22df61
* When building with CMake, dynamic model lists are now generated * Factored common code out of generate_*_list.js files to deduplicate * Better separated generated model lists from main game code `dynamic_asset_data.c` and `dynamic_animated_asset_data.c` previously #included the corresponding generated headers, and `dynamic_asset_loader.c` declared externs for the contents. This messiness was likely done so the generated code would be built automatically (the Makefile globs all C files under `src/`). Now, model list data is output to C source files which are built explicitly. This, with some refactoring, allows the previously mentioned source files and externs to be removed. This is a bit hacky in the Makefile but will be automatic under CMake by using target properties. * Reorganized some files under `tools/`
133 lines
3.5 KiB
JavaScript
133 lines
3.5 KiB
JavaScript
const path = require('path');
|
|
|
|
const INVALID_TOKEN_CHARACTER = /[^A-Za-z0-9_]/gim;
|
|
|
|
// Common
|
|
|
|
function sanitize(s) {
|
|
return s.replace(INVALID_TOKEN_CHARACTER, '_');
|
|
}
|
|
|
|
function wrapWithIncludeGuard(outputPath, content) {
|
|
const outputName = path.basename(outputPath);
|
|
const includeGuardName = `__${sanitize(outputName).toUpperCase()}__`;
|
|
return `#ifndef ${includeGuardName}
|
|
#define ${includeGuardName}
|
|
|
|
${content}
|
|
|
|
#endif
|
|
`;
|
|
}
|
|
|
|
function generateRelativeModelName(outputPath, modelHeader, suffix='') {
|
|
const outputDirName = path.dirname(outputPath);
|
|
const { dir: headerDirName, name: headerName } = path.parse(modelHeader);
|
|
|
|
const relative = path.join(
|
|
path.relative(outputDirName, headerDirName),
|
|
headerName
|
|
);
|
|
|
|
return sanitize(relative + suffix);
|
|
}
|
|
|
|
function generateModelName(modelHeader) {
|
|
return generateRelativeModelName(modelHeader, modelHeader);
|
|
}
|
|
|
|
function generateListDefinition(modelGroup, modelType) {
|
|
const groupPascalCase = modelGroup
|
|
.split('_')
|
|
.map(s => s && s.replace(/^(\w)/g, m => m.toUpperCase()))
|
|
.join('');
|
|
|
|
const listName = `g${groupPascalCase}s`;
|
|
return `struct ${modelType} ${listName}[]`;
|
|
}
|
|
|
|
// Header generation
|
|
|
|
function generateCount(modelGroup, modelHeaders) {
|
|
return `#define ${modelGroup.toUpperCase()}_COUNT ${modelHeaders.length}`;
|
|
}
|
|
|
|
function generateModelIndices(outputPath, modelHeaders, modelGroup) {
|
|
return modelHeaders.map((modelHeader, index) => {
|
|
const modelName = generateRelativeModelName(outputPath, modelHeader, `_${modelGroup}`);
|
|
return `#define ${modelName.toUpperCase()} ${index}`;
|
|
}).join('\n');
|
|
}
|
|
|
|
function generateListExtern(modelGroup, modelType) {
|
|
return `extern ${generateListDefinition(modelGroup, modelType)};`;
|
|
}
|
|
|
|
function generateHeader(outputPath, config) {
|
|
const { modelHeaders, modelGroup, modelType } = config;
|
|
|
|
return wrapWithIncludeGuard(outputPath,
|
|
`#include "util/dynamic_asset_model.h"
|
|
|
|
${generateCount(modelGroup, modelHeaders)}
|
|
|
|
${generateModelIndices(outputPath, modelHeaders, modelGroup)}
|
|
|
|
${generateListExtern(modelGroup, modelType)}`
|
|
);
|
|
}
|
|
|
|
// Data generation
|
|
|
|
function generateIncludes(outputPath, modelHeaders) {
|
|
return modelHeaders.map(modelHeader => {
|
|
const relativePath = path.relative(path.dirname(outputPath), modelHeader);
|
|
return `#include "${relativePath}"`;
|
|
}).join('\n');
|
|
}
|
|
|
|
function generateExterns(modelHeaders) {
|
|
return modelHeaders.map(modelHeader => {
|
|
const modelName = generateModelName(modelHeader);
|
|
return `extern char _${modelName}_geoSegmentRomStart[];
|
|
extern char _${modelName}_geoSegmentRomEnd[];
|
|
extern char _${modelName}_geoSegmentStart[];`;
|
|
}).join('\n\n');
|
|
}
|
|
|
|
function generateModelList(outputPath, config) {
|
|
const { modelHeaders, modelGroup, modelType, listEntryGenerator } = config;
|
|
|
|
return `${generateListDefinition(modelGroup, modelType)} = {
|
|
${modelHeaders.map(modelHeader => listEntryGenerator(outputPath, modelHeader)).join('\n')}
|
|
};`
|
|
}
|
|
|
|
function generateData(outputPath, config) {
|
|
const { name } = path.parse(outputPath);
|
|
const { modelHeaders } = config;
|
|
|
|
return `#include "${name}.h"
|
|
|
|
${generateIncludes(outputPath, modelHeaders)}
|
|
|
|
${generateExterns(modelHeaders)}
|
|
|
|
${generateModelList(outputPath, config)}
|
|
`;
|
|
}
|
|
|
|
module.exports = {
|
|
wrapWithIncludeGuard,
|
|
generateRelativeModelName,
|
|
generateModelName,
|
|
|
|
generateCount,
|
|
generateHeader,
|
|
|
|
generateIncludes,
|
|
generateExterns,
|
|
generateModelList,
|
|
generateData
|
|
};
|