mirror of
https://github.com/isledecomp/actionheadergen.git
synced 2024-11-23 07:47:58 -05: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…
Reference in a new issue