Initial commit
This commit is contained in:
commit
bdd292ce2a
1 changed files with 210 additions and 0 deletions
210
main.odin
Normal file
210
main.odin
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:io"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:os"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
TAB :: "\t"
|
||||||
|
ENUM_TAG :: "gml_pragma(\"enum_reflection\")"
|
||||||
|
ENUM_ENUM :: "Enum"
|
||||||
|
ENUM_NAMES_GLOBAL :: "global._ENUM_NAMES"
|
||||||
|
ENUM_MEMBER_NAMES_GLOBAL :: "global._ENUM_MEMBER_NAMES"
|
||||||
|
|
||||||
|
ENUM_GET_NAME_FUNCTION :: "enum_get_name"
|
||||||
|
ENUM_GET_MEMBER_NAME_FUNCTION :: "enum_get_member_name"
|
||||||
|
ENUM_GET_MEMBER_NAMES_FUNCTION :: "enum_get_member_names"
|
||||||
|
ENUM_GET_MEMBER_NAME_FULL_FUNCTION :: "enum_get_member_name_full"
|
||||||
|
|
||||||
|
print_help_message :: proc() {
|
||||||
|
fmt.println("--- GameMaker enum reflection ---", flush = false)
|
||||||
|
fmt.println("Usage: <program> <project dir> <output gml>")
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
switch len(os.args) {
|
||||||
|
case 0, 1, 2:
|
||||||
|
print_help_message()
|
||||||
|
case 3:
|
||||||
|
err := program(os.args[1], os.args[2])
|
||||||
|
if err != nil {
|
||||||
|
fmt.println("Error: ", err)
|
||||||
|
}
|
||||||
|
case:
|
||||||
|
fmt.println("Error: Too many arguments")
|
||||||
|
print_help_message()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Error :: union {
|
||||||
|
os.Error,
|
||||||
|
mem.Allocator_Error,
|
||||||
|
ProgramError,
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramError :: enum {
|
||||||
|
ProjectDirNotDir,
|
||||||
|
OutputGmlNotExists,
|
||||||
|
ScriptsDirNotExists,
|
||||||
|
ObjectsDirNotExists,
|
||||||
|
}
|
||||||
|
|
||||||
|
program :: proc(project_dir, output_gml_path: string) -> Error {
|
||||||
|
if !os.is_directory(project_dir) {
|
||||||
|
return ProgramError.ProjectDirNotDir
|
||||||
|
}
|
||||||
|
if !os.exists(output_gml_path) {
|
||||||
|
return ProgramError.OutputGmlNotExists
|
||||||
|
}
|
||||||
|
_, output_gml_name := os.split_path(output_gml_path)
|
||||||
|
|
||||||
|
scripts_dir := os.join_path({project_dir, "scripts"}, context.allocator) or_return
|
||||||
|
if !os.exists(scripts_dir) {
|
||||||
|
return ProgramError.ScriptsDirNotExists
|
||||||
|
}
|
||||||
|
|
||||||
|
objects_dir := os.join_path({project_dir, "objects"}, context.allocator) or_return
|
||||||
|
if !os.exists(objects_dir) {
|
||||||
|
return ProgramError.ObjectsDirNotExists
|
||||||
|
}
|
||||||
|
|
||||||
|
enums := make([dynamic]GmlEnum)
|
||||||
|
walker := os.walker_create(scripts_dir)
|
||||||
|
for info, ok := os.walker_walk(&walker); ok; info, ok = os.walker_walk(&walker) {
|
||||||
|
if _, ext := os.split_filename(info.name); ext != "gml" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if info.name == output_gml_name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data := os.read_entire_file(info.fullpath, context.allocator) or_return
|
||||||
|
gml := string(data)
|
||||||
|
gml_extract_enums(gml, info.name, &enums)
|
||||||
|
}
|
||||||
|
|
||||||
|
walker = os.walker_create(objects_dir)
|
||||||
|
for info, ok := os.walker_walk(&walker); ok; info, ok = os.walker_walk(&walker) {
|
||||||
|
if _, ext := os.split_filename(info.name); ext != "gml" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if info.name == output_gml_name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data := os.read_entire_file(info.fullpath, context.allocator) or_return
|
||||||
|
gml := string(data)
|
||||||
|
gml_extract_enums(gml, info.name, &enums)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := os.open(output_gml_path, {.Write, .Trunc}) or_return
|
||||||
|
writer := io.Writer(os.to_stream(f))
|
||||||
|
gml_write_enums(writer, enums)
|
||||||
|
os.close(f)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
GmlEnum :: struct {
|
||||||
|
name: string,
|
||||||
|
members: []string,
|
||||||
|
}
|
||||||
|
gml_extract_enums :: proc(gml, filename: string, enums_out: ^[dynamic]GmlEnum) {
|
||||||
|
ENUM_SEARCH :: "enum "
|
||||||
|
|
||||||
|
pos: int
|
||||||
|
for true {
|
||||||
|
gml_prev_enum := gml[pos:]
|
||||||
|
pos_tag_start := strings.index(gml_prev_enum, ENUM_TAG)
|
||||||
|
if pos_tag_start == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pos_tag_end := pos_tag_start + len(ENUM_TAG)
|
||||||
|
pos += pos_tag_end
|
||||||
|
gml_tag_end := gml_prev_enum[pos_tag_end:]
|
||||||
|
pos_enum_start := strings.index(gml_tag_end, ENUM_SEARCH)
|
||||||
|
if pos_enum_start == -1 {
|
||||||
|
fmt.println("Found " + ENUM_TAG + " in", filename, "but no enum")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(strings.trim(strings.trim_space(gml_tag_end[:pos_enum_start]), ";\n")) > 0 {
|
||||||
|
fmt.println("Found", ENUM_TAG, "in", filename, "but separation between tag and enum was too great")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
gml_enum_name_start := gml_tag_end[pos_enum_start + len(ENUM_SEARCH):]
|
||||||
|
pos_enum_curly_open := strings.index(gml_enum_name_start, "{")
|
||||||
|
if pos_enum_curly_open == -1 {
|
||||||
|
fmt.println("Missing enum opening bracket in", filename)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
enum_name := strings.trim_space(gml_enum_name_start[:pos_enum_curly_open])
|
||||||
|
pos_enum_curly_close := strings.index(gml_enum_name_start, "}")
|
||||||
|
if pos_enum_curly_close == -1 {
|
||||||
|
fmt.println("Missing enum closing bracket in", filename)
|
||||||
|
}
|
||||||
|
gml_enum_content := gml_enum_name_start[pos_enum_curly_open + 1:pos_enum_curly_close]
|
||||||
|
if strings.index(gml_enum_content, "/") != -1 {
|
||||||
|
fmt.println("Enum members of", enum_name, "in", filename, "contained disallowed character '/' (possibly comments). Skipping...")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.index(gml_enum_content, "=") != -1 {
|
||||||
|
fmt.println("Enum members of", enum_name, "in", filename, "contained disallowed character '='. Skipping...")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
enum_names := strings.split(gml_enum_content, ",")
|
||||||
|
for i in 0 ..< len(enum_names) {
|
||||||
|
enum_names[i] = strings.trim_space(enum_names[i])
|
||||||
|
}
|
||||||
|
if len(enum_names[len(enum_names) - 1]) == 0 {
|
||||||
|
enum_names = enum_names[:len(enum_names) - 1]
|
||||||
|
}
|
||||||
|
append(enums_out, GmlEnum{name = enum_name, members = enum_names})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gml_write_enums :: proc(writer: io.Writer, enums: [dynamic]GmlEnum) {
|
||||||
|
io.write_string(writer, "/*\n" + TAB + "This file was generated using enum_reflection tool\n*/\n")
|
||||||
|
io.write_string(writer, "enum " + ENUM_ENUM + " {\n")
|
||||||
|
for e in enums {
|
||||||
|
io.write_string(writer, TAB)
|
||||||
|
io.write_string(writer, e.name)
|
||||||
|
io.write_string(writer, ",\n")
|
||||||
|
}
|
||||||
|
io.write_string(writer, TAB + "_Count\n}\n\n")
|
||||||
|
|
||||||
|
io.write_string(writer, ENUM_NAMES_GLOBAL + " = [\n")
|
||||||
|
for e in enums {
|
||||||
|
io.write_string(writer, TAB + "\"")
|
||||||
|
io.write_string(writer, e.name)
|
||||||
|
io.write_string(writer, "\",\n")
|
||||||
|
}
|
||||||
|
io.write_string(writer, "];\n\n")
|
||||||
|
|
||||||
|
io.write_string(writer, ENUM_MEMBER_NAMES_GLOBAL + " = [\n")
|
||||||
|
for e in enums {
|
||||||
|
io.write_string(writer, TAB + "[")
|
||||||
|
for entry in e.members {
|
||||||
|
io.write_string(writer, "\"")
|
||||||
|
io.write_string(writer, entry)
|
||||||
|
io.write_string(writer, "\", ")
|
||||||
|
}
|
||||||
|
io.write_string(writer, "],\n")
|
||||||
|
}
|
||||||
|
io.write_string(writer, "];\n\n")
|
||||||
|
|
||||||
|
io.write_string(writer, "function " + ENUM_GET_NAME_FUNCTION + "(index) {\n")
|
||||||
|
io.write_string(writer, TAB + "return " + ENUM_NAMES_GLOBAL + "[index];\n")
|
||||||
|
io.write_string(writer, "}\n\n")
|
||||||
|
|
||||||
|
io.write_string(writer, "function " + ENUM_GET_MEMBER_NAME_FUNCTION + "(index, member) {\n")
|
||||||
|
io.write_string(writer, TAB + "return " + ENUM_MEMBER_NAMES_GLOBAL + "[index][member];\n")
|
||||||
|
io.write_string(writer, "}\n\n")
|
||||||
|
|
||||||
|
io.write_string(writer, "function " + ENUM_GET_MEMBER_NAMES_FUNCTION + "(index) {\n")
|
||||||
|
io.write_string(writer, TAB + "return " + ENUM_MEMBER_NAMES_GLOBAL + "[index];\n")
|
||||||
|
io.write_string(writer, "}\n\n")
|
||||||
|
|
||||||
|
io.write_string(writer, "function " + ENUM_GET_MEMBER_NAME_FULL_FUNCTION + "(index, member) {\n")
|
||||||
|
io.write_string(writer, TAB + "return $\"{" + ENUM_NAMES_GLOBAL + "[index]}.{" + ENUM_MEMBER_NAMES_GLOBAL + "[index][member]}\";\n")
|
||||||
|
io.write_string(writer, "}\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue