portal64-still-alive/tools/models/model_list_utils.js

133 lines
3.5 KiB
JavaScript
Raw Permalink Normal View History

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
};