mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 20:58:07 +03:00
tools: move to libtrx; remove memleaks patch
This commit is contained in:
parent
32bf0eddf3
commit
6d71455dd8
55 changed files with 124 additions and 928 deletions
|
@ -10,4 +10,3 @@
|
|||
<string>icon</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "game/input.h"
|
||||
#include "game/music.h"
|
||||
#include "game/sound.h"
|
||||
#include "gfx/context.h"
|
||||
#include "global/const.h"
|
||||
#include "global/types.h"
|
||||
|
||||
|
@ -14,6 +13,7 @@
|
|||
#include <libtrx/memory.h>
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// IWYU pragma: no_include <bits/types/struct_tm.h>
|
||||
#include "game/clock.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "game/console.h"
|
||||
#include "game/game_string.h"
|
||||
#include "game/phase/phase.h"
|
||||
#include "global/vars.h"
|
||||
#include "global/const.h"
|
||||
|
||||
#include <libtrx/log.h>
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "game/items.h"
|
||||
#include "game/lara.h"
|
||||
#include "game/lara/lara_cheat.h"
|
||||
#include "game/los.h"
|
||||
#include "game/objects/common.h"
|
||||
#include "game/objects/names.h"
|
||||
#include "game/output.h"
|
||||
|
@ -21,13 +20,10 @@
|
|||
#include "global/const.h"
|
||||
#include "global/types.h"
|
||||
#include "global/vars.h"
|
||||
#include "math/math.h"
|
||||
|
||||
#include <libtrx/memory.h>
|
||||
#include <libtrx/strings.h>
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
#include "game/interpolation.h"
|
||||
|
||||
#include "game/clock.h"
|
||||
#include "config.h"
|
||||
#include "game/effects.h"
|
||||
#include "game/items.h"
|
||||
#include "game/lara/lara_hair.h"
|
||||
#include "game/phase/phase.h"
|
||||
#include "game/room.h"
|
||||
#include "global/const.h"
|
||||
#include "global/vars.h"
|
||||
#include "math/math_misc.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define REMEMBER(target, member) (target)->interp.prev.member = (target)->member
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "global/types.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool Interpolation_IsEnabled(void);
|
||||
void Interpolation_Disable(void);
|
||||
void Interpolation_Enable(void);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "game/inventory/inventory_vars.h"
|
||||
#include "game/phase/phase.h"
|
||||
#include "global/types.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "game/inventory/inventory_ring.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "game/clock.h"
|
||||
#include "game/game_string.h"
|
||||
#include "game/inventory.h"
|
||||
#include "game/inventory/inventory_vars.h"
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "game/anim.h"
|
||||
#include "game/camera.h"
|
||||
#include "game/carrier.h"
|
||||
#include "game/clock.h"
|
||||
#include "game/interpolation.h"
|
||||
#include "game/room.h"
|
||||
#include "game/shell.h"
|
||||
|
|
|
@ -3,14 +3,12 @@
|
|||
#include "config.h"
|
||||
#include "game/camera.h"
|
||||
#include "game/collide.h"
|
||||
#include "game/console.h"
|
||||
#include "game/gun.h"
|
||||
#include "game/input.h"
|
||||
#include "game/inventory.h"
|
||||
#include "game/items.h"
|
||||
#include "game/lara/lara_cheat.h"
|
||||
#include "game/lara/lara_control.h"
|
||||
#include "game/los.h"
|
||||
#include "game/lot.h"
|
||||
#include "game/music.h"
|
||||
#include "game/objects/common.h"
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "global/types.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
|
|
@ -3,10 +3,8 @@
|
|||
#include "config.h"
|
||||
#include "game/box.h"
|
||||
#include "game/collide.h"
|
||||
#include "game/gameflow.h"
|
||||
#include "game/gun.h"
|
||||
#include "game/input.h"
|
||||
#include "game/inventory.h"
|
||||
#include "game/items.h"
|
||||
#include "game/lara.h"
|
||||
#include "game/lara/lara_cheat.h"
|
||||
|
@ -14,7 +12,6 @@
|
|||
#include "game/lara/lara_look.h"
|
||||
#include "game/lara/lara_state.h"
|
||||
#include "game/lot.h"
|
||||
#include "game/objects/general/door.h"
|
||||
#include "game/room.h"
|
||||
#include "game/sound.h"
|
||||
#include "global/const.h"
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "game/lara/lara_hair.h"
|
||||
#include "game/output.h"
|
||||
#include "game/viewport.h"
|
||||
#include "global/const.h"
|
||||
#include "global/vars.h"
|
||||
#include "math/matrix.h"
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
#include "global/types.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct HAIR_SEGMENT {
|
||||
XYZ_32 pos;
|
||||
XYZ_16 rot;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <libtrx/filesystem.h>
|
||||
#include <libtrx/log.h>
|
||||
#include <libtrx/memory.h>
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
const GAME_OBJECT_ID g_EnemyObjects[] = {
|
||||
// clang-format off
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "game/input.h"
|
||||
#include "game/inventory.h"
|
||||
#include "game/inventory/inventory_vars.h"
|
||||
#include "game/items.h"
|
||||
#include "game/lara.h"
|
||||
#include "game/sound.h"
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
#include "game/input.h"
|
||||
#include "game/inventory.h"
|
||||
#include "game/inventory/inventory_vars.h"
|
||||
#include "game/items.h"
|
||||
#include "game/lara.h"
|
||||
#include "game/objects/general/keyhole.h"
|
||||
#include "game/sound.h"
|
||||
#include "global/const.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define LF_USEPUZZLE 80
|
||||
|
||||
static XYZ_32 m_PuzzleHolePosition = { .x = 0,
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "global/types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Return a list of object ids that match given string.
|
||||
// out_match_count may be NULL.
|
||||
// The result must be freed by the caller.
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "global/const.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define EXTRA_ANIM_PLACE_BAR 0
|
||||
#define EXTRA_ANIM_DIE_GOLD 1
|
||||
#define LF_PICKUP_GOLD_BAR 113
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "game/phase/phase.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "game/clock.h"
|
||||
#include "game/interpolation.h"
|
||||
#include "game/output.h"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "game/phase/phase_cutscene.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "game/camera.h"
|
||||
#include "game/effects.h"
|
||||
#include "game/game.h"
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "game/gameflow.h"
|
||||
#include "game/input.h"
|
||||
#include "game/interpolation.h"
|
||||
#include "game/inventory.h"
|
||||
#include "game/items.h"
|
||||
#include "game/lara.h"
|
||||
#include "game/lara/lara_cheat.h"
|
||||
|
@ -26,6 +25,8 @@
|
|||
#include "global/types.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
#include "global/types.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "game/music.h"
|
||||
#include "game/objects/common.h"
|
||||
#include "game/option.h"
|
||||
#include "game/option/option_compass.h"
|
||||
#include "game/output.h"
|
||||
#include "game/overlay.h"
|
||||
#include "game/savegame.h"
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "game/game.h"
|
||||
#include "game/game_string.h"
|
||||
#include "game/gameflow.h"
|
||||
#include "game/input.h"
|
||||
#include "game/interpolation.h"
|
||||
#include "game/music.h"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/bson.h>
|
||||
#include <libtrx/json.h>
|
||||
#include <libtrx/log.h>
|
||||
#include <libtrx/memory.h>
|
||||
#include <libtrx/utils.h>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "gfx/gl/gl_core_3_3.h"
|
||||
#include "gfx/gl/utils.h"
|
||||
#include "gfx/screenshot.h"
|
||||
#include "gfx_options.h"
|
||||
|
||||
#include <libtrx/log.h>
|
||||
#include <libtrx/memory.h>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "game/shell.h"
|
||||
#include "gfx/gl/utils.h"
|
||||
#include "gfx_options.h"
|
||||
|
||||
#include <libtrx/filesystem.h>
|
||||
#include <libtrx/log.h>
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "game/effect_routines/sand.h"
|
||||
#include "game/effect_routines/stairs2slope.h"
|
||||
#include "game/effect_routines/turn_180.h"
|
||||
#include "game/gameflow.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <libtrx/log.h>
|
||||
#include <libtrx/memory.h>
|
||||
|
||||
#include <libavcodec/version.h>
|
||||
|
||||
#define SDL_MAIN_HANDLED
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -27,7 +29,6 @@
|
|||
#include <SDL2/SDL_mouse.h>
|
||||
#include <SDL2/SDL_stdinc.h>
|
||||
#include <SDL2/SDL_video.h>
|
||||
#include <libavcodec/version.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit b1d1e82e5ce3fd47d746b78209733a11d7ef8996
|
||||
Subproject commit c43b761b702012017a76248d34cde5e5af929b1b
|
|
@ -1 +0,0 @@
|
|||
../subprojects/libtrx/tools/additional_lint
|
4
tools/additional_lint
Executable file
4
tools/additional_lint
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env python3
|
||||
from libtrx.cli.additional_lint import run_script
|
||||
|
||||
run_script(ignored_extensions=[".patch", ".bin"])
|
|
@ -1,160 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""This script can be used to analyze TR1X for potential memory leaks.
|
||||
|
||||
For the script to work, the repository needs to have applied
|
||||
tools/analyze_memleaks.patch that logs information about every memory
|
||||
allocation and free.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable, Iterable
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from itertools import groupby
|
||||
from pathlib import Path
|
||||
from typing import Any, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
U = TypeVar("U")
|
||||
|
||||
|
||||
class OperationType(Enum):
|
||||
Allocation = 1
|
||||
Reallocation = 2
|
||||
Deallocation = 3
|
||||
|
||||
|
||||
@dataclass
|
||||
class Operation:
|
||||
file: str
|
||||
line: int
|
||||
func: str
|
||||
type: OperationType
|
||||
address: int
|
||||
size: int | None
|
||||
new_address: int | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Problem:
|
||||
message: str
|
||||
operation: Operation | None
|
||||
|
||||
|
||||
def group(
|
||||
source: Iterable[T], key=Callable[[T], U]
|
||||
) -> Iterable[tuple[U, list[T]]]:
|
||||
for key, group in groupby(sorted(source, key=key), key=key):
|
||||
yield key, list(group)
|
||||
|
||||
|
||||
def get_operations_from_log_lines(lines: Iterable[str]) -> Iterable[Operation]:
|
||||
for line in lines:
|
||||
if match := re.search(
|
||||
r"(\S+) (\d+) (\S+) Allocating memory \((\d+) bytes\): ([A-F0-9]*)",
|
||||
line,
|
||||
):
|
||||
yield Operation(
|
||||
file=match.group(1),
|
||||
line=int(match.group(2)),
|
||||
func=match.group(3),
|
||||
type=OperationType.Allocation,
|
||||
size=int(match.group(4)),
|
||||
address=int(match.group(5), 16),
|
||||
)
|
||||
elif match := re.search(
|
||||
r"(\S+) (\d+) (\S+) Reallocating memory at ([A-F0-9]*) \((\d+) bytes\): ([A-F0-9]*)",
|
||||
line,
|
||||
):
|
||||
yield Operation(
|
||||
file=match.group(1),
|
||||
line=int(match.group(2)),
|
||||
func=match.group(3),
|
||||
type=OperationType.Reallocation,
|
||||
address=int(match.group(4), 16),
|
||||
size=int(match.group(5)),
|
||||
new_address=int(match.group(6), 16),
|
||||
)
|
||||
elif match := re.search(
|
||||
r"(\S+) (\d+) (\S+) Freeing memory at ([A-F0-9]*)", line
|
||||
):
|
||||
yield Operation(
|
||||
file=match.group(1),
|
||||
line=int(match.group(2)),
|
||||
func=match.group(3),
|
||||
type=OperationType.Deallocation,
|
||||
size=None,
|
||||
address=int(match.group(4), 16),
|
||||
)
|
||||
|
||||
|
||||
def find_problems(operations: Iterable[Operation]) -> Iterable[Problem]:
|
||||
reachable_memory: dict[int, Operation] = {}
|
||||
for operation in operations:
|
||||
if operation.type == OperationType.Allocation:
|
||||
assert operation.address not in reachable_memory
|
||||
reachable_memory[operation.address] = operation
|
||||
elif operation.type == OperationType.Reallocation:
|
||||
if operation.address != 0:
|
||||
del reachable_memory[operation.address]
|
||||
assert operation.new_address not in reachable_memory
|
||||
reachable_memory[operation.new_address] = operation
|
||||
elif operation.type == OperationType.Deallocation:
|
||||
if operation.address != 0:
|
||||
try:
|
||||
del reachable_memory[operation.address]
|
||||
except KeyError:
|
||||
yield Problem("Invalid free", operation=operation)
|
||||
|
||||
for address, operation in reachable_memory.items():
|
||||
yield Problem(
|
||||
f"Unfreed memory: {address:08x} ({operation.size} bytes)",
|
||||
operation=operation,
|
||||
)
|
||||
|
||||
reachable_bytes = sum(
|
||||
operation.size for operation in reachable_memory.values()
|
||||
)
|
||||
yield Problem(
|
||||
f"Total reachable memory: {reachable_bytes / 1024.0 / 1024.0:.02f} MB",
|
||||
operation=None,
|
||||
)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("path", help="Path to the TR1X.log file.")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
with Path(args.path).open("r", encoding="utf-8") as handle:
|
||||
operations = list(get_operations_from_log_lines(handle))
|
||||
|
||||
key: tuple[str, int]
|
||||
for key, values in group(
|
||||
source=find_problems(operations),
|
||||
key=(
|
||||
lambda problem: (
|
||||
problem.operation.file,
|
||||
problem.operation.line,
|
||||
)
|
||||
if problem.operation
|
||||
else ("ZZZ", 0)
|
||||
),
|
||||
):
|
||||
operation = values[0].operation
|
||||
if operation:
|
||||
print(operation.file, operation.line)
|
||||
else:
|
||||
print("General problems:")
|
||||
for problem in values:
|
||||
print(" " * 4, problem.message)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,89 +0,0 @@
|
|||
diff --git a/src/memory.c b/src/memory.c
|
||||
index c73c24cf..fbc704f7 100644
|
||||
--- a/src/memory.c
|
||||
+++ b/src/memory.c
|
||||
@@ -1,14 +1,16 @@
|
||||
#include "memory.h"
|
||||
|
||||
#include "game/shell.h"
|
||||
+#include "log.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
-void *Memory_Alloc(size_t size)
|
||||
+void *Memory_AllocImpl(size_t size, const char *file, int line, const char *func)
|
||||
{
|
||||
void *result = malloc(size);
|
||||
+ Log_Message(file, line, func, "Allocating memory (%d bytes): %p", size, result);
|
||||
if (!result) {
|
||||
Shell_ExitSystem("ERROR: Could not allocate enough memory");
|
||||
}
|
||||
@@ -16,33 +18,35 @@ void *Memory_Alloc(size_t size)
|
||||
return result;
|
||||
}
|
||||
|
||||
-void *Memory_Realloc(void *memory, size_t size)
|
||||
+void *Memory_ReallocImpl(void *memory, size_t size, const char *file, int line, const char *func)
|
||||
{
|
||||
void *result = realloc(memory, size);
|
||||
+ Log_Message(file, line, func, "Reallocating memory at %p (%d bytes): %p", memory, size, result);
|
||||
if (!result) {
|
||||
Shell_ExitSystem("ERROR: Could not allocate enough memory");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
-void Memory_Free(void *memory)
|
||||
+void Memory_FreeImpl(void *memory, const char *file, int line, const char *func)
|
||||
{
|
||||
+ Log_Message(file, line, func, "Freeing memory at %p", memory);
|
||||
if (memory) {
|
||||
free(memory);
|
||||
}
|
||||
}
|
||||
|
||||
-void Memory_FreePointer(void *arg)
|
||||
+void Memory_FreePointerImpl(void *arg, const char *file, int line, const char *func)
|
||||
{
|
||||
assert(arg);
|
||||
void *memory;
|
||||
memcpy(&memory, arg, sizeof(void *));
|
||||
memcpy(arg, &(void *) { NULL }, sizeof(void *));
|
||||
- Memory_Free(memory);
|
||||
+ Memory_FreeImpl(memory, file, line, func);
|
||||
}
|
||||
|
||||
-char *Memory_DupStr(const char *string)
|
||||
+char *Memory_DupStrImpl(const char *string, const char *file, int line, const char *func)
|
||||
{
|
||||
- char *memory = Memory_Alloc(strlen(string) + 1);
|
||||
+ char *memory = Memory_AllocImpl(strlen(string) + 1, file, line, func);
|
||||
strcpy(memory, string);
|
||||
return memory;
|
||||
}
|
||||
diff --git a/src/memory.h b/src/memory.h
|
||||
index 23c23f95..3995bada 100644
|
||||
--- a/src/memory.h
|
||||
+++ b/src/memory.h
|
||||
@@ -5,8 +5,14 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
-void *Memory_Alloc(size_t size);
|
||||
-void *Memory_Realloc(void *memory, size_t size);
|
||||
-void Memory_Free(void *memory);
|
||||
-void Memory_FreePointer(void *memory);
|
||||
-char *Memory_DupStr(const char *string);
|
||||
+void *Memory_AllocImpl(size_t size, const char *file, int line, const char *func);
|
||||
+void *Memory_ReallocImpl(void *memory, size_t size, const char *file, int line, const char *func);
|
||||
+void Memory_FreeImpl(void *memory, const char *file, int line, const char *func);
|
||||
+void Memory_FreePointerImpl(void *memory, const char *file, int line, const char *func);
|
||||
+char *Memory_DupStrImpl(const char *string, const char *file, int line, const char *func);
|
||||
+
|
||||
+#define Memory_Alloc(size) (Memory_AllocImpl(size, __FILE__, __LINE__, __func__))
|
||||
+#define Memory_Realloc(memory, size) (Memory_ReallocImpl(memory, size, __FILE__, __LINE__, __func__))
|
||||
+#define Memory_Free(memory) (Memory_FreeImpl(memory, __FILE__, __LINE__, __func__))
|
||||
+#define Memory_FreePointer(memory) (Memory_FreePointerImpl(memory, __FILE__, __LINE__, __func__))
|
||||
+#define Memory_DupStr(string) (Memory_DupStrImpl(string, __FILE__, __LINE__, __func__))
|
|
@ -3,11 +3,10 @@ import argparse
|
|||
from pathlib import Path
|
||||
|
||||
import pyjson5
|
||||
from tr1x.paths import TR1X_TOOLS_DIR
|
||||
|
||||
TOOLS_DIR = Path(__file__).parent
|
||||
REPO_DIR = TOOLS_DIR.parent
|
||||
CONFIG_TOOL_SPEC_PATH = (
|
||||
TOOLS_DIR / "config/TR1X_ConfigTool/Resources/specification.json"
|
||||
TR1X_TOOLS_DIR / "config/TR1X_ConfigTool/Resources/specification.json"
|
||||
)
|
||||
|
||||
|
||||
|
@ -36,16 +35,22 @@ def main() -> None:
|
|||
}
|
||||
|
||||
for key, spec_value in spec_map.items():
|
||||
if key in game_config and (game_value := game_config.get(key)) != spec_value:
|
||||
print(f'(!) Wrong value: {key} (tool supplies {spec_value}, game supplies {game_value})')
|
||||
if (
|
||||
key in game_config
|
||||
and (game_value := game_config.get(key)) != spec_value
|
||||
):
|
||||
print(
|
||||
f"(!) Wrong value: {key} (tool supplies {spec_value}, game supplies {game_value})"
|
||||
)
|
||||
|
||||
for key, spec_value in spec_map.items():
|
||||
if key not in game_config:
|
||||
print(f'Surplus key: {key}')
|
||||
print(f"Surplus key: {key}")
|
||||
|
||||
for key, spec_value in game_config.items():
|
||||
if key not in spec_map:
|
||||
print(f'Missing key: {key}')
|
||||
print(f"Missing key: {key}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
|
||||
from shared.docker import BaseGameEntrypoint
|
||||
from libtrx.cli.game_docker_entrypoint import run_script
|
||||
|
||||
|
||||
class LinuxEntrypoint(BaseGameEntrypoint):
|
||||
BUILD_ROOT = Path("/app/build/linux/")
|
||||
COMPILE_ARGS = []
|
||||
RELEASE_ZIP_SUFFIX = "Linux"
|
||||
RELEASE_ZIP_FILES = [
|
||||
(BUILD_ROOT / "TR1X", "TR1X"),
|
||||
]
|
||||
|
||||
def post_compile(self) -> None:
|
||||
if self.target == "release":
|
||||
self.compress_exe(self.BUILD_ROOT / "TR1X")
|
||||
|
||||
if __name__ == "__main__":
|
||||
LinuxEntrypoint().run()
|
||||
run_script(
|
||||
ship_dir=Path("/app/data/ship/"),
|
||||
build_root=Path("/app/build/linux/"),
|
||||
compile_args=[],
|
||||
release_zip_filename="TR1X-{version}-Linux.zip",
|
||||
release_zip_files=[
|
||||
(Path("/app/build/linux/TR1X"), "TR1X"),
|
||||
],
|
||||
compressable_exes=[
|
||||
Path("/app/build/linux/TR1X"),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
|
||||
from shared.docker import BaseGameEntrypoint
|
||||
from libtrx.cli.game_docker_entrypoint import run_script
|
||||
|
||||
|
||||
class WindowsEntrypoint(BaseGameEntrypoint):
|
||||
BUILD_ROOT = Path("/app/build/win/")
|
||||
COMPILE_ARGS = [
|
||||
run_script(
|
||||
ship_dir=Path("/app/data/ship/"),
|
||||
build_root=Path("/app/build/win/"),
|
||||
compile_args=[
|
||||
"--cross",
|
||||
"/app/tools/docker/game-win/meson_linux_mingw32.txt",
|
||||
]
|
||||
RELEASE_ZIP_SUFFIX = "Windows"
|
||||
RELEASE_ZIP_FILES = [
|
||||
(BUILD_ROOT / "TR1X.exe", "TR1X.exe"),
|
||||
(Path("/app/tools/config/out/TR1X_ConfigTool.exe"), "TR1X_ConfigTool.exe"),
|
||||
]
|
||||
|
||||
def post_compile(self) -> None:
|
||||
if self.target == "release":
|
||||
for path in self.BUILD_ROOT.glob("*.exe"):
|
||||
self.compress_exe(path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
WindowsEntrypoint().run()
|
||||
],
|
||||
release_zip_filename="TR1X-{version}-Windows.zip",
|
||||
release_zip_files=[
|
||||
(Path("/app/build/win/TR1X.exe"), "TR1X.exe"),
|
||||
(
|
||||
Path("/app/tools/config/out/TR1X_ConfigTool.exe"),
|
||||
"TR1X_ConfigTool.exe",
|
||||
),
|
||||
],
|
||||
compressable_exes=[
|
||||
Path("/app/build/win/TR1X.exe"),
|
||||
],
|
||||
)
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# regenerate the .ICO file from .PSD.
|
||||
import argparse
|
||||
import tempfile
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from subprocess import check_call
|
||||
|
||||
|
||||
@dataclass
|
||||
class IconSpec:
|
||||
size: int
|
||||
type: str
|
||||
|
||||
|
||||
SPECS = [
|
||||
IconSpec(size=32, type="bmp"),
|
||||
IconSpec(size=16, type="bmp"),
|
||||
IconSpec(size=256, type="png"),
|
||||
IconSpec(size=128, type="png"),
|
||||
IconSpec(size=64, type="png"),
|
||||
IconSpec(size=48, type="png"),
|
||||
IconSpec(size=32, type="png"),
|
||||
IconSpec(size=16, type="png"),
|
||||
]
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("path", type=Path)
|
||||
parser.add_argument("-o", "--output", type=Path, required=True)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def resize_transformer(path: Path, spec: IconSpec) -> None:
|
||||
check_call(
|
||||
[
|
||||
"convert",
|
||||
f"{path}[0]",
|
||||
"-filter",
|
||||
"lanczos",
|
||||
"-resize",
|
||||
f"{spec.size}x{spec.size}",
|
||||
f'PNG:{path}',
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def quantize_transformer(path: Path, spec: IconSpec) -> None:
|
||||
quantized_path = path.with_stem(f"{path.stem}-quantized")
|
||||
check_call(["pngquant", path, "--output", quantized_path])
|
||||
path.write_bytes(quantized_path.read_bytes())
|
||||
quantized_path.unlink()
|
||||
|
||||
|
||||
def optimize_transformer(path: Path, spec: IconSpec) -> None:
|
||||
check_call(["zopflipng", "-y", path, path])
|
||||
|
||||
|
||||
def convert_transformer(path: Path, spec: IconSpec) -> None:
|
||||
if spec.type != "png":
|
||||
check_call(["convert", path, f'{spec.type.upper()}:{path}'])
|
||||
|
||||
|
||||
TRANSFORMERS = [
|
||||
resize_transformer,
|
||||
quantize_transformer,
|
||||
optimize_transformer,
|
||||
convert_transformer,
|
||||
]
|
||||
|
||||
|
||||
def generate_icon(source_path: Path, target_path: Path) -> None:
|
||||
aux_paths = []
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
tmp_path = Path(tmpdir)
|
||||
for spec in SPECS:
|
||||
aux_path = tmp_path / f"{spec.size}-{spec.type}.tmp"
|
||||
aux_path.write_bytes(source_path.read_bytes())
|
||||
for transform in TRANSFORMERS:
|
||||
transform(aux_path, spec)
|
||||
|
||||
aux_paths.append(aux_path)
|
||||
|
||||
# NOTE: image order is important for certain software.
|
||||
check_call(["identify", *aux_paths])
|
||||
check_call(["convert", *aux_paths, target_path])
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
|
||||
if args.output.exists():
|
||||
args.output.unlink()
|
||||
generate_icon(args.path, args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -3,10 +3,10 @@ import argparse
|
|||
from pathlib import Path
|
||||
from subprocess import check_call
|
||||
|
||||
from common import DATA_DIR
|
||||
from tr1x.paths import TR1X_DATA_DIR
|
||||
|
||||
SOURCE_DIR = DATA_DIR / "images"
|
||||
TARGET_DIR = DATA_DIR / "ship/data/images"
|
||||
SOURCE_DIR = TR1X_DATA_DIR / "images"
|
||||
TARGET_DIR = TR1X_DATA_DIR / "ship/data/images"
|
||||
|
||||
|
||||
def format_size(size: int) -> str:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
from shared.versioning import generate_version
|
||||
from libtrx.versioning import generate_version
|
||||
|
||||
TEMPLATE = """
|
||||
const char *g_TR1XVersion = "TR1X {version}";
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
from shared.common import DATA_DIR
|
||||
from shared.versioning import generate_version
|
||||
from libtrx.versioning import generate_version
|
||||
from tr1x.paths import TR1X_DATA_DIR
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
|
@ -17,7 +17,7 @@ def write_rc_template(
|
|||
) -> None:
|
||||
template = input_path.read_text()
|
||||
template = template.replace("{version}", version)
|
||||
template = template.replace("{icon_path}", str(DATA_DIR / "icon.ico"))
|
||||
template = template.replace("{icon_path}", str(TR1X_DATA_DIR / "icon.ico"))
|
||||
output_path.write_text(template)
|
||||
|
||||
|
||||
|
@ -27,7 +27,7 @@ def main() -> None:
|
|||
|
||||
for output_path in args.output:
|
||||
write_rc_template(
|
||||
input_path=DATA_DIR / output_path.name,
|
||||
input_path=TR1X_DATA_DIR / output_path.name,
|
||||
output_path=output_path,
|
||||
version=version,
|
||||
)
|
||||
|
|
|
@ -1,25 +1,4 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from subprocess import run
|
||||
from libtrx.versioning import generate_version
|
||||
|
||||
from shared.versioning import generate_version
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-o", "--output", type=Path)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
version = generate_version()
|
||||
if args.output:
|
||||
args.output.write_text(version)
|
||||
else:
|
||||
print(version, end="")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
print(generate_version(), end="")
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import struct
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
|
||||
import bson
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("path", type=Path)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
with args.path.open("rb") as handle:
|
||||
magic = handle.read(4)
|
||||
version, compressed_size, uncompressed_size = struct.unpack(
|
||||
"III", handle.read(12)
|
||||
)
|
||||
data = bson.loads(zlib.decompress(handle.read(uncompressed_size)))
|
||||
print(json.dumps(data, indent=4))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
1
tools/libtrx
Symbolic link
1
tools/libtrx
Symbolic link
|
@ -0,0 +1 @@
|
|||
../subprojects/libtrx/tools/libtrx
|
|
@ -1,28 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
import re
|
||||
from pathlib import Path
|
||||
from libtrx.changelog import get_current_version_changelog
|
||||
from tr1x.paths import TR1X_REPO_DIR
|
||||
|
||||
TOOLS_DIR = Path(__file__).parent
|
||||
ROOT_DIR = TOOLS_DIR.parent
|
||||
CHANGELOG_PATH = ROOT_DIR / "CHANGELOG.md"
|
||||
|
||||
|
||||
def get_current_changelog() -> str:
|
||||
sections = [
|
||||
section
|
||||
for section in CHANGELOG_PATH.read_text().split("\n\n")
|
||||
if re.search(r"- \w", section)
|
||||
]
|
||||
if sections:
|
||||
section = sections[0]
|
||||
return "\n".join(
|
||||
line for line in section.splitlines() if not line.startswith("#")
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print(get_current_changelog())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
print(get_current_version_changelog(TR1X_REPO_DIR / "CHANGELOG.md"))
|
||||
|
|
169
tools/release
169
tools/release
|
@ -1,165 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from subprocess import check_output, run
|
||||
from libtrx.cli.release import run_script
|
||||
from tr1x.paths import TR1X_REPO_DIR
|
||||
|
||||
from shared.common import REPO_DIR
|
||||
from shared.versioning import get_branch_version
|
||||
|
||||
HEADER = "## [Unreleased](https://github.com/LostArtefacts/TR1X/compare/stable...develop) - ××××-××-××"
|
||||
CHANGELOG_PATH = REPO_DIR / "CHANGELOG.md"
|
||||
|
||||
|
||||
def update_changelog(
|
||||
changelog: str, old_version: str, new_version: str
|
||||
) -> str:
|
||||
if f"[{new_version}]" in changelog:
|
||||
return changelog
|
||||
changelog = re.sub("Unreleased", new_version, changelog, count=1)
|
||||
changelog = re.sub("stable", old_version, changelog, count=1)
|
||||
changelog = re.sub("develop", new_version, changelog, count=1)
|
||||
changelog = re.sub(
|
||||
"××××-××-××", datetime.now().strftime("%Y-%m-%d"), changelog
|
||||
)
|
||||
changelog = HEADER + "\n\n" + changelog
|
||||
return changelog
|
||||
|
||||
|
||||
class Git:
|
||||
def checkout_branch(self, branch_name: str) -> None:
|
||||
if check_output(["git", "diff", "--cached", "--name-only"]):
|
||||
raise RuntimeError("Staged files")
|
||||
check_output(["git", "checkout", branch_name])
|
||||
|
||||
def reset(self, target: str, hard: bool = False) -> None:
|
||||
check_output(
|
||||
["git", "reset", "develop", *(["--hard"] if hard else [])]
|
||||
)
|
||||
|
||||
def delete_tag(self, tag_name: str) -> None:
|
||||
run(["git", "tag", "-d", tag_name])
|
||||
|
||||
def create_tag(self, tag_name: str) -> None:
|
||||
check_output(["git", "tag", tag_name])
|
||||
|
||||
def add(self, target: str) -> None:
|
||||
check_output(["git", "add", target])
|
||||
|
||||
def commit(self, message: str) -> None:
|
||||
check_output(["git", "commit", "-m", message])
|
||||
|
||||
def push(
|
||||
self, upstream: str, targets: list[str], force: bool = False
|
||||
) -> None:
|
||||
check_output(
|
||||
[
|
||||
"git",
|
||||
"push",
|
||||
upstream,
|
||||
*targets,
|
||||
*(["--force-with-lease"] if force else []),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class BaseCommand:
|
||||
name: str = NotImplemented
|
||||
help: str = NotImplemented
|
||||
|
||||
def __init__(self, git: Git) -> None:
|
||||
self.git = git
|
||||
|
||||
def decorate_parser(self, parser: argparse.ArgumentParser) -> None:
|
||||
parser.add_argument("version")
|
||||
|
||||
def run(self, args: argparse.Namespace) -> None:
|
||||
raise NotImplementedError("not implemented")
|
||||
|
||||
|
||||
class CommitCommand(BaseCommand):
|
||||
name = "commit"
|
||||
help = "Create and tag a commit with the release information"
|
||||
|
||||
def decorate_parser(self, parser: argparse.ArgumentParser) -> None:
|
||||
super().decorate_parser(parser)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--dry-run",
|
||||
action='store_true',
|
||||
help="only output the changelog to stdout, do not commit anything",
|
||||
)
|
||||
|
||||
def run(self, args: argparse.Namespace) -> None:
|
||||
self.git.checkout_branch("develop")
|
||||
old_version = get_branch_version("origin/stable")
|
||||
new_version = args.version
|
||||
|
||||
old_changelog = CHANGELOG_PATH.read_text()
|
||||
new_changelog = update_changelog(
|
||||
old_changelog, old_version, args.version
|
||||
)
|
||||
if old_changelog == new_changelog:
|
||||
return
|
||||
|
||||
if args.dry_run:
|
||||
print(new_changelog)
|
||||
return
|
||||
|
||||
CHANGELOG_PATH.write_text(new_changelog)
|
||||
self.git.add(CHANGELOG_PATH)
|
||||
self.git.commit(f"docs: release {args.version}")
|
||||
self.git.delete_tag(args.version)
|
||||
self.git.create_tag(args.version)
|
||||
|
||||
|
||||
class BranchCommand(BaseCommand):
|
||||
name = "branch"
|
||||
help = "Merge branch to the specified tag"
|
||||
|
||||
def run(self, args: argparse.Namespace) -> None:
|
||||
self.git.checkout_branch("stable")
|
||||
self.git.reset(args.version, hard=True)
|
||||
self.git.checkout_branch("develop")
|
||||
|
||||
|
||||
class PushCommand(BaseCommand):
|
||||
name = "push"
|
||||
help = (
|
||||
"Push the develop and stable branches, and the version tag to GitHub"
|
||||
)
|
||||
|
||||
def run(self, args) -> None:
|
||||
self.git.push(
|
||||
"origin", ["develop", "stable", args.version], force=True
|
||||
)
|
||||
|
||||
|
||||
def parse_args(commands: list[BaseCommand]) -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Argument parser with subcommands"
|
||||
)
|
||||
subparsers = parser.add_subparsers(title="subcommands", dest="subcommand")
|
||||
for command in commands:
|
||||
subparser = subparsers.add_parser(command.name, help=command.help)
|
||||
command.decorate_parser(subparser)
|
||||
subparser.set_defaults(command=command)
|
||||
|
||||
result = parser.parse_args()
|
||||
if not hasattr(result, "command"):
|
||||
parser.error("missing command")
|
||||
return result
|
||||
|
||||
|
||||
def main() -> None:
|
||||
git = Git()
|
||||
commands = [
|
||||
command_cls(git) for command_cls in BaseCommand.__subclasses__()
|
||||
]
|
||||
args = parse_args(commands)
|
||||
args.command.run(args)
|
||||
|
||||
|
||||
main()
|
||||
run_script(
|
||||
project_name="TR1X",
|
||||
changelog_path=TR1X_REPO_DIR / "CHANGELOG.md",
|
||||
)
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
from pathlib import Path
|
||||
|
||||
TOOLS_DIR = Path(__file__).parent.parent
|
||||
REPO_DIR = TOOLS_DIR.parent
|
||||
DATA_DIR = REPO_DIR / "data"
|
||||
SRC_DIR = REPO_DIR / "src"
|
|
@ -1,92 +0,0 @@
|
|||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
from subprocess import check_call, run
|
||||
|
||||
from shared.common import DATA_DIR
|
||||
from shared.packaging import create_zip
|
||||
from shared.versioning import generate_version
|
||||
|
||||
SHIP_DIR = DATA_DIR / "ship"
|
||||
|
||||
|
||||
class BaseGameEntrypoint:
|
||||
BUILD_ROOT: Path = ...
|
||||
COMPILE_ARGS: list[str] = ...
|
||||
STRIP_TOOL = "strip"
|
||||
UPX_TOOL = "upx"
|
||||
RELEASE_ZIP_SUFFIX: str = ...
|
||||
RELEASE_ZIP_FILES: list[tuple[Path, str]] = ...
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.target = os.environ.get("TARGET", "debug")
|
||||
|
||||
def run(self) -> None:
|
||||
args = self.parse_args()
|
||||
args.func(args)
|
||||
|
||||
def parse_args(self) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Docker entrypoint")
|
||||
subparsers = parser.add_subparsers(dest="action", help="Subcommands")
|
||||
|
||||
compile_parser = subparsers.add_parser(
|
||||
"compile", help="Compile action"
|
||||
)
|
||||
compile_parser.set_defaults(func=self.compile)
|
||||
|
||||
package_parser = subparsers.add_parser(
|
||||
"package", help="Package action"
|
||||
)
|
||||
package_parser.add_argument("-o", "--output", type=Path)
|
||||
package_parser.set_defaults(func=self.package)
|
||||
|
||||
args = parser.parse_args()
|
||||
if not hasattr(args, "func"):
|
||||
args.action = "compile"
|
||||
args.func = self.compile
|
||||
return args
|
||||
|
||||
def compile(self, args: argparse.Namespace) -> None:
|
||||
pkg_config_path = os.environ["PKG_CONFIG_PATH"]
|
||||
|
||||
if not Path("/app/build/linux/build.jinja").exists():
|
||||
check_call(
|
||||
[
|
||||
"meson",
|
||||
"--buildtype",
|
||||
self.target,
|
||||
*self.COMPILE_ARGS,
|
||||
self.BUILD_ROOT,
|
||||
"--pkg-config-path",
|
||||
pkg_config_path,
|
||||
]
|
||||
)
|
||||
|
||||
check_call(["meson", "compile"], cwd=self.BUILD_ROOT)
|
||||
|
||||
self.post_compile()
|
||||
|
||||
def post_compile(self) -> None:
|
||||
pass
|
||||
|
||||
def compress_exe(self, path: Path) -> None:
|
||||
if run([self.UPX_TOOL, "-t", str(path)]).returncode != 0:
|
||||
check_call([self.STRIP_TOOL, str(path)])
|
||||
check_call([self.UPX_TOOL, str(path)])
|
||||
|
||||
def package(self, args: argparse.Namespace) -> None:
|
||||
if args.output:
|
||||
zip_path = args.output
|
||||
else:
|
||||
version = generate_version()
|
||||
zip_path = Path(f"TR1X-{version}-{self.RELEASE_ZIP_SUFFIX}.zip")
|
||||
source_files = [
|
||||
*[
|
||||
(path, path.relative_to(SHIP_DIR))
|
||||
for path in SHIP_DIR.rglob("*")
|
||||
if path.is_file()
|
||||
],
|
||||
*self.RELEASE_ZIP_FILES,
|
||||
]
|
||||
create_zip(zip_path, source_files)
|
||||
print(f"Created {zip_path}")
|
|
@ -1,15 +0,0 @@
|
|||
import sys
|
||||
import zipfile
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def create_zip(
|
||||
output_path: Path, source_files: Iterable[tuple[Path, str]]
|
||||
) -> None:
|
||||
with zipfile.ZipFile(output_path, "w") as handle:
|
||||
for source_path, archive_name in source_files:
|
||||
if not source_path.exists():
|
||||
print(f"WARNING: {source_path} does not exist", file=sys.stderr)
|
||||
continue
|
||||
handle.write(source_path, archive_name)
|
|
@ -1,27 +0,0 @@
|
|||
from subprocess import run
|
||||
|
||||
from shared.common import SRC_DIR
|
||||
|
||||
|
||||
def get_branch_version(branch: str | None) -> str:
|
||||
return run(
|
||||
[
|
||||
"git",
|
||||
"describe",
|
||||
*([branch] if branch else ["--dirty"]),
|
||||
"--always",
|
||||
"--abbrev=7",
|
||||
"--tags",
|
||||
"--exclude",
|
||||
"latest",
|
||||
],
|
||||
cwd=SRC_DIR,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
).stdout.strip()
|
||||
|
||||
|
||||
def generate_version() -> str:
|
||||
version = get_branch_version(None)
|
||||
return version or "?"
|
|
@ -1,41 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import re
|
||||
from pathlib import Path
|
||||
from shutil import which
|
||||
from subprocess import run
|
||||
from libtrx.cli.sort_imports import run_script
|
||||
from tr1x.paths import TR1X_REPO_DIR, TR1X_SRC_DIR
|
||||
|
||||
from shared.common import SRC_DIR
|
||||
|
||||
|
||||
def fix_imports(path: Path) -> None:
|
||||
iwyu_result = run(
|
||||
["include-what-you-use", "-I", "src", path],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
).stderr
|
||||
run(
|
||||
[which("fix_include") and "fix_include" or "iwyu-fix-includes"],
|
||||
input=iwyu_result,
|
||||
text=True,
|
||||
)
|
||||
|
||||
|
||||
def custom_sort(source: list[str], forced_order: list[str]) -> list[str]:
|
||||
def key_func(item: str) -> tuple[int, int, str]:
|
||||
if item in forced_order:
|
||||
return (forced_order[0], forced_order.index(item))
|
||||
return (item, 0)
|
||||
|
||||
return sorted(source, key=key_func)
|
||||
|
||||
|
||||
def sort_imports(path: Path) -> None:
|
||||
source = path.read_text()
|
||||
rel_path = path.relative_to(SRC_DIR)
|
||||
own_include = str(rel_path.with_suffix(".h"))
|
||||
own_include = {
|
||||
# files headers of which are not a 1:1 match with their filename
|
||||
run_script(
|
||||
root_dir=TR1X_SRC_DIR,
|
||||
include_dirs=[
|
||||
TR1X_SRC_DIR,
|
||||
TR1X_REPO_DIR / "build/linux",
|
||||
TR1X_REPO_DIR / "build/windows",
|
||||
],
|
||||
system_include_dirs=[TR1X_REPO_DIR / "subprojects/libtrx/include"],
|
||||
own_include_map={
|
||||
"game/game/game.c": "game/game.h",
|
||||
"game/game/game_cutscene.c": "game/game.h",
|
||||
"game/game/game_demo.c": "game/game.h",
|
||||
|
@ -50,77 +25,8 @@ def sort_imports(path: Path) -> None:
|
|||
"game/savegame/savegame.c": "game/savegame.h",
|
||||
"specific/s_audio_sample.c": "specific/s_audio.h",
|
||||
"specific/s_audio_stream.c": "specific/s_audio.h",
|
||||
"specific/s_log_unknown.c": "specific/s_log.h",
|
||||
"specific/s_log_linux.c": "specific/s_log.h",
|
||||
"specific/s_log_windows.c": "specific/s_log.h",
|
||||
}.get(str(rel_path), own_include)
|
||||
|
||||
forced_order = [
|
||||
"<windows.h>",
|
||||
"<dbghelp.h>",
|
||||
"<tlhelp32.h>",
|
||||
]
|
||||
|
||||
def cb(match):
|
||||
includes = re.findall(r'#include (["<][^"<>]+[">])', match.group(0))
|
||||
groups = {
|
||||
"self": set(),
|
||||
"local": set(),
|
||||
"shared": set(),
|
||||
"external": set(),
|
||||
}
|
||||
for include in includes:
|
||||
if include.strip('"') == own_include:
|
||||
groups["self"].add(include)
|
||||
elif include.startswith("<libtrx"):
|
||||
groups["shared"].add(include)
|
||||
elif include.startswith("<"):
|
||||
groups["external"].add(include)
|
||||
elif include.startswith('"'):
|
||||
groups["local"].add(include)
|
||||
|
||||
groups = {key: value for key, value in groups.items() if value}
|
||||
|
||||
ret = "\n\n".join(
|
||||
"\n".join(
|
||||
f"#include {include}"
|
||||
for include in custom_sort(group, forced_order)
|
||||
)
|
||||
for group in groups.values()
|
||||
).strip()
|
||||
return ret
|
||||
|
||||
source = re.sub(
|
||||
"^#include [^\n]+(\n*#include [^\n]+)*",
|
||||
cb,
|
||||
source,
|
||||
flags=re.M,
|
||||
)
|
||||
if source != path.read_text():
|
||||
path.write_text(source)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(metavar="path", type=Path, nargs="*", dest="paths")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
paths = [path.absolute() for path in args.paths]
|
||||
|
||||
if not paths:
|
||||
paths = sorted(
|
||||
path
|
||||
for path in SRC_DIR.glob("**/*.[ch]")
|
||||
if path != SRC_DIR / "init.c"
|
||||
)
|
||||
|
||||
for path in paths:
|
||||
fix_imports(path)
|
||||
sort_imports(path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"specific/s_shell.c": "specific/s_shell.h",
|
||||
},
|
||||
fix_map={"libavcodec/version_major.h": "libavcodec/version.h"},
|
||||
forced_order=[],
|
||||
)
|
||||
|
|
6
tools/tr1x/paths.py
Normal file
6
tools/tr1x/paths.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from pathlib import Path
|
||||
|
||||
TR1X_TOOLS_DIR = Path(__file__).parent.parent
|
||||
TR1X_REPO_DIR = TR1X_TOOLS_DIR.parent
|
||||
TR1X_DATA_DIR = TR1X_REPO_DIR / "data"
|
||||
TR1X_SRC_DIR = TR1X_REPO_DIR / "src"
|
|
@ -2,10 +2,10 @@
|
|||
import json
|
||||
import re
|
||||
|
||||
from shared.common import DATA_DIR, SRC_DIR
|
||||
from tr1x.paths import TR1X_DATA_DIR, TR1X_SRC_DIR
|
||||
|
||||
SHIP_DIR = DATA_DIR / "ship"
|
||||
GAME_STRING_DEF_PATH = SRC_DIR / "game/game_string.def"
|
||||
SHIP_DIR = TR1X_DATA_DIR / "ship"
|
||||
GAME_STRING_DEF_PATH = TR1X_SRC_DIR / "game/game_string.def"
|
||||
|
||||
|
||||
def get_default_string_map() -> dict[str, str]:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue