mirror of
https://github.com/isledecomp/actionheadergen.git
synced 2025-03-25 20:19:49 -04:00
initial commit
This commit is contained in:
commit
0cdcf86e4c
11 changed files with 446 additions and 0 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object Files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic Libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran Module Files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static Libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# Build Directory
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
.DS_Store
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "libweaver"]
|
||||||
|
path = libweaver
|
||||||
|
url = https://github.com/isledecomp/SIEdit
|
25
CMakeLists.txt
Normal file
25
CMakeLists.txt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
|
||||||
|
|
||||||
|
project(actionheadergen LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(PROJECT_SOURCES
|
||||||
|
src/main.cpp
|
||||||
|
src/headergenerator.cpp
|
||||||
|
src/interleafhandler.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(actionheadergen ${PROJECT_SOURCES})
|
||||||
|
|
||||||
|
target_link_libraries(actionheadergen PRIVATE libweaver)
|
||||||
|
|
||||||
|
target_link_directories(actionheadergen PRIVATE "${CMAKE_SOURCE_DIR}/libweaver/build/lib")
|
||||||
|
|
||||||
|
target_include_directories(actionheadergen PRIVATE
|
||||||
|
"${CMAKE_SOURCE_DIR}/include"
|
||||||
|
"${CMAKE_SOURCE_DIR}/libweaver/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(actionheadergen PROPERTIES
|
||||||
|
CXX_STANDARD 98
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
)
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 isledecomp
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
20
include/headergenerator.h
Normal file
20
include/headergenerator.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef HEADERGENERATOR_H
|
||||||
|
#define HEADERGENERATOR_H
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class HeaderGenerator {
|
||||||
|
public:
|
||||||
|
bool GenerateHeader(char *p_interleafName, std::vector<const char *> *p_actionVector, char *p_outputDir);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool CreateHeader(char *p_interleafName, char *p_outputDir);
|
||||||
|
bool WriteHeader(char *p_interleafName, std::vector<const char *> *p_actionVector);
|
||||||
|
std::ofstream m_fout;
|
||||||
|
char m_normalizedInlfName[512];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HEADERGENERATOR_H
|
||||||
|
|
20
include/interleafhandler.h
Normal file
20
include/interleafhandler.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef INTERLEAFHANDLER_H
|
||||||
|
#define INTERLEAFHANDLER_H
|
||||||
|
|
||||||
|
#include <interleaf.h>
|
||||||
|
|
||||||
|
class InterleafHandler {
|
||||||
|
public:
|
||||||
|
si::Interleaf::Error ReadInterleaf(char *p_filePath);
|
||||||
|
bool SortActionsIntoVector();
|
||||||
|
|
||||||
|
inline std::vector<const char *> *GetActionVector() { return m_actionVector; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
si::Interleaf m_inlf;
|
||||||
|
size_t m_actionCount;
|
||||||
|
std::vector<const char *> *m_actionVector;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INTERLEAFHANDLER_H
|
||||||
|
|
1
libweaver
Submodule
1
libweaver
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 76dab7dfd9af194637e99f58531807bce06a12b4
|
137
src/headergenerator.cpp
Normal file
137
src/headergenerator.cpp
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "headergenerator.h"
|
||||||
|
|
||||||
|
// the generic extension to the header filename, used by weaver
|
||||||
|
const char *g_headerExt = "_actions.h";
|
||||||
|
|
||||||
|
// the generic extension used for the include guard
|
||||||
|
const char *g_headerGuard = "_ACTIONS_H";
|
||||||
|
|
||||||
|
// enum entry prefix used in the isle decomp
|
||||||
|
const char *g_enumEntryPrefix = "c_";
|
||||||
|
|
||||||
|
// notice to not edit autogenerated headers
|
||||||
|
const char *g_doNotEditNotice = "// This file was automatically generated by the actionheadergen tool.\n// Please do not manually edit this file.\n";
|
||||||
|
|
||||||
|
bool HeaderGenerator::GenerateHeader(char *p_interleafName, std::vector<const char *> *p_actionVector, char *p_outputDir)
|
||||||
|
{
|
||||||
|
// attempt to create header file
|
||||||
|
if (!CreateHeader(p_interleafName, p_outputDir)) {
|
||||||
|
printf("Failed to create header, check file permissions?\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to write header file
|
||||||
|
if (!WriteHeader(p_interleafName, p_actionVector)) {
|
||||||
|
printf("Failed to write header\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// success
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeaderGenerator::CreateHeader(char *p_interleafName, char *p_outputDir)
|
||||||
|
{
|
||||||
|
char headerName[1024];
|
||||||
|
char outputPath[2048];
|
||||||
|
|
||||||
|
strcpy(m_normalizedInlfName, p_interleafName);
|
||||||
|
|
||||||
|
// format Interleaf name to mostly lowercase,
|
||||||
|
// to make acceptable by decomp styling guidelines
|
||||||
|
for (int i = 1; i < strlen(p_interleafName); i++) {
|
||||||
|
m_normalizedInlfName[i] = tolower(m_normalizedInlfName[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the header name to the Interleaf name
|
||||||
|
strcpy(headerName, m_normalizedInlfName);
|
||||||
|
|
||||||
|
// set first character to lowercase, which
|
||||||
|
// was skipped by the previous operation
|
||||||
|
headerName[0] = tolower(headerName[0]);
|
||||||
|
|
||||||
|
// append the generic extension
|
||||||
|
strcat(headerName, g_headerExt);
|
||||||
|
|
||||||
|
// generate the full path of the output file
|
||||||
|
strcpy(outputPath, p_outputDir);
|
||||||
|
strcat(outputPath, "/");
|
||||||
|
strcat(outputPath, headerName);
|
||||||
|
|
||||||
|
m_fout.open(outputPath);
|
||||||
|
|
||||||
|
// make sure we can actually create this file
|
||||||
|
if (!m_fout.is_open()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeaderGenerator::WriteHeader(char *p_interleafName, std::vector<const char *> *p_actionVector)
|
||||||
|
{
|
||||||
|
// create boilerplate
|
||||||
|
char guardBuffer[2048];
|
||||||
|
char guardName[1024];
|
||||||
|
|
||||||
|
strcpy(guardName, p_interleafName);
|
||||||
|
strcat(guardName, g_headerGuard);
|
||||||
|
|
||||||
|
// might be messy, but I don't know a better way to do it
|
||||||
|
strcpy(guardBuffer, "#ifndef ");
|
||||||
|
strcat(guardBuffer, guardName);
|
||||||
|
strcat(guardBuffer, "\n");
|
||||||
|
strcat(guardBuffer, "#define ");
|
||||||
|
strcat(guardBuffer, guardName);
|
||||||
|
|
||||||
|
// insert notice to not edit this header
|
||||||
|
m_fout << g_doNotEditNotice;
|
||||||
|
|
||||||
|
// insert include guard
|
||||||
|
m_fout << guardBuffer << "\n\n";
|
||||||
|
|
||||||
|
// declare enum
|
||||||
|
m_fout << "enum " << m_normalizedInlfName << "Script {\n";
|
||||||
|
|
||||||
|
// cache action vector size
|
||||||
|
int vecSize = p_actionVector->size();
|
||||||
|
|
||||||
|
// iterate through the action vector and insert each action into enum
|
||||||
|
for (int i = 0; i < vecSize; i++) {
|
||||||
|
if (strlen(p_actionVector->at(i)) == 0) {
|
||||||
|
// this action has no name, so it's
|
||||||
|
// probably padding or not important
|
||||||
|
// we'll just skip it
|
||||||
|
|
||||||
|
// check if the previous action was also skipped so we
|
||||||
|
// don't stack crazy amounts of whitespace into the enum
|
||||||
|
if (i != 0) {
|
||||||
|
if (strlen(p_actionVector->at(i - 1)) != 0) {
|
||||||
|
m_fout << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
m_fout << " " << g_enumEntryPrefix << p_actionVector->at(i) << " = " << i;
|
||||||
|
if (i != vecSize - 1) {
|
||||||
|
// if there are still more entries, we need write a comma
|
||||||
|
m_fout << ",";
|
||||||
|
}
|
||||||
|
m_fout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// all done with actions, so lets close the enum
|
||||||
|
m_fout << "};\n\n";
|
||||||
|
|
||||||
|
// finally, close the include guard
|
||||||
|
m_fout << "#endif // " << guardName << "\n";
|
||||||
|
|
||||||
|
// close the file
|
||||||
|
m_fout.close();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
39
src/interleafhandler.cpp
Normal file
39
src/interleafhandler.cpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#include "interleafhandler.h"
|
||||||
|
|
||||||
|
si::Interleaf::Error InterleafHandler::ReadInterleaf(char *p_filePath)
|
||||||
|
{
|
||||||
|
// this is basically a wrapper function for libweaver's
|
||||||
|
// Read() so we can set private member variables
|
||||||
|
return m_inlf.Read(p_filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterleafHandler::SortActionsIntoVector()
|
||||||
|
{
|
||||||
|
// if there's no actions in this Interleaf, exit
|
||||||
|
if (!m_inlf.HasChildren()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the amount of actions in this Interleaf
|
||||||
|
m_actionCount = m_inlf.GetChildCount();
|
||||||
|
|
||||||
|
// prepare our vector for use
|
||||||
|
m_actionVector = new std::vector<const char *>;
|
||||||
|
|
||||||
|
// itereate through every action in our action count
|
||||||
|
for (size_t i = 0; i < m_actionCount; i++) {
|
||||||
|
// get the action as Core initially
|
||||||
|
si::Core *actionAsCore = m_inlf.GetChildAt(i);
|
||||||
|
|
||||||
|
// Core doesn't provide some of the data we need, so we get the
|
||||||
|
// action as Object, so then we can retrieve the name of the action
|
||||||
|
if (si::Object *actionAsObject = dynamic_cast<si::Object*>(actionAsCore)) {
|
||||||
|
// push the name of the action into the vector
|
||||||
|
m_actionVector->push_back(actionAsObject->name().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// success
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
140
src/main.cpp
Normal file
140
src/main.cpp
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "headergenerator.h"
|
||||||
|
#include "interleafhandler.h"
|
||||||
|
|
||||||
|
bool RecursivelyFindInterleaf(const char *p_path, std::vector<std::string> &p_interleafFiles)
|
||||||
|
{
|
||||||
|
// TODO: *by default* this is a UNIX-specific solution
|
||||||
|
// Windows is capable of using dirent through third party implementations,
|
||||||
|
// but it would be nice to have something that works out of the box on Win32
|
||||||
|
// this is one of the pains of C++98, there is no good directory support in the standard
|
||||||
|
DIR *directory = opendir(p_path);
|
||||||
|
if (!directory) {
|
||||||
|
printf("Could not open directory %s, exiting\n", p_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *dentry;
|
||||||
|
// start scouring the directory
|
||||||
|
while ((dentry = readdir(directory)) != NULL) {
|
||||||
|
// if this path is a directory
|
||||||
|
if (dentry->d_type == DT_DIR) {
|
||||||
|
// filter out backwards paths
|
||||||
|
if (strcmp(dentry->d_name, ".") != 0 && strcmp(dentry->d_name, "..") != 0) {
|
||||||
|
// this is a subdirectory, so recurse
|
||||||
|
char nextPathBuffer[1024];
|
||||||
|
snprintf(nextPathBuffer, sizeof(nextPathBuffer), "%s/%s", p_path, dentry->d_name);
|
||||||
|
|
||||||
|
if (!RecursivelyFindInterleaf(nextPathBuffer, p_interleafFiles)) {
|
||||||
|
// couldn't access this subdirectory, abort
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (strcasestr(dentry->d_name, ".si") != NULL) {
|
||||||
|
// found an Interleaf file, return it
|
||||||
|
char fullPathBuffer[1024];
|
||||||
|
|
||||||
|
// construct the full path to the file first
|
||||||
|
snprintf(fullPathBuffer, sizeof(fullPathBuffer), "%s/%s", p_path, dentry->d_name);
|
||||||
|
|
||||||
|
p_interleafFiles.push_back(fullPathBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// success
|
||||||
|
closedir(directory);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
// no file or output directory provided
|
||||||
|
if (argc < 3) {
|
||||||
|
printf("Usage: %s (<interleaf.si> or <input directory>) <output directory>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *interleafExtension = ".si";
|
||||||
|
|
||||||
|
std::vector<std::string> interleafFiles;
|
||||||
|
|
||||||
|
// cache the arguments so we don't have to enter the array unnecessarily
|
||||||
|
char *filePath = argv[1];
|
||||||
|
char *outputDir = argv[2];
|
||||||
|
|
||||||
|
// if filePath doesn't end in ".si",
|
||||||
|
// we got passed a directory, enter batch mode
|
||||||
|
if (strcasecmp(filePath + strlen(filePath) - strlen(interleafExtension), interleafExtension) != 0) {
|
||||||
|
// find each Interleaf recursively in the directory
|
||||||
|
// get all files and folders in current directory
|
||||||
|
if (!RecursivelyFindInterleaf(filePath, interleafFiles)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else interleafFiles.push_back(filePath); // we were provided a single file, but we'll still use the vector
|
||||||
|
|
||||||
|
// iterate through every Interleaf in the vector and perform our operations
|
||||||
|
for (int i = 0; i < interleafFiles.size(); i++) {
|
||||||
|
|
||||||
|
InterleafHandler ihandler;
|
||||||
|
|
||||||
|
char currentFilePath[1024];
|
||||||
|
strcpy(currentFilePath, interleafFiles.at(i).c_str());
|
||||||
|
|
||||||
|
// load the Interleaf provided to us in the first argument
|
||||||
|
// InterleafHandler is using libweaver for this
|
||||||
|
// if we're not successful, exit
|
||||||
|
if (ihandler.ReadInterleaf(currentFilePath) != si::Interleaf::ERROR_SUCCESS) {
|
||||||
|
printf("Failure reading Interleaf, exiting\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the Interleaf's actions into a vector
|
||||||
|
// if libweaver is stable enough, a failure is only
|
||||||
|
// possible if no actions exist in the Interleaf
|
||||||
|
if (!ihandler.SortActionsIntoVector()) {
|
||||||
|
printf("No actions found in this Interleaf, exiting\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set filename to filePath without directory appended
|
||||||
|
// libweaver unfortunately doesn't provide a way to get
|
||||||
|
// the Interleaf name so we have to construct it ourselves
|
||||||
|
char *filename = strrchr(currentFilePath, '/');
|
||||||
|
if (filename) {
|
||||||
|
// we don't need ".SI" for our purposes
|
||||||
|
// so just remove the last 3 characters
|
||||||
|
filename[strlen(filename) - 3] = '\0';
|
||||||
|
// remove the first character as well, since it will be "/"
|
||||||
|
filename = filename + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderGenerator hgenerator;
|
||||||
|
|
||||||
|
// generate the actual header
|
||||||
|
// we use the Interleaf name for the filename
|
||||||
|
// and the vector previously generated to make the enum data
|
||||||
|
if (!hgenerator.GenerateHeader(filename, ihandler.GetActionVector(), outputDir)) {
|
||||||
|
// we failed for some reason, so let's just move on
|
||||||
|
printf("Failure generating header for %s, skipping\n", filename);
|
||||||
|
|
||||||
|
delete ihandler.GetActionVector();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all steps were successful
|
||||||
|
delete ihandler.GetActionVector();
|
||||||
|
|
||||||
|
printf("Succesfully generated header for %s\n", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Finished!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue