chore(release): v0.21.0 — empaquetat de recursos (AE26) i neteja de fonts
- Nou format de pack AE26 v1 (XOR + djb2), eina `pack_resources`, regeneració automàtica de `build/resources.pack` a cada build. - `Resource::Helper` amb init/shutdown, fallback a disc només en Debug (apunta al source tree); Release exigeix pack present. - Callers de YAML migrats a `loadFile`+deserialize des de buffer. - Arrel neta: el pack viu a `build/`, eliminat `sync_data`. - Placeholder font -> `ari-w9500-condensed-display`; esborrades variants bold/condensed no usades a `data/fonts/`.
This commit is contained in:
1 parent
aeed0e01c2
commit
4da05b3d76
24 files changed
+577
-628
No files matched your search
+34
-12
@@ -76,6 +76,7 @@ set(APP_SOURCES
|
|||||||
source/core/rendering/sdl3gpu/pipelines/crtpi_pipeline.cpp
|
source/core/rendering/sdl3gpu/pipelines/crtpi_pipeline.cpp
|
||||||
source/core/resources/resource_helper.cpp
|
source/core/resources/resource_helper.cpp
|
||||||
source/core/resources/resource_cache.cpp
|
source/core/resources/resource_cache.cpp
|
||||||
|
source/core/resources/resource_pack.cpp
|
||||||
source/core/resources/palette_loader.cpp
|
source/core/resources/palette_loader.cpp
|
||||||
source/utils/easing.cpp
|
source/utils/easing.cpp
|
||||||
source/utils/utils.cpp
|
source/utils/utils.cpp
|
||||||
@@ -160,24 +161,43 @@ target_include_directories(${PROJECT_NAME} PRIVATE
|
|||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE SDL3::SDL3)
|
target_link_libraries(${PROJECT_NAME} PRIVATE SDL3::SDL3)
|
||||||
|
|
||||||
# Còpia recursiva de data/ al costat del binari perquè les rutes relatives
|
# --- EINA STANDALONE: pack_resources ---
|
||||||
# ("data/player/player.gif") funcionin sense desplegament. Es defineix com
|
# Executable auxiliar que empaqueta `data/` a `build/resources.pack`.
|
||||||
# a custom target amb `ALL` — s'executa a cada `cmake --build` encara que
|
# EXCLUDE_FROM_ALL: només es compila quan algun target en depèn (ho fa
|
||||||
# el binari no hagi canviat, de manera que editar YAML/GIF sense tocar codi
|
# `resource_pack`). Build manual: `cmake --build build --target pack_resources`.
|
||||||
# igualment sincronitza el build. Abans era un POST_BUILD, i si només es
|
add_executable(pack_resources EXCLUDE_FROM_ALL
|
||||||
# modificava una dada la còpia no es disparava.
|
tools/pack_resources/pack_resources.cpp
|
||||||
add_custom_target(sync_data ALL
|
source/core/resources/resource_pack.cpp
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
|
||||||
"${CMAKE_SOURCE_DIR}/data"
|
|
||||||
"${CMAKE_BINARY_DIR}/data"
|
|
||||||
COMMENT "Copying data/ next to the binary"
|
|
||||||
)
|
)
|
||||||
add_dependencies(${PROJECT_NAME} sync_data)
|
target_include_directories(pack_resources PRIVATE "${CMAKE_SOURCE_DIR}/source")
|
||||||
|
target_compile_options(pack_resources PRIVATE -Wall -Wextra -Wpedantic)
|
||||||
|
|
||||||
|
# --- REGENERACIÓ AUTOMÀTICA DE build/resources.pack ---
|
||||||
|
# A cada build re-empaquetem data/ si algun fitxer dins ha canviat. Evita
|
||||||
|
# debugar amb un pack obsolet. CONFIGURE_DEPENDS força CMake a re-globbar
|
||||||
|
# a la pròxima invocació (recull fitxers nous afegits a data/).
|
||||||
|
file(GLOB_RECURSE DATA_FILES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/data/*")
|
||||||
|
set(RESOURCE_PACK "${CMAKE_BINARY_DIR}/resources.pack")
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${RESOURCE_PACK}
|
||||||
|
COMMAND $<TARGET_FILE:pack_resources>
|
||||||
|
"${CMAKE_SOURCE_DIR}/data"
|
||||||
|
"${RESOURCE_PACK}"
|
||||||
|
DEPENDS pack_resources ${DATA_FILES}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
COMMENT "Empaquetant data/ → build/resources.pack"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
add_custom_target(resource_pack ALL DEPENDS ${RESOURCE_PACK})
|
||||||
|
add_dependencies(${PROJECT_NAME} resource_pack)
|
||||||
|
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic)
|
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic)
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||||
$<$<CONFIG:Debug>:_DEBUG>
|
$<$<CONFIG:Debug>:_DEBUG>
|
||||||
$<$<CONFIG:Release>:RELEASE_BUILD>
|
$<$<CONFIG:Release>:RELEASE_BUILD>
|
||||||
|
# En Debug el fallback a disc apunta al source tree (sense re-packejar).
|
||||||
|
AEE_SOURCE_DIR="${CMAKE_SOURCE_DIR}/"
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- STATIC ANALYSIS / FORMAT TARGETS ---
|
# --- STATIC ANALYSIS / FORMAT TARGETS ---
|
||||||
@@ -271,6 +291,7 @@ if(BUILD_TESTS)
|
|||||||
add_executable(test_entities_smoke
|
add_executable(test_entities_smoke
|
||||||
tests/test_entities_smoke.cpp
|
tests/test_entities_smoke.cpp
|
||||||
source/core/resources/resource_helper.cpp
|
source/core/resources/resource_helper.cpp
|
||||||
|
source/core/resources/resource_pack.cpp
|
||||||
source/game/map/map.cpp
|
source/game/map/map.cpp
|
||||||
${ENTITY_SOURCES}
|
${ENTITY_SOURCES}
|
||||||
)
|
)
|
||||||
@@ -295,6 +316,7 @@ if(BUILD_TESTS)
|
|||||||
source/game/scenes/placeholder_input.cpp
|
source/game/scenes/placeholder_input.cpp
|
||||||
source/core/input/key_config.cpp
|
source/core/input/key_config.cpp
|
||||||
source/core/resources/resource_helper.cpp
|
source/core/resources/resource_helper.cpp
|
||||||
|
source/core/resources/resource_pack.cpp
|
||||||
)
|
)
|
||||||
target_include_directories(test_scene_flow PRIVATE
|
target_include_directories(test_scene_flow PRIVATE
|
||||||
"${CMAKE_SOURCE_DIR}/source"
|
"${CMAKE_SOURCE_DIR}/source"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ endif
|
|||||||
|
|
||||||
CMAKE_DEFS := -DGIT_HASH=$(GIT_HASH)
|
CMAKE_DEFS := -DGIT_HASH=$(GIT_HASH)
|
||||||
|
|
||||||
.PHONY: all debug release run run-debug clean rebuild show-version format format-check tidy tidy-fix cppcheck hooks-install help
|
.PHONY: all debug release run run-debug clean rebuild show-version pack format format-check tidy tidy-fix cppcheck hooks-install help
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# COMPILACIÓ
|
# COMPILACIÓ
|
||||||
@@ -53,6 +53,14 @@ clean:
|
|||||||
|
|
||||||
rebuild: clean all
|
rebuild: clean all
|
||||||
|
|
||||||
|
# Empaqueta data/ a $(BUILDDIR)/resources.pack (format AE26). Força un rebuild
|
||||||
|
# del pack encara que res no hagi canviat dins data/. L'empaquetat també es fa
|
||||||
|
# automàticament a cada `make`/`make debug` via el target CMake `resource_pack`.
|
||||||
|
pack:
|
||||||
|
@cmake -S . -B $(BUILDDIR) -DCMAKE_BUILD_TYPE=Release $(CMAKE_DEFS)
|
||||||
|
@cmake --build $(BUILDDIR) --target pack_resources
|
||||||
|
@./$(BUILDDIR)/pack_resources data $(BUILDDIR)/resources.pack
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# CODE QUALITY (delegados a cmake)
|
# CODE QUALITY (delegados a cmake)
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -100,6 +108,9 @@ help:
|
|||||||
@echo " Release:"
|
@echo " Release:"
|
||||||
@echo " make release - Release + empaquetat (TODO pendent)"
|
@echo " make release - Release + empaquetat (TODO pendent)"
|
||||||
@echo ""
|
@echo ""
|
||||||
|
@echo " Recursos:"
|
||||||
|
@echo " make pack - Empaquetar data/ a $(BUILDDIR)/resources.pack (format AE26)"
|
||||||
|
@echo ""
|
||||||
@echo " Qualitat de codi:"
|
@echo " Qualitat de codi:"
|
||||||
@echo " make format - Formatar codi amb clang-format"
|
@echo " make format - Formatar codi amb clang-format"
|
||||||
@echo " make format-check - Verificar format sense modificar"
|
@echo " make format-check - Verificar format sense modificar"
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
# Font: ari-w9500-bold — generado desde ari-w9500-bold.ttf size 11
|
|
||||||
# Generado con tools/font_gen/font_gen.py
|
|
||||||
|
|
||||||
box_width 13
|
|
||||||
box_height 11
|
|
||||||
columns 15
|
|
||||||
|
|
||||||
# codepoint_decimal ancho_visual
|
|
||||||
32 3 # U+0020
|
|
||||||
33 3 # !
|
|
||||||
34 6 # "
|
|
||||||
35 9 # #
|
|
||||||
36 7 # $
|
|
||||||
37 12 # %
|
|
||||||
38 8 # &
|
|
||||||
39 3 # '
|
|
||||||
40 5 # (
|
|
||||||
41 5 # )
|
|
||||||
42 5 # *
|
|
||||||
43 7 # +
|
|
||||||
44 3 # ,
|
|
||||||
45 5 # -
|
|
||||||
46 3 # .
|
|
||||||
47 6 # /
|
|
||||||
48 7 # 0
|
|
||||||
49 6 # 1
|
|
||||||
50 7 # 2
|
|
||||||
51 7 # 3
|
|
||||||
52 7 # 4
|
|
||||||
53 7 # 5
|
|
||||||
54 7 # 6
|
|
||||||
55 7 # 7
|
|
||||||
56 7 # 8
|
|
||||||
57 7 # 9
|
|
||||||
58 3 # :
|
|
||||||
59 3 # ;
|
|
||||||
60 7 # <
|
|
||||||
61 7 # =
|
|
||||||
62 7 # >
|
|
||||||
63 7 # ?
|
|
||||||
64 13 # @
|
|
||||||
65 9 # A
|
|
||||||
66 8 # B
|
|
||||||
67 8 # C
|
|
||||||
68 8 # D
|
|
||||||
69 7 # E
|
|
||||||
70 7 # F
|
|
||||||
71 8 # G
|
|
||||||
72 8 # H
|
|
||||||
73 3 # I
|
|
||||||
74 6 # J
|
|
||||||
75 8 # K
|
|
||||||
76 7 # L
|
|
||||||
77 10 # M
|
|
||||||
78 9 # N
|
|
||||||
79 9 # O
|
|
||||||
80 8 # P
|
|
||||||
81 9 # Q
|
|
||||||
82 8 # R
|
|
||||||
83 8 # S
|
|
||||||
84 7 # T
|
|
||||||
85 8 # U
|
|
||||||
86 8 # V
|
|
||||||
87 11 # W
|
|
||||||
88 9 # X
|
|
||||||
89 9 # Y
|
|
||||||
90 9 # Z
|
|
||||||
91 5 # [
|
|
||||||
92 6 # \
|
|
||||||
93 5 # ]
|
|
||||||
94 6 # ^
|
|
||||||
95 7 # _
|
|
||||||
96 4 # `
|
|
||||||
97 7 # a
|
|
||||||
98 7 # b
|
|
||||||
99 7 # c
|
|
||||||
100 7 # d
|
|
||||||
101 7 # e
|
|
||||||
102 5 # f
|
|
||||||
103 7 # g
|
|
||||||
104 7 # h
|
|
||||||
105 3 # i
|
|
||||||
106 3 # j
|
|
||||||
107 7 # k
|
|
||||||
108 3 # l
|
|
||||||
109 9 # m
|
|
||||||
110 7 # n
|
|
||||||
111 7 # o
|
|
||||||
112 7 # p
|
|
||||||
113 7 # q
|
|
||||||
114 5 # r
|
|
||||||
115 6 # s
|
|
||||||
116 5 # t
|
|
||||||
117 7 # u
|
|
||||||
118 7 # v
|
|
||||||
119 9 # w
|
|
||||||
120 7 # x
|
|
||||||
121 7 # y
|
|
||||||
122 7 # z
|
|
||||||
123 6 # {
|
|
||||||
124 3 # |
|
|
||||||
125 6 # }
|
|
||||||
126 8 # ~
|
|
||||||
192 9 # À
|
|
||||||
193 9 # Á
|
|
||||||
200 7 # È
|
|
||||||
201 7 # É
|
|
||||||
204 3 # Ì
|
|
||||||
205 3 # Í
|
|
||||||
210 9 # Ò
|
|
||||||
211 9 # Ó
|
|
||||||
217 8 # Ù
|
|
||||||
218 8 # Ú
|
|
||||||
209 9 # Ñ
|
|
||||||
199 8 # Ç
|
|
||||||
224 7 # à
|
|
||||||
225 7 # á
|
|
||||||
232 7 # è
|
|
||||||
233 7 # é
|
|
||||||
236 3 # ì
|
|
||||||
237 4 # í
|
|
||||||
242 7 # ò
|
|
||||||
243 7 # ó
|
|
||||||
249 7 # ù
|
|
||||||
250 7 # ú
|
|
||||||
241 7 # ñ
|
|
||||||
231 7 # ç
|
|
||||||
161 3 # ¡
|
|
||||||
191 6 # ¿
|
|
||||||
171 9 # «
|
|
||||||
187 9 # »
|
|
||||||
183 3 # ·
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,132 +0,0 @@
|
|||||||
# Font: ari-w9500-condensed-bold — generado desde ari-w9500-condensed-bold.ttf size 11
|
|
||||||
# Generado con tools/font_gen/font_gen.py
|
|
||||||
|
|
||||||
box_width 12
|
|
||||||
box_height 11
|
|
||||||
columns 15
|
|
||||||
|
|
||||||
# codepoint_decimal ancho_visual
|
|
||||||
32 2 # U+0020
|
|
||||||
33 3 # !
|
|
||||||
34 6 # "
|
|
||||||
35 9 # #
|
|
||||||
36 6 # $
|
|
||||||
37 10 # %
|
|
||||||
38 7 # &
|
|
||||||
39 3 # '
|
|
||||||
40 5 # (
|
|
||||||
41 5 # )
|
|
||||||
42 5 # *
|
|
||||||
43 7 # +
|
|
||||||
44 3 # ,
|
|
||||||
45 5 # -
|
|
||||||
46 3 # .
|
|
||||||
47 5 # /
|
|
||||||
48 6 # 0
|
|
||||||
49 5 # 1
|
|
||||||
50 6 # 2
|
|
||||||
51 6 # 3
|
|
||||||
52 6 # 4
|
|
||||||
53 6 # 5
|
|
||||||
54 6 # 6
|
|
||||||
55 6 # 7
|
|
||||||
56 6 # 8
|
|
||||||
57 6 # 9
|
|
||||||
58 3 # :
|
|
||||||
59 3 # ;
|
|
||||||
60 7 # <
|
|
||||||
61 7 # =
|
|
||||||
62 7 # >
|
|
||||||
63 6 # ?
|
|
||||||
64 12 # @
|
|
||||||
65 8 # A
|
|
||||||
66 7 # B
|
|
||||||
67 7 # C
|
|
||||||
68 7 # D
|
|
||||||
69 6 # E
|
|
||||||
70 6 # F
|
|
||||||
71 7 # G
|
|
||||||
72 7 # H
|
|
||||||
73 3 # I
|
|
||||||
74 6 # J
|
|
||||||
75 7 # K
|
|
||||||
76 7 # L
|
|
||||||
77 8 # M
|
|
||||||
78 8 # N
|
|
||||||
79 8 # O
|
|
||||||
80 7 # P
|
|
||||||
81 8 # Q
|
|
||||||
82 7 # R
|
|
||||||
83 7 # S
|
|
||||||
84 7 # T
|
|
||||||
85 7 # U
|
|
||||||
86 7 # V
|
|
||||||
87 9 # W
|
|
||||||
88 7 # X
|
|
||||||
89 7 # Y
|
|
||||||
90 7 # Z
|
|
||||||
91 5 # [
|
|
||||||
92 5 # \
|
|
||||||
93 5 # ]
|
|
||||||
94 6 # ^
|
|
||||||
95 6 # _
|
|
||||||
96 4 # `
|
|
||||||
97 6 # a
|
|
||||||
98 6 # b
|
|
||||||
99 6 # c
|
|
||||||
100 6 # d
|
|
||||||
101 6 # e
|
|
||||||
102 5 # f
|
|
||||||
103 6 # g
|
|
||||||
104 6 # h
|
|
||||||
105 3 # i
|
|
||||||
106 3 # j
|
|
||||||
107 6 # k
|
|
||||||
108 3 # l
|
|
||||||
109 8 # m
|
|
||||||
110 6 # n
|
|
||||||
111 6 # o
|
|
||||||
112 6 # p
|
|
||||||
113 6 # q
|
|
||||||
114 5 # r
|
|
||||||
115 6 # s
|
|
||||||
116 5 # t
|
|
||||||
117 6 # u
|
|
||||||
118 6 # v
|
|
||||||
119 8 # w
|
|
||||||
120 6 # x
|
|
||||||
121 6 # y
|
|
||||||
122 6 # z
|
|
||||||
123 6 # {
|
|
||||||
124 3 # |
|
|
||||||
125 6 # }
|
|
||||||
126 8 # ~
|
|
||||||
192 8 # À
|
|
||||||
193 8 # Á
|
|
||||||
200 6 # È
|
|
||||||
201 6 # É
|
|
||||||
204 3 # Ì
|
|
||||||
205 3 # Í
|
|
||||||
210 8 # Ò
|
|
||||||
211 8 # Ó
|
|
||||||
217 7 # Ù
|
|
||||||
218 7 # Ú
|
|
||||||
209 8 # Ñ
|
|
||||||
199 7 # Ç
|
|
||||||
224 6 # à
|
|
||||||
225 6 # á
|
|
||||||
232 6 # è
|
|
||||||
233 6 # é
|
|
||||||
236 3 # ì
|
|
||||||
237 4 # í
|
|
||||||
242 6 # ò
|
|
||||||
243 6 # ó
|
|
||||||
249 6 # ù
|
|
||||||
250 6 # ú
|
|
||||||
241 6 # ñ
|
|
||||||
231 6 # ç
|
|
||||||
161 3 # ¡
|
|
||||||
191 6 # ¿
|
|
||||||
171 9 # «
|
|
||||||
187 9 # »
|
|
||||||
183 3 # ·
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 KiB |
@@ -1,132 +0,0 @@
|
|||||||
# Font: ari-w9500-condensed — generado desde ari-w9500-condensed.ttf size 11
|
|
||||||
# Generado con tools/font_gen/font_gen.py
|
|
||||||
|
|
||||||
box_width 9
|
|
||||||
box_height 11
|
|
||||||
columns 15
|
|
||||||
|
|
||||||
# codepoint_decimal ancho_visual
|
|
||||||
32 2 # U+0020
|
|
||||||
33 2 # !
|
|
||||||
34 4 # "
|
|
||||||
35 8 # #
|
|
||||||
36 5 # $
|
|
||||||
37 8 # %
|
|
||||||
38 6 # &
|
|
||||||
39 2 # '
|
|
||||||
40 4 # (
|
|
||||||
41 4 # )
|
|
||||||
42 4 # *
|
|
||||||
43 6 # +
|
|
||||||
44 2 # ,
|
|
||||||
45 4 # -
|
|
||||||
46 2 # .
|
|
||||||
47 4 # /
|
|
||||||
48 5 # 0
|
|
||||||
49 4 # 1
|
|
||||||
50 5 # 2
|
|
||||||
51 5 # 3
|
|
||||||
52 5 # 4
|
|
||||||
53 5 # 5
|
|
||||||
54 5 # 6
|
|
||||||
55 5 # 7
|
|
||||||
56 5 # 8
|
|
||||||
57 5 # 9
|
|
||||||
58 2 # :
|
|
||||||
59 2 # ;
|
|
||||||
60 4 # <
|
|
||||||
61 6 # =
|
|
||||||
62 4 # >
|
|
||||||
63 5 # ?
|
|
||||||
64 9 # @
|
|
||||||
65 6 # A
|
|
||||||
66 6 # B
|
|
||||||
67 6 # C
|
|
||||||
68 6 # D
|
|
||||||
69 6 # E
|
|
||||||
70 6 # F
|
|
||||||
71 6 # G
|
|
||||||
72 6 # H
|
|
||||||
73 2 # I
|
|
||||||
74 4 # J
|
|
||||||
75 6 # K
|
|
||||||
76 6 # L
|
|
||||||
77 6 # M
|
|
||||||
78 6 # N
|
|
||||||
79 7 # O
|
|
||||||
80 6 # P
|
|
||||||
81 7 # Q
|
|
||||||
82 6 # R
|
|
||||||
83 6 # S
|
|
||||||
84 6 # T
|
|
||||||
85 6 # U
|
|
||||||
86 6 # V
|
|
||||||
87 8 # W
|
|
||||||
88 6 # X
|
|
||||||
89 6 # Y
|
|
||||||
90 6 # Z
|
|
||||||
91 4 # [
|
|
||||||
92 4 # \
|
|
||||||
93 4 # ]
|
|
||||||
94 6 # ^
|
|
||||||
95 5 # _
|
|
||||||
96 3 # `
|
|
||||||
97 5 # a
|
|
||||||
98 5 # b
|
|
||||||
99 5 # c
|
|
||||||
100 5 # d
|
|
||||||
101 5 # e
|
|
||||||
102 4 # f
|
|
||||||
103 5 # g
|
|
||||||
104 5 # h
|
|
||||||
105 2 # i
|
|
||||||
106 2 # j
|
|
||||||
107 5 # k
|
|
||||||
108 2 # l
|
|
||||||
109 6 # m
|
|
||||||
110 5 # n
|
|
||||||
111 5 # o
|
|
||||||
112 5 # p
|
|
||||||
113 5 # q
|
|
||||||
114 4 # r
|
|
||||||
115 5 # s
|
|
||||||
116 4 # t
|
|
||||||
117 5 # u
|
|
||||||
118 5 # v
|
|
||||||
119 6 # w
|
|
||||||
120 5 # x
|
|
||||||
121 5 # y
|
|
||||||
122 5 # z
|
|
||||||
123 5 # {
|
|
||||||
124 2 # |
|
|
||||||
125 5 # }
|
|
||||||
126 6 # ~
|
|
||||||
192 6 # À
|
|
||||||
193 6 # Á
|
|
||||||
200 6 # È
|
|
||||||
201 6 # É
|
|
||||||
204 2 # Ì
|
|
||||||
205 2 # Í
|
|
||||||
210 7 # Ò
|
|
||||||
211 7 # Ó
|
|
||||||
217 6 # Ù
|
|
||||||
218 6 # Ú
|
|
||||||
209 6 # Ñ
|
|
||||||
199 6 # Ç
|
|
||||||
224 5 # à
|
|
||||||
225 5 # á
|
|
||||||
232 5 # è
|
|
||||||
233 5 # é
|
|
||||||
236 2 # ì
|
|
||||||
237 3 # í
|
|
||||||
242 5 # ò
|
|
||||||
243 5 # ó
|
|
||||||
249 5 # ù
|
|
||||||
250 5 # ú
|
|
||||||
241 5 # ñ
|
|
||||||
231 5 # ç
|
|
||||||
161 2 # ¡
|
|
||||||
191 5 # ¿
|
|
||||||
171 7 # «
|
|
||||||
187 7 # »
|
|
||||||
183 2 # ·
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,132 +0,0 @@
|
|||||||
# Font: ari-w9500 — generado desde ari-w9500.ttf size 11
|
|
||||||
# Generado con tools/font_gen/font_gen.py
|
|
||||||
|
|
||||||
box_width 12
|
|
||||||
box_height 11
|
|
||||||
columns 15
|
|
||||||
|
|
||||||
# codepoint_decimal ancho_visual
|
|
||||||
32 3 # U+0020
|
|
||||||
33 2 # !
|
|
||||||
34 4 # "
|
|
||||||
35 9 # #
|
|
||||||
36 6 # $
|
|
||||||
37 10 # %
|
|
||||||
38 7 # &
|
|
||||||
39 2 # '
|
|
||||||
40 4 # (
|
|
||||||
41 4 # )
|
|
||||||
42 4 # *
|
|
||||||
43 6 # +
|
|
||||||
44 2 # ,
|
|
||||||
45 4 # -
|
|
||||||
46 2 # .
|
|
||||||
47 5 # /
|
|
||||||
48 6 # 0
|
|
||||||
49 5 # 1
|
|
||||||
50 6 # 2
|
|
||||||
51 6 # 3
|
|
||||||
52 6 # 4
|
|
||||||
53 6 # 5
|
|
||||||
54 6 # 6
|
|
||||||
55 6 # 7
|
|
||||||
56 6 # 8
|
|
||||||
57 6 # 9
|
|
||||||
58 2 # :
|
|
||||||
59 2 # ;
|
|
||||||
60 6 # <
|
|
||||||
61 6 # =
|
|
||||||
62 6 # >
|
|
||||||
63 6 # ?
|
|
||||||
64 12 # @
|
|
||||||
65 8 # A
|
|
||||||
66 7 # B
|
|
||||||
67 8 # C
|
|
||||||
68 8 # D
|
|
||||||
69 7 # E
|
|
||||||
70 7 # F
|
|
||||||
71 8 # G
|
|
||||||
72 7 # H
|
|
||||||
73 2 # I
|
|
||||||
74 5 # J
|
|
||||||
75 7 # K
|
|
||||||
76 7 # L
|
|
||||||
77 8 # M
|
|
||||||
78 7 # N
|
|
||||||
79 8 # O
|
|
||||||
80 7 # P
|
|
||||||
81 8 # Q
|
|
||||||
82 7 # R
|
|
||||||
83 7 # S
|
|
||||||
84 6 # T
|
|
||||||
85 7 # U
|
|
||||||
86 7 # V
|
|
||||||
87 10 # W
|
|
||||||
88 8 # X
|
|
||||||
89 8 # Y
|
|
||||||
90 8 # Z
|
|
||||||
91 4 # [
|
|
||||||
92 5 # \
|
|
||||||
93 4 # ]
|
|
||||||
94 6 # ^
|
|
||||||
95 6 # _
|
|
||||||
96 3 # `
|
|
||||||
97 6 # a
|
|
||||||
98 6 # b
|
|
||||||
99 6 # c
|
|
||||||
100 6 # d
|
|
||||||
101 6 # e
|
|
||||||
102 4 # f
|
|
||||||
103 6 # g
|
|
||||||
104 6 # h
|
|
||||||
105 2 # i
|
|
||||||
106 2 # j
|
|
||||||
107 6 # k
|
|
||||||
108 2 # l
|
|
||||||
109 8 # m
|
|
||||||
110 6 # n
|
|
||||||
111 6 # o
|
|
||||||
112 6 # p
|
|
||||||
113 6 # q
|
|
||||||
114 4 # r
|
|
||||||
115 5 # s
|
|
||||||
116 4 # t
|
|
||||||
117 6 # u
|
|
||||||
118 6 # v
|
|
||||||
119 8 # w
|
|
||||||
120 6 # x
|
|
||||||
121 6 # y
|
|
||||||
122 6 # z
|
|
||||||
123 5 # {
|
|
||||||
124 2 # |
|
|
||||||
125 5 # }
|
|
||||||
126 6 # ~
|
|
||||||
192 8 # À
|
|
||||||
193 8 # Á
|
|
||||||
200 7 # È
|
|
||||||
201 7 # É
|
|
||||||
204 2 # Ì
|
|
||||||
205 2 # Í
|
|
||||||
210 8 # Ò
|
|
||||||
211 8 # Ó
|
|
||||||
217 7 # Ù
|
|
||||||
218 7 # Ú
|
|
||||||
209 7 # Ñ
|
|
||||||
199 8 # Ç
|
|
||||||
224 6 # à
|
|
||||||
225 6 # á
|
|
||||||
232 6 # è
|
|
||||||
233 6 # é
|
|
||||||
236 2 # ì
|
|
||||||
237 3 # í
|
|
||||||
242 6 # ò
|
|
||||||
243 6 # ó
|
|
||||||
249 6 # ù
|
|
||||||
250 6 # ú
|
|
||||||
241 6 # ñ
|
|
||||||
231 6 # ç
|
|
||||||
161 2 # ¡
|
|
||||||
191 6 # ¿
|
|
||||||
171 7 # «
|
|
||||||
187 7 # »
|
|
||||||
183 2 # ·
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -13,17 +13,18 @@ auto KeyConfig::get() -> KeyConfig& {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void KeyConfig::load(const std::string& file_path) {
|
void KeyConfig::load(const std::string& file_path) {
|
||||||
const auto FULL = Resource::Helper::resolve(file_path);
|
auto bytes = Resource::Helper::loadFile(file_path);
|
||||||
std::ifstream file(FULL);
|
if (bytes.empty()) {
|
||||||
if (!file.is_open()) {
|
std::cerr << "KeyConfig: no s'ha pogut obrir " << file_path << '\n';
|
||||||
std::cerr << "KeyConfig: no s'ha pogut obrir " << FULL << '\n';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto yaml = fkyaml::node::deserialize(file);
|
const auto* begin = reinterpret_cast<const char*>(bytes.data());
|
||||||
|
const auto* end = begin + bytes.size();
|
||||||
|
auto yaml = fkyaml::node::deserialize(begin, end);
|
||||||
if (!yaml.contains("keys") || !yaml["keys"].is_sequence()) {
|
if (!yaml.contains("keys") || !yaml["keys"].is_sequence()) {
|
||||||
std::cerr << "KeyConfig: falta la secció 'keys' a " << FULL << '\n';
|
std::cerr << "KeyConfig: falta la secció 'keys' a " << file_path << '\n';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bindings_.clear();
|
bindings_.clear();
|
||||||
@@ -38,9 +39,9 @@ void KeyConfig::load(const std::string& file_path) {
|
|||||||
}
|
}
|
||||||
bindings_[ID] = SC;
|
bindings_[ID] = SC;
|
||||||
}
|
}
|
||||||
std::cout << "KeyConfig: " << bindings_.size() << " bindings des de " << FULL << '\n';
|
std::cout << "KeyConfig: " << bindings_.size() << " bindings des de " << file_path << '\n';
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cerr << "KeyConfig: error parsejant " << FULL << ": " << e.what() << '\n';
|
std::cerr << "KeyConfig: error parsejant " << file_path << ": " << e.what() << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,20 +34,21 @@ auto Locale::get() -> Locale& {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Locale::load(const std::string& file_path) {
|
void Locale::load(const std::string& file_path) {
|
||||||
const auto FULL = Resource::Helper::resolve(file_path);
|
auto bytes = Resource::Helper::loadFile(file_path);
|
||||||
std::ifstream file(FULL);
|
if (bytes.empty()) {
|
||||||
if (!file.is_open()) {
|
std::cerr << "Locale: no s'ha pogut obrir " << file_path << '\n';
|
||||||
std::cerr << "Locale: no s'ha pogut obrir " << FULL << '\n';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto yaml = fkyaml::node::deserialize(file);
|
const auto* begin = reinterpret_cast<const char*>(bytes.data());
|
||||||
|
const auto* end = begin + bytes.size();
|
||||||
|
auto yaml = fkyaml::node::deserialize(begin, end);
|
||||||
strings_.clear();
|
strings_.clear();
|
||||||
flatten(yaml, "", strings_);
|
flatten(yaml, "", strings_);
|
||||||
std::cout << "Locale: " << strings_.size() << " traduccions des de " << FULL << '\n';
|
std::cout << "Locale: " << strings_.size() << " traduccions des de " << file_path << '\n';
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::cerr << "Locale: error parsejant " << FULL << ": " << e.what() << '\n';
|
std::cerr << "Locale: error parsejant " << file_path << ": " << e.what() << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,29 +3,20 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "core/resources/resource_pack.hpp"
|
||||||
|
|
||||||
namespace Resource::Helper {
|
namespace Resource::Helper {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string g_data_root;
|
std::string g_data_root;
|
||||||
|
Pack g_pack;
|
||||||
|
bool g_pack_loaded = false;
|
||||||
|
bool g_fallback_enabled = true;
|
||||||
|
|
||||||
} // namespace
|
auto readFromDisk(const std::string& filepath) -> std::vector<std::uint8_t> {
|
||||||
|
|
||||||
void setDataRoot(const std::string& absolute_path) {
|
|
||||||
g_data_root = absolute_path;
|
|
||||||
if (!g_data_root.empty() && g_data_root.back() != '/') {
|
|
||||||
g_data_root += '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto resolve(const std::string& filepath) -> std::string {
|
|
||||||
if (filepath.empty() || filepath.front() == '/') { return filepath; }
|
|
||||||
if (g_data_root.empty()) { return filepath; }
|
|
||||||
return g_data_root + filepath;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto loadFile(const std::string& filepath) -> std::vector<std::uint8_t> {
|
|
||||||
const auto FULL = resolve(filepath);
|
const auto FULL = resolve(filepath);
|
||||||
std::ifstream file(FULL, std::ios::binary | std::ios::ate);
|
std::ifstream file(FULL, std::ios::binary | std::ios::ate);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
@@ -42,4 +33,60 @@ namespace Resource::Helper {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
auto initializeResourceSystem(const std::string& pack_file, bool enable_fallback) -> bool {
|
||||||
|
g_fallback_enabled = enable_fallback;
|
||||||
|
g_pack_loaded = g_pack.loadPack(pack_file);
|
||||||
|
|
||||||
|
if (g_pack_loaded) {
|
||||||
|
std::cout << "Resource::Helper: pack loaded (" << g_pack.getResourceCount()
|
||||||
|
<< " entries) from " << pack_file << '\n';
|
||||||
|
} else if (enable_fallback) {
|
||||||
|
std::cout << "Resource::Helper: no pack at " << pack_file
|
||||||
|
<< " — using filesystem fallback\n";
|
||||||
|
} else {
|
||||||
|
std::cerr << "Resource::Helper: FATAL — no pack at " << pack_file
|
||||||
|
<< " and fallback disabled\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdownResourceSystem() {
|
||||||
|
g_pack.clear();
|
||||||
|
g_pack_loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hasPack() -> bool {
|
||||||
|
return g_pack_loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDataRoot(const std::string& absolute_path) {
|
||||||
|
g_data_root = absolute_path;
|
||||||
|
if (!g_data_root.empty() && g_data_root.back() != '/') {
|
||||||
|
g_data_root += '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resolve(const std::string& filepath) -> std::string {
|
||||||
|
if (filepath.empty() || filepath.front() == '/') { return filepath; }
|
||||||
|
if (g_data_root.empty()) { return filepath; }
|
||||||
|
return g_data_root + filepath;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto loadFile(const std::string& filepath) -> std::vector<std::uint8_t> {
|
||||||
|
if (g_pack_loaded) {
|
||||||
|
// El pack està indexat relatiu a `data/` (la carpeta que
|
||||||
|
// s'empaqueta), però els callers passen "data/foo.yaml" amb
|
||||||
|
// prefix. Fem el strip ací perquè no hagin de tenir-ho en compte.
|
||||||
|
constexpr std::string_view PREFIX = "data/";
|
||||||
|
std::string key = filepath;
|
||||||
|
if (key.starts_with(PREFIX)) { key.erase(0, PREFIX.size()); }
|
||||||
|
if (g_pack.hasResource(key)) { return g_pack.getResource(key); }
|
||||||
|
}
|
||||||
|
if (g_fallback_enabled) { return readFromDisk(filepath); }
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Resource::Helper
|
} // namespace Resource::Helper
|
||||||
@@ -6,23 +6,33 @@
|
|||||||
|
|
||||||
namespace Resource::Helper {
|
namespace Resource::Helper {
|
||||||
|
|
||||||
// Estableix el prefix absolut des d'on es resolen totes les rutes
|
// Inicialitza el sistema de recursos. Carrega el pack des de `pack_file`
|
||||||
// relatives passades a `loadFile`/`resolve`. Pensat per a cridar-se una
|
// i decideix si `loadFile` pot caure a disc quan un recurs no és al pack.
|
||||||
// sola vegada al primer pas del Director amb `SDL_GetBasePath()`: així el
|
// En Release el pack és obligatori: passar `enable_fallback=false` fa
|
||||||
// binari troba `data/` sense dependre del CWD des d'on s'ha llançat (és
|
// que `initializeResourceSystem` retorne false si no s'ha pogut carregar.
|
||||||
// obligatori per a .app de macOS i útil a Linux per no haver d'estar
|
auto initializeResourceSystem(const std::string& pack_file, bool enable_fallback) -> bool;
|
||||||
// dins de `build/`).
|
|
||||||
|
// Allibera el pack carregat a memòria.
|
||||||
|
void shutdownResourceSystem();
|
||||||
|
|
||||||
|
// True si el sistema es va inicialitzar amb un pack vàlid.
|
||||||
|
[[nodiscard]] auto hasPack() -> bool;
|
||||||
|
|
||||||
|
// Estableix el prefix des d'on es resolen les rutes quan el fallback
|
||||||
|
// llig de disc. En Debug apunta al source tree (`${CMAKE_SOURCE_DIR}/`)
|
||||||
|
// perquè editar YAML/GIF dins `data/` es vegi reflectit sense re-packejar.
|
||||||
|
// En Release el fallback està desactivat i aquest camí no es fa servir.
|
||||||
void setDataRoot(const std::string& absolute_path);
|
void setDataRoot(const std::string& absolute_path);
|
||||||
|
|
||||||
// Resol una ruta relativa contra el data root. Si la ruta ja és absoluta
|
// Resol una ruta relativa contra el data root. Si ja és absoluta
|
||||||
// (comença per '/') la retorna sense modificar — així els fitxers de
|
// (comença per '/') la retorna sense modificar. S'usa per a rutes que
|
||||||
// config (system_folder_ + "config.yaml") segueixen funcionant sense
|
// NO passen pel pack (p.ex. fitxers de config dins system_folder_).
|
||||||
// canvis al call site.
|
|
||||||
[[nodiscard]] auto resolve(const std::string& filepath) -> std::string;
|
[[nodiscard]] auto resolve(const std::string& filepath) -> std::string;
|
||||||
|
|
||||||
// Carga un fitxer del filesystem (a aquesta fase encara no hi ha pack).
|
// Carrega un recurs pel seu camí relatiu (p.ex. "fonts/hud.gif",
|
||||||
// Les rutes relatives es resolen via `resolve()`; les absolutes s'obrin
|
// "input/keys.yaml"). Prova primer el pack; si no hi és i el fallback
|
||||||
// directament.
|
// està actiu, cau a disc (data root + ruta). Retorna vector buit si
|
||||||
|
// no el troba.
|
||||||
auto loadFile(const std::string& filepath) -> std::vector<std::uint8_t>;
|
auto loadFile(const std::string& filepath) -> std::vector<std::uint8_t>;
|
||||||
|
|
||||||
} // namespace Resource::Helper
|
} // namespace Resource::Helper
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
#include "core/resources/resource_pack.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace Resource {
|
||||||
|
|
||||||
|
const std::string Pack::DEFAULT_ENCRYPT_KEY = "AEE_2026_PACK_KEY";
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr const char* MAGIC = "AE26";
|
||||||
|
constexpr std::uint32_t VERSION = 1;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
auto Pack::calculateChecksum(const std::vector<std::uint8_t>& data) -> std::uint32_t {
|
||||||
|
// djb2: seed 0x12345678. Compatible amb aee/projecte_2026 per si mai
|
||||||
|
// reutilitzem eines de diagnòstic entre projectes.
|
||||||
|
std::uint32_t checksum = 0x12345678;
|
||||||
|
for (unsigned char b : data) {
|
||||||
|
// cppcheck-suppress useStlAlgorithm
|
||||||
|
checksum = ((checksum << 5) + checksum) + b;
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack::xorInPlace(std::vector<std::uint8_t>& data, const std::string& key) {
|
||||||
|
if (key.empty()) { return; }
|
||||||
|
for (std::size_t i = 0; i < data.size(); ++i) {
|
||||||
|
data[i] ^= static_cast<std::uint8_t>(key[i % key.length()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Pack::loadPack(const std::string& pack_file) -> bool {
|
||||||
|
std::ifstream file(pack_file, std::ios::binary);
|
||||||
|
if (!file) { return false; }
|
||||||
|
|
||||||
|
std::array<char, 4> header{};
|
||||||
|
file.read(header.data(), 4);
|
||||||
|
if (std::string(header.data(), 4) != MAGIC) {
|
||||||
|
std::cerr << "Resource::Pack: invalid pack file format (bad magic): " << pack_file << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t version = 0;
|
||||||
|
file.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||||
|
if (version != VERSION) {
|
||||||
|
std::cerr << "Resource::Pack: unsupported pack version: " << version << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t resource_count = 0;
|
||||||
|
file.read(reinterpret_cast<char*>(&resource_count), sizeof(resource_count));
|
||||||
|
|
||||||
|
resources_.clear();
|
||||||
|
resources_.reserve(resource_count);
|
||||||
|
|
||||||
|
for (std::uint32_t i = 0; i < resource_count; ++i) {
|
||||||
|
std::uint32_t filename_length = 0;
|
||||||
|
file.read(reinterpret_cast<char*>(&filename_length), sizeof(filename_length));
|
||||||
|
|
||||||
|
std::string filename(filename_length, '\0');
|
||||||
|
file.read(filename.data(), filename_length);
|
||||||
|
|
||||||
|
PackEntry entry;
|
||||||
|
entry.filename = filename;
|
||||||
|
file.read(reinterpret_cast<char*>(&entry.offset), sizeof(entry.offset));
|
||||||
|
file.read(reinterpret_cast<char*>(&entry.size), sizeof(entry.size));
|
||||||
|
file.read(reinterpret_cast<char*>(&entry.checksum), sizeof(entry.checksum));
|
||||||
|
|
||||||
|
resources_[filename] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t data_size = 0;
|
||||||
|
file.read(reinterpret_cast<char*>(&data_size), sizeof(data_size));
|
||||||
|
|
||||||
|
data_.resize(data_size);
|
||||||
|
file.read(reinterpret_cast<char*>(data_.data()), static_cast<std::streamsize>(data_size));
|
||||||
|
|
||||||
|
xorInPlace(data_, DEFAULT_ENCRYPT_KEY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Pack::savePack(const std::string& pack_file) const -> bool {
|
||||||
|
std::ofstream file(pack_file, std::ios::binary);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Resource::Pack: could not create pack file: " << pack_file << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write(MAGIC, 4);
|
||||||
|
|
||||||
|
std::uint32_t version = VERSION;
|
||||||
|
file.write(reinterpret_cast<const char*>(&version), sizeof(version));
|
||||||
|
|
||||||
|
auto resource_count = static_cast<std::uint32_t>(resources_.size());
|
||||||
|
file.write(reinterpret_cast<const char*>(&resource_count), sizeof(resource_count));
|
||||||
|
|
||||||
|
for (const auto& [filename, entry] : resources_) {
|
||||||
|
auto filename_length = static_cast<std::uint32_t>(filename.length());
|
||||||
|
file.write(reinterpret_cast<const char*>(&filename_length), sizeof(filename_length));
|
||||||
|
file.write(filename.c_str(), filename_length);
|
||||||
|
file.write(reinterpret_cast<const char*>(&entry.offset), sizeof(entry.offset));
|
||||||
|
file.write(reinterpret_cast<const char*>(&entry.size), sizeof(entry.size));
|
||||||
|
file.write(reinterpret_cast<const char*>(&entry.checksum), sizeof(entry.checksum));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> encrypted = data_;
|
||||||
|
xorInPlace(encrypted, DEFAULT_ENCRYPT_KEY);
|
||||||
|
|
||||||
|
std::uint64_t data_size = encrypted.size();
|
||||||
|
file.write(reinterpret_cast<const char*>(&data_size), sizeof(data_size));
|
||||||
|
file.write(reinterpret_cast<const char*>(encrypted.data()), static_cast<std::streamsize>(data_size));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Pack::addFile(const std::string& filename, const std::string& filepath) -> bool {
|
||||||
|
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Resource::Pack: could not open file: " << filepath << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize file_size = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> file_data(file_size);
|
||||||
|
if (!file.read(reinterpret_cast<char*>(file_data.data()), file_size)) {
|
||||||
|
std::cerr << "Resource::Pack: could not read file: " << filepath << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackEntry entry;
|
||||||
|
entry.filename = filename;
|
||||||
|
entry.offset = data_.size();
|
||||||
|
entry.size = file_data.size();
|
||||||
|
entry.checksum = calculateChecksum(file_data);
|
||||||
|
|
||||||
|
data_.insert(data_.end(), file_data.begin(), file_data.end());
|
||||||
|
resources_[filename] = entry;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Pack::addDirectory(const std::string& directory) -> bool {
|
||||||
|
if (!std::filesystem::exists(directory)) {
|
||||||
|
std::cerr << "Resource::Pack: directory does not exist: " << directory << '\n';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cppcheck-suppress useStlAlgorithm
|
||||||
|
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory)) {
|
||||||
|
if (!entry.is_regular_file()) { continue; }
|
||||||
|
|
||||||
|
std::string filepath = entry.path().string();
|
||||||
|
std::string filename = std::filesystem::relative(entry.path(), directory).string();
|
||||||
|
std::ranges::replace(filename, '\\', '/');
|
||||||
|
|
||||||
|
if (!addFile(filename, filepath)) { return false; }
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Pack::getResource(const std::string& filename) const -> std::vector<std::uint8_t> {
|
||||||
|
auto it = resources_.find(filename);
|
||||||
|
if (it == resources_.end()) { return {}; }
|
||||||
|
|
||||||
|
const PackEntry& entry = it->second;
|
||||||
|
if (entry.offset + entry.size > data_.size()) {
|
||||||
|
std::cerr << "Resource::Pack: invalid resource data: " << filename << '\n';
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> result(
|
||||||
|
data_.begin() + static_cast<std::ptrdiff_t>(entry.offset),
|
||||||
|
data_.begin() + static_cast<std::ptrdiff_t>(entry.offset + entry.size));
|
||||||
|
|
||||||
|
std::uint32_t checksum = calculateChecksum(result);
|
||||||
|
if (checksum != entry.checksum) {
|
||||||
|
std::cerr << "Resource::Pack: checksum mismatch for: " << filename << '\n';
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Pack::hasResource(const std::string& filename) const -> bool {
|
||||||
|
return resources_.contains(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pack::clear() {
|
||||||
|
resources_.clear();
|
||||||
|
data_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Pack::getResourceCount() const -> std::size_t {
|
||||||
|
return resources_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Pack::getResourceList() const -> std::vector<std::string> {
|
||||||
|
std::vector<std::string> result;
|
||||||
|
result.reserve(resources_.size());
|
||||||
|
for (const auto& [filename, entry] : resources_) {
|
||||||
|
result.push_back(filename);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Resource
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Resource {
|
||||||
|
|
||||||
|
// Entrada d'un recurs dins el pack.
|
||||||
|
struct PackEntry {
|
||||||
|
std::string filename;
|
||||||
|
std::uint64_t offset{0};
|
||||||
|
std::uint64_t size{0};
|
||||||
|
std::uint32_t checksum{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pack binari de recursos carregat a memòria. Format:
|
||||||
|
// Header: "AE26" (4 bytes) + version uint32 + resource_count uint32
|
||||||
|
// Index: per cada recurs -> filename_len uint32 + filename
|
||||||
|
// + offset uint64 + size uint64 + checksum uint32
|
||||||
|
// Payload: data_size uint64 + bytes xifrats amb XOR (DEFAULT_ENCRYPT_KEY)
|
||||||
|
// La classe té dos rols: (a) loader en temps d'execució — carregar un
|
||||||
|
// .pack del disc i obtindre els bytes d'un recurs per nom; (b) builder
|
||||||
|
// usat per l'eina `pack_resources` per a empaquetar `data/`.
|
||||||
|
class Pack {
|
||||||
|
public:
|
||||||
|
Pack() = default;
|
||||||
|
~Pack() = default;
|
||||||
|
|
||||||
|
Pack(const Pack&) = delete;
|
||||||
|
auto operator=(const Pack&) -> Pack& = delete;
|
||||||
|
Pack(Pack&&) = default;
|
||||||
|
auto operator=(Pack&&) -> Pack& = default;
|
||||||
|
|
||||||
|
// I/O del fitxer. `loadPack` retorna false silenciosament si el
|
||||||
|
// fitxer no existeix (el caller decideix si cal fallback).
|
||||||
|
auto loadPack(const std::string& pack_file) -> bool;
|
||||||
|
auto savePack(const std::string& pack_file) const -> bool;
|
||||||
|
|
||||||
|
// Builders usats per l'eina `pack_resources`.
|
||||||
|
auto addFile(const std::string& filename, const std::string& filepath) -> bool;
|
||||||
|
auto addDirectory(const std::string& directory) -> bool;
|
||||||
|
|
||||||
|
[[nodiscard]] auto getResource(const std::string& filename) const -> std::vector<std::uint8_t>;
|
||||||
|
[[nodiscard]] auto hasResource(const std::string& filename) const -> bool;
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
[[nodiscard]] auto getResourceCount() const -> std::size_t;
|
||||||
|
[[nodiscard]] auto getResourceList() const -> std::vector<std::string>;
|
||||||
|
|
||||||
|
static const std::string DEFAULT_ENCRYPT_KEY;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, PackEntry> resources_;
|
||||||
|
std::vector<std::uint8_t> data_;
|
||||||
|
|
||||||
|
static auto calculateChecksum(const std::vector<std::uint8_t>& data) -> std::uint32_t;
|
||||||
|
static void xorInPlace(std::vector<std::uint8_t>& data, const std::string& key);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Resource
|
||||||
@@ -40,13 +40,26 @@ Director::Director() {
|
|||||||
std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
|
std::fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rutes dels recursos relatives a l'executable, no al CWD. SDL_GetBasePath
|
// Sistema de recursos: el pack viu al costat del binari (build/), el
|
||||||
// retorna la carpeta del binari al desplegament portable i Contents/
|
// regenera CMake a cada build. En Debug habilitem el fallback a disc
|
||||||
// Resources/ dins d'un .app a macOS. Ha de fer-se abans de qualsevol
|
// apuntant al source tree perquè editar YAML/GIF dins `data/` es vegi
|
||||||
// loader que passi per Resource::Helper::resolve (Locale, KeyConfig,
|
// sense re-packejar; en Release el pack és obligatori.
|
||||||
// Resource::Cache, PaletteLoader, etc.).
|
std::string pack_file = "resources.pack";
|
||||||
if (const char* base = SDL_GetBasePath()) {
|
if (const char* base = SDL_GetBasePath()) {
|
||||||
|
pack_file = std::string(base) + "resources.pack";
|
||||||
|
#ifndef _DEBUG
|
||||||
Resource::Helper::setDataRoot(base);
|
Resource::Helper::setDataRoot(base);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef _DEBUG
|
||||||
|
Resource::Helper::setDataRoot(AEE_SOURCE_DIR);
|
||||||
|
constexpr bool ENABLE_FALLBACK = true;
|
||||||
|
#else
|
||||||
|
constexpr bool ENABLE_FALLBACK = false;
|
||||||
|
#endif
|
||||||
|
if (!Resource::Helper::initializeResourceSystem(pack_file, ENABLE_FALLBACK)) {
|
||||||
|
std::fprintf(stderr, "Director: sistema de recursos no disponible; avortem\n");
|
||||||
|
std::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepara la carpeta de sistema (~/.config/jailgames/aee_2026/) i carrega
|
// Prepara la carpeta de sistema (~/.config/jailgames/aee_2026/) i carrega
|
||||||
@@ -207,6 +220,7 @@ Director::~Director() {
|
|||||||
// desfà les llibreries abans que corren els destructors estàtics.
|
// desfà les llibreries abans que corren els destructors estàtics.
|
||||||
current_scene_.reset();
|
current_scene_.reset();
|
||||||
Resource::Cache::get().clearAudio();
|
Resource::Cache::get().clearAudio();
|
||||||
|
Resource::Helper::shutdownResourceSystem();
|
||||||
GamepadManager::get().shutdown();
|
GamepadManager::get().shutdown();
|
||||||
Audio::destroy();
|
Audio::destroy();
|
||||||
Screen::get().shutdown();
|
Screen::get().shutdown();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "game/entities/entity_loader.hpp"
|
#include "game/entities/entity_loader.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@@ -31,23 +30,26 @@ namespace EntityLoader {
|
|||||||
auto it = cache.find(name);
|
auto it = cache.find(name);
|
||||||
if (it != cache.end()) { return it->second; }
|
if (it != cache.end()) { return it->second; }
|
||||||
|
|
||||||
const std::string FULL = Resource::Helper::resolve(templatePath(name));
|
const auto PATH = templatePath(name);
|
||||||
std::ifstream file(FULL);
|
auto bytes = Resource::Helper::loadFile(PATH);
|
||||||
if (!file.is_open()) {
|
if (bytes.empty()) {
|
||||||
throw std::runtime_error("EntityLoader: no s'ha pogut obrir " + FULL);
|
throw std::runtime_error("EntityLoader: no s'ha pogut obrir " + PATH);
|
||||||
}
|
}
|
||||||
fkyaml::node node = fkyaml::node::deserialize(file);
|
const auto* begin = reinterpret_cast<const char*>(bytes.data());
|
||||||
|
const auto* end = begin + bytes.size();
|
||||||
|
fkyaml::node node = fkyaml::node::deserialize(begin, end);
|
||||||
cache.emplace(name, node);
|
cache.emplace(name, node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto readSpawns(const std::string& level_yaml_path) -> std::vector<fkyaml::node> {
|
auto readSpawns(const std::string& level_yaml_path) -> std::vector<fkyaml::node> {
|
||||||
const auto FULL = Resource::Helper::resolve(level_yaml_path);
|
auto bytes = Resource::Helper::loadFile(level_yaml_path);
|
||||||
std::ifstream file(FULL);
|
if (bytes.empty()) {
|
||||||
if (!file.is_open()) {
|
throw std::runtime_error("EntityLoader: no s'ha pogut obrir " + level_yaml_path);
|
||||||
throw std::runtime_error("EntityLoader: no s'ha pogut obrir " + FULL);
|
|
||||||
}
|
}
|
||||||
fkyaml::node root = fkyaml::node::deserialize(file);
|
const auto* begin = reinterpret_cast<const char*>(bytes.data());
|
||||||
|
const auto* end = begin + bytes.size();
|
||||||
|
fkyaml::node root = fkyaml::node::deserialize(begin, end);
|
||||||
std::vector<fkyaml::node> spawns;
|
std::vector<fkyaml::node> spawns;
|
||||||
if (!root.contains("entities")) { return spawns; }
|
if (!root.contains("entities")) { return spawns; }
|
||||||
const auto& list = root["entities"];
|
const auto& list = root["entities"];
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <fstream>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@@ -47,18 +46,19 @@ namespace {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void Player::loadFrom(const std::string& yaml_path) {
|
void Player::loadFrom(const std::string& yaml_path) {
|
||||||
const auto FULL = Resource::Helper::resolve(yaml_path);
|
auto bytes = Resource::Helper::loadFile(yaml_path);
|
||||||
std::ifstream file(FULL);
|
if (bytes.empty()) {
|
||||||
if (!file.is_open()) {
|
throw std::runtime_error("Player: no s'ha pogut obrir " + yaml_path);
|
||||||
throw std::runtime_error("Player: no s'ha pogut obrir " + FULL);
|
|
||||||
}
|
}
|
||||||
auto yaml = fkyaml::node::deserialize(file);
|
const auto* begin = reinterpret_cast<const char*>(bytes.data());
|
||||||
|
const auto* end = begin + bytes.size();
|
||||||
|
auto yaml = fkyaml::node::deserialize(begin, end);
|
||||||
|
|
||||||
sprite_w = yaml["frameWidth"].get_value<int>();
|
sprite_w = yaml["frameWidth"].get_value<int>();
|
||||||
sprite_h = yaml["frameHeight"].get_value<int>();
|
sprite_h = yaml["frameHeight"].get_value<int>();
|
||||||
|
|
||||||
if (!yaml.contains("collision")) {
|
if (!yaml.contains("collision")) {
|
||||||
throw std::runtime_error("Player: manca bloc `collision:` a " + FULL);
|
throw std::runtime_error("Player: manca bloc `collision:` a " + yaml_path);
|
||||||
}
|
}
|
||||||
const auto& c = yaml["collision"];
|
const auto& c = yaml["collision"];
|
||||||
collision_size = c["size"].get_value<int>();
|
collision_size = c["size"].get_value<int>();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "game/map/map.hpp"
|
#include "game/map/map.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@@ -22,14 +21,15 @@ namespace {
|
|||||||
namespace Map {
|
namespace Map {
|
||||||
|
|
||||||
void loadFrom(const std::string& yaml_path) {
|
void loadFrom(const std::string& yaml_path) {
|
||||||
const auto FULL = Resource::Helper::resolve(yaml_path);
|
auto bytes = Resource::Helper::loadFile(yaml_path);
|
||||||
std::ifstream file(FULL);
|
if (bytes.empty()) {
|
||||||
if (!file.is_open()) {
|
throw std::runtime_error("Map: no s'ha pogut obrir " + yaml_path);
|
||||||
throw std::runtime_error("Map: no s'ha pogut obrir " + FULL);
|
|
||||||
}
|
}
|
||||||
auto yaml = fkyaml::node::deserialize(file);
|
const auto* begin = reinterpret_cast<const char*>(bytes.data());
|
||||||
|
const auto* end = begin + bytes.size();
|
||||||
|
auto yaml = fkyaml::node::deserialize(begin, end);
|
||||||
if (!yaml.contains("geometry")) {
|
if (!yaml.contains("geometry")) {
|
||||||
throw std::runtime_error("Map: manca bloc `geometry:` a " + FULL);
|
throw std::runtime_error("Map: manca bloc `geometry:` a " + yaml_path);
|
||||||
}
|
}
|
||||||
const auto& g = yaml["geometry"];
|
const auto& g = yaml["geometry"];
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ namespace Map {
|
|||||||
std::cout << "Map: geometria " << grid_cols << "x" << grid_rows
|
std::cout << "Map: geometria " << grid_cols << "x" << grid_rows
|
||||||
<< " sarcs " << sarc_w << "x" << sarc_h
|
<< " sarcs " << sarc_w << "x" << sarc_h
|
||||||
<< " pitch " << pitch_x << "x" << pitch_y
|
<< " pitch " << pitch_x << "x" << pitch_y
|
||||||
<< " des de " << FULL << '\n';
|
<< " des de " << yaml_path << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
auto getSarcRect(int col, int row) -> SDL_FRect {
|
auto getSarcRect(int col, int row) -> SDL_FRect {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@@ -68,13 +67,13 @@ namespace {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Tilemap::Tilemap(const std::string& yaml_path) {
|
Tilemap::Tilemap(const std::string& yaml_path) {
|
||||||
const auto FULL = Resource::Helper::resolve(yaml_path);
|
auto bytes = Resource::Helper::loadFile(yaml_path);
|
||||||
std::ifstream file(FULL);
|
if (bytes.empty()) {
|
||||||
if (!file.is_open()) {
|
throw std::runtime_error("Tilemap: no s'ha pogut obrir " + yaml_path);
|
||||||
throw std::runtime_error("Tilemap: no s'ha pogut obrir " + FULL);
|
|
||||||
}
|
}
|
||||||
|
const auto* begin = reinterpret_cast<const char*>(bytes.data());
|
||||||
auto yaml = fkyaml::node::deserialize(file);
|
const auto* end = begin + bytes.size();
|
||||||
|
auto yaml = fkyaml::node::deserialize(begin, end);
|
||||||
|
|
||||||
const auto TILESET_PATH = yaml["tileset"].get_value<std::string>();
|
const auto TILESET_PATH = yaml["tileset"].get_value<std::string>();
|
||||||
const auto TILE_SIZE = yaml["tile_size"].get_value<int>();
|
const auto TILE_SIZE = yaml["tile_size"].get_value<int>();
|
||||||
@@ -113,7 +112,7 @@ Tilemap::Tilemap(const std::string& yaml_path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Tilemap: " << COLS << "x" << ROWS << " tiles ("
|
std::cout << "Tilemap: " << COLS << "x" << ROWS << " tiles ("
|
||||||
<< width_px_ << "x" << height_px_ << " px) des de " << FULL << '\n';
|
<< width_px_ << "x" << height_px_ << " px) des de " << yaml_path << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tilemap::render() const {
|
void Tilemap::render() const {
|
||||||
|
|||||||
@@ -107,9 +107,9 @@ namespace Defaults {
|
|||||||
|
|
||||||
// Placeholders (LOGO / TITLE / INTRO): font temporal fins que els
|
// Placeholders (LOGO / TITLE / INTRO): font temporal fins que els
|
||||||
// placeholders es substitueixin per les seues escenes finals.
|
// placeholders es substitueixin per les seues escenes finals.
|
||||||
constexpr const char* PLACEHOLDER_NAME = "ari-w9500";
|
constexpr const char* PLACEHOLDER_NAME = "ari-w9500-condensed-display";
|
||||||
constexpr const char* PLACEHOLDER_GIF = "data/fonts/ari-w9500.gif";
|
constexpr const char* PLACEHOLDER_GIF = "data/fonts/ari-w9500-condensed-display.gif";
|
||||||
constexpr const char* PLACEHOLDER_FNT = "data/fonts/ari-w9500.fnt";
|
constexpr const char* PLACEHOLDER_FNT = "data/fonts/ari-w9500-condensed-display.fnt";
|
||||||
constexpr int PLACEHOLDER_KERNING = 1;
|
constexpr int PLACEHOLDER_KERNING = 1;
|
||||||
|
|
||||||
// Menú de servei (F9): títol en display normal, items en la
|
// Menú de servei (F9): títol en display normal, items en la
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace Texts {
|
namespace Texts {
|
||||||
constexpr const char* WINDOW_TITLE = "© 2026 Aventures en Egipte — JailDesigner";
|
constexpr const char* WINDOW_TITLE = "© 2026 Aventures en Egipte — JailDesigner";
|
||||||
constexpr const char* COPYRIGHT = "© 2026 JailDesigner";
|
constexpr const char* COPYRIGHT = "© 2026 JailDesigner";
|
||||||
constexpr const char* VERSION = "0.20.0"; // Single source of truth — Makefile el grep
|
constexpr const char* VERSION = "0.21.0"; // Single source of truth — Makefile el grep
|
||||||
} // namespace Texts
|
} // namespace Texts
|
||||||
|
|
||||||
namespace GameCanvas {
|
namespace GameCanvas {
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "core/resources/resource_pack.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void showHelp() {
|
||||||
|
std::cout << "aee_2026 - Resource Packer\n";
|
||||||
|
std::cout << "==========================\n";
|
||||||
|
std::cout << "Usage: pack_resources [options] [input_dir] [output_file]\n\n";
|
||||||
|
std::cout << "Options:\n";
|
||||||
|
std::cout << " --help Show this help message\n";
|
||||||
|
std::cout << " --list List contents of an existing pack file\n\n";
|
||||||
|
std::cout << "Arguments:\n";
|
||||||
|
std::cout << " input_dir Directory to pack (default: data)\n";
|
||||||
|
std::cout << " output_file Pack file name (default: resources.pack)\n\n";
|
||||||
|
std::cout << "Examples:\n";
|
||||||
|
std::cout << " pack_resources # data -> resources.pack\n";
|
||||||
|
std::cout << " pack_resources data build/resources.pack # data -> build/resources.pack\n";
|
||||||
|
std::cout << " pack_resources --list build/resources.pack\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void listPackContents(const std::string& pack_file) {
|
||||||
|
Resource::Pack pack;
|
||||||
|
if (!pack.loadPack(pack_file)) {
|
||||||
|
std::cerr << "Error: cannot open pack file: " << pack_file << '\n';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto resources = pack.getResourceList();
|
||||||
|
std::cout << "Pack file: " << pack_file << '\n';
|
||||||
|
std::cout << "Resources: " << resources.size() << '\n';
|
||||||
|
for (const auto& r : resources) { std::cout << " " << r << '\n'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
std::string data_dir = "data";
|
||||||
|
std::string output_file = "resources.pack";
|
||||||
|
bool list_mode = false;
|
||||||
|
bool data_dir_set = false;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
std::string arg = argv[i];
|
||||||
|
if (arg == "--help" || arg == "-h") {
|
||||||
|
showHelp();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (arg == "--list") {
|
||||||
|
list_mode = true;
|
||||||
|
if (i + 1 < argc) { output_file = argv[++i]; }
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!arg.empty() && arg[0] != '-') {
|
||||||
|
if (!data_dir_set) {
|
||||||
|
data_dir = arg;
|
||||||
|
data_dir_set = true;
|
||||||
|
} else {
|
||||||
|
output_file = arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list_mode) {
|
||||||
|
listPackContents(output_file);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "aee_2026 - Resource Packer\n==========================\n";
|
||||||
|
std::cout << "Input directory: " << data_dir << '\n';
|
||||||
|
std::cout << "Output file: " << output_file << '\n';
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(data_dir)) {
|
||||||
|
std::cerr << "Error: input directory does not exist: " << data_dir << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource::Pack pack;
|
||||||
|
std::cout << "Scanning and packing resources...\n";
|
||||||
|
if (!pack.addDirectory(data_dir)) {
|
||||||
|
std::cerr << "Error: failed to add directory to pack\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::cout << "Found " << pack.getResourceCount() << " resources\n";
|
||||||
|
|
||||||
|
std::cout << "Saving pack file...\n";
|
||||||
|
if (!pack.savePack(output_file)) {
|
||||||
|
std::cerr << "Error: failed to save pack file\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto file_size = std::filesystem::file_size(std::filesystem::path(output_file));
|
||||||
|
std::cout << "Pack file created: " << output_file << " ("
|
||||||
|
<< (static_cast<double>(file_size) / 1024.0 / 1024.0) << " MB)\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user