1+ MAP_SIZE = 400
2+ TILE_COUNT = MAP_SIZE * ((MAP_SIZE // 2 ) + 1 )
3+ # python -m pip install Pillow
4+ import struct , numpy
5+ from sourcehold .tool .memory .map .common import get_process_handle , validate_input_path
6+ from sourcehold .world import create_selection_matrix
7+ import cv2 as cv # type: ignore
8+ from .logics import logic1 , logic1_vk , logic2 , logic2_vk
9+ from .colors import DEFAULT_PALETTE , Palette
10+ import sys
11+
12+ def get_image_data (img_path ):
13+ img = cv .imread (img_path )
14+ return img
15+
16+ selection = create_selection_matrix (size = MAP_SIZE )
17+
18+ # (Little endian) unsigned bytes
19+ def get_raw_logic1 (process ):
20+ return struct .unpack (f"<{ TILE_COUNT } I" , process .read_section ('1003' ))
21+
22+ def set_raw_logic1 (process , data ):
23+ serialized = struct .pack (f"<{ TILE_COUNT } I" , * data )
24+ # Logical terrain height layer
25+ process .write_section ('1003' , serialized )
26+
27+ def get_raw_logic2 (process ):
28+ return struct .unpack (f"<{ TILE_COUNT } B" , process .read_section ('1037' ))
29+
30+ def set_raw_logic2 (process , data ):
31+ serialized = struct .pack (f"<{ TILE_COUNT } B" , * data )
32+ # Logical terrain height layer
33+ process .write_section ('1037' , serialized )
34+
35+
36+ # 16 is used for the inaccessible parts of the map, including the outer border of the 800x800 space, and 32 is used for a border just within that
37+
38+ def set_terrain (args ):
39+ #' returns None in case of non applicable
40+ if args .what != 'terrain' :
41+ return None
42+
43+ if args .action != "set" :
44+ return None
45+
46+ if args .palette :
47+ palette = Palette (args .palette )
48+ else :
49+ palette = DEFAULT_PALETTE
50+
51+ img_path = args .input
52+ validate_input_path (img_path )
53+ img = get_image_data (img_path )
54+
55+ process = get_process_handle (args .game )
56+
57+ logic1matrix = numpy .zeros ((400 , 400 ), dtype = 'uint32' )
58+ borderlogic1matrix = numpy .zeros ((400 , 400 ), dtype = 'uint32' )
59+ borderlogic1matrix [selection ] = get_raw_logic1 (process ) # We load it here to get the map borders only...
60+ logic1matrix [borderlogic1matrix & logic1 ['border' ] != 0 ] |= logic1 ['border' ]
61+ logic1matrix [borderlogic1matrix & logic1 ['border_edge' ] != 0 ] |= logic1 ['border_edge' ]
62+ logic1matrix [borderlogic1matrix == 0 ] = 0
63+
64+ logic2matrix = numpy .zeros ((400 , 400 ), dtype = 'uint8' )
65+ # logic2matrix[selection] = get_raw_logic2(process)
66+
67+ for color , name in palette .bgr_palette .items ():
68+ where = (img == color ).all (2 )
69+ logic1matrix [where ] |= logic1 [name ]
70+ if name in logic2 :
71+ logic2matrix [where ] = logic2 [name ]
72+
73+ set_raw_logic1 (process , logic1matrix [selection ].flat )
74+ set_raw_logic2 (process , logic2matrix [selection ].flat )
75+
76+ return True
77+
78+ def get_terrain (args ):
79+ #' returns None in case of non applicable
80+ if args .what != 'terrain' :
81+ return None
82+
83+ if args .action != "get" :
84+ return None
85+
86+ if args .palette :
87+ palette = Palette (args .palette )
88+ else :
89+ palette = DEFAULT_PALETTE
90+
91+ process = get_process_handle (args .game )
92+
93+ logic1matrix = numpy .zeros ((400 , 400 ), dtype = 'uint32' )
94+ logic1matrix [selection ] = get_raw_logic1 (process )
95+
96+ logic2matrix = numpy .zeros ((400 , 400 ), dtype = 'uint8' )
97+ logic2matrix [selection ] = get_raw_logic2 (process )
98+
99+ img = numpy .zeros ((400 ,400 ,3 ), dtype = 'uint8' )
100+
101+ if args .debug :
102+ print ("logic1" )
103+ for flag , name in logic1_vk .items ():
104+ color = (0 , 0 , 0 )
105+ if name in palette .palette_bgr :
106+ color = palette .palette_bgr [name ]
107+ else :
108+ if args .debug :
109+ print (f"skipping color for: { name } " )
110+ img [logic1matrix & flag != 0 ] = color
111+ if args .debug :
112+ print (f"set '{ name } ' { img [logic1matrix & flag != 0 ].sum ()} times to color: { palette .bgr_palette [color ]} " )
113+
114+ if args .debug :
115+ print ("logic2" )
116+ for flag , name in logic2_vk .items ():
117+ if name == 'none' :
118+ continue
119+ color = (0 , 0 , 0 )
120+ where = (logic1matrix & logic1 ['default_earth_or_texture' ]) != 0
121+ if name in palette .palette_bgr :
122+ color = palette .palette_bgr [name ]
123+ else :
124+ if args .debug :
125+ print (f"skipping color for: { name } " )
126+ img [where & (logic2matrix == flag )] = color
127+ if args .debug :
128+ print (f"set '{ name } ' { img [where & (logic2matrix == flag )].sum ()} times to color: { palette .bgr_palette [color ]} " )
129+
130+ if not args .output :
131+ if args .debug :
132+ print (args .output_format )
133+ sys .stdout .buffer .write (cv .imencode (f".{ args .output_format } " , img )[1 ].tobytes ())
134+ else :
135+ cv .imwrite (args .output , img = img )
136+
137+ return True
0 commit comments