initial commit

This commit is contained in:
Ramen2X 2024-03-08 01:38:16 -05:00
commit 0cdcf86e4c
11 changed files with 446 additions and 0 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

38
.gitignore vendored Normal file
View 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
View file

@ -0,0 +1,3 @@
[submodule "libweaver"]
path = libweaver
url = https://github.com/isledecomp/SIEdit

25
CMakeLists.txt Normal file
View 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
View 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
View 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

View 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

@ -0,0 +1 @@
Subproject commit 76dab7dfd9af194637e99f58531807bce06a12b4

137
src/headergenerator.cpp Normal file
View 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
View 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
View 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;
}