pywargame 0.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pywargame/__init__.py +2 -0
- pywargame/common/__init__.py +3 -0
- pywargame/common/collector.py +87 -0
- pywargame/common/dicedraw.py +363 -0
- pywargame/common/drawdice.py +40 -0
- pywargame/common/singleton.py +22 -0
- pywargame/common/test.py +25 -0
- pywargame/common/verbose.py +59 -0
- pywargame/common/verboseguard.py +53 -0
- pywargame/cyberboard/__init__.py +18 -0
- pywargame/cyberboard/archive.py +283 -0
- pywargame/cyberboard/base.py +63 -0
- pywargame/cyberboard/board.py +462 -0
- pywargame/cyberboard/cell.py +200 -0
- pywargame/cyberboard/collect.py +49 -0
- pywargame/cyberboard/collectgbx0pwd.py +30 -0
- pywargame/cyberboard/collectgbxext.py +30 -0
- pywargame/cyberboard/collectgsnexp.py +32 -0
- pywargame/cyberboard/collectgsnext.py +30 -0
- pywargame/cyberboard/draw.py +396 -0
- pywargame/cyberboard/exporter.py +1132 -0
- pywargame/cyberboard/extractor.py +240 -0
- pywargame/cyberboard/features.py +17 -0
- pywargame/cyberboard/gamebox.py +81 -0
- pywargame/cyberboard/gbxexp.py +76 -0
- pywargame/cyberboard/gbxext.py +64 -0
- pywargame/cyberboard/gsnexp.py +147 -0
- pywargame/cyberboard/gsnext.py +59 -0
- pywargame/cyberboard/head.py +111 -0
- pywargame/cyberboard/image.py +76 -0
- pywargame/cyberboard/main.py +47 -0
- pywargame/cyberboard/mark.py +102 -0
- pywargame/cyberboard/palette.py +36 -0
- pywargame/cyberboard/piece.py +169 -0
- pywargame/cyberboard/player.py +36 -0
- pywargame/cyberboard/scenario.py +115 -0
- pywargame/cyberboard/testgrid.py +156 -0
- pywargame/cyberboard/tile.py +121 -0
- pywargame/cyberboard/tray.py +68 -0
- pywargame/cyberboard/windows.py +41 -0
- pywargame/cyberboard/zeropwd.py +45 -0
- pywargame/cyberboard.py +2728 -0
- pywargame/gbx0pwd.py +2776 -0
- pywargame/gbxextract.py +2795 -0
- pywargame/gsnexport.py +16499 -0
- pywargame/gsnextract.py +2790 -0
- pywargame/latex/__init__.py +2 -0
- pywargame/latex/collect.py +34 -0
- pywargame/latex/latexexporter.py +4010 -0
- pywargame/latex/main.py +184 -0
- pywargame/vassal/__init__.py +66 -0
- pywargame/vassal/base.py +139 -0
- pywargame/vassal/board.py +243 -0
- pywargame/vassal/buildfile.py +60 -0
- pywargame/vassal/chart.py +79 -0
- pywargame/vassal/chessclock.py +197 -0
- pywargame/vassal/collect.py +98 -0
- pywargame/vassal/collectpatch.py +28 -0
- pywargame/vassal/command.py +21 -0
- pywargame/vassal/documentation.py +322 -0
- pywargame/vassal/dumpcollect.py +28 -0
- pywargame/vassal/dumpvsav.py +28 -0
- pywargame/vassal/element.py +439 -0
- pywargame/vassal/exporter.py +89 -0
- pywargame/vassal/extension.py +101 -0
- pywargame/vassal/folder.py +103 -0
- pywargame/vassal/game.py +940 -0
- pywargame/vassal/gameelements.py +1091 -0
- pywargame/vassal/globalkey.py +127 -0
- pywargame/vassal/globalproperty.py +433 -0
- pywargame/vassal/grid.py +573 -0
- pywargame/vassal/map.py +1061 -0
- pywargame/vassal/mapelements.py +1020 -0
- pywargame/vassal/merge.py +57 -0
- pywargame/vassal/merger.py +460 -0
- pywargame/vassal/moduledata.py +275 -0
- pywargame/vassal/mrgcollect.py +31 -0
- pywargame/vassal/patch.py +44 -0
- pywargame/vassal/patchcollect.py +28 -0
- pywargame/vassal/player.py +83 -0
- pywargame/vassal/save.py +495 -0
- pywargame/vassal/skel.py +380 -0
- pywargame/vassal/trait.py +224 -0
- pywargame/vassal/traits/__init__.py +36 -0
- pywargame/vassal/traits/area.py +50 -0
- pywargame/vassal/traits/basic.py +35 -0
- pywargame/vassal/traits/calculatedproperty.py +22 -0
- pywargame/vassal/traits/cargo.py +29 -0
- pywargame/vassal/traits/click.py +41 -0
- pywargame/vassal/traits/clone.py +28 -0
- pywargame/vassal/traits/delete.py +24 -0
- pywargame/vassal/traits/deselect.py +32 -0
- pywargame/vassal/traits/dynamicproperty.py +112 -0
- pywargame/vassal/traits/globalcommand.py +55 -0
- pywargame/vassal/traits/globalhotkey.py +26 -0
- pywargame/vassal/traits/globalproperty.py +54 -0
- pywargame/vassal/traits/hide.py +67 -0
- pywargame/vassal/traits/label.py +76 -0
- pywargame/vassal/traits/layer.py +105 -0
- pywargame/vassal/traits/mark.py +20 -0
- pywargame/vassal/traits/mask.py +85 -0
- pywargame/vassal/traits/mat.py +26 -0
- pywargame/vassal/traits/moved.py +35 -0
- pywargame/vassal/traits/movefixed.py +51 -0
- pywargame/vassal/traits/nonrect.py +95 -0
- pywargame/vassal/traits/nostack.py +55 -0
- pywargame/vassal/traits/place.py +104 -0
- pywargame/vassal/traits/prototype.py +20 -0
- pywargame/vassal/traits/report.py +34 -0
- pywargame/vassal/traits/restrictaccess.py +28 -0
- pywargame/vassal/traits/restrictcommand.py +32 -0
- pywargame/vassal/traits/return.py +40 -0
- pywargame/vassal/traits/rotate.py +62 -0
- pywargame/vassal/traits/sendto.py +59 -0
- pywargame/vassal/traits/sheet.py +129 -0
- pywargame/vassal/traits/skel.py +9 -0
- pywargame/vassal/traits/stack.py +28 -0
- pywargame/vassal/traits/submenu.py +27 -0
- pywargame/vassal/traits/trail.py +61 -0
- pywargame/vassal/traits/trigger.py +72 -0
- pywargame/vassal/turn.py +272 -0
- pywargame/vassal/upgrade.py +191 -0
- pywargame/vassal/vmod.py +323 -0
- pywargame/vassal/vsav.py +100 -0
- pywargame/vassal/widget.py +358 -0
- pywargame/vassal/withtraits.py +634 -0
- pywargame/vassal/xml.py +4 -0
- pywargame/vassal/zone.py +399 -0
- pywargame/vassal.py +12500 -0
- pywargame/vmodpatch.py +12548 -0
- pywargame/vsavdump.py +12533 -0
- pywargame/vslmerge.py +13015 -0
- pywargame/wgexport.py +16689 -0
- pywargame/ztexport.py +14351 -0
- pywargame/zuntzu/__init__.py +5 -0
- pywargame/zuntzu/base.py +82 -0
- pywargame/zuntzu/collect.py +38 -0
- pywargame/zuntzu/countersheet.py +250 -0
- pywargame/zuntzu/dicehand.py +48 -0
- pywargame/zuntzu/exporter.py +936 -0
- pywargame/zuntzu/gamebox.py +154 -0
- pywargame/zuntzu/map.py +36 -0
- pywargame/zuntzu/piece.py +37 -0
- pywargame/zuntzu/scenario.py +208 -0
- pywargame/zuntzu/ztexp.py +115 -0
- pywargame-0.3.1.dist-info/METADATA +353 -0
- pywargame-0.3.1.dist-info/RECORD +150 -0
- pywargame-0.3.1.dist-info/WHEEL +5 -0
- pywargame-0.3.1.dist-info/licenses/LICENSE +5 -0
- pywargame-0.3.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,283 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from common import VerboseGuard
|
3
|
+
from common import Verbose
|
4
|
+
from . features import Features
|
5
|
+
## END_IMPORT
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
class BaseArchive:
|
10
|
+
WORD_SIZE = 2
|
11
|
+
|
12
|
+
def __init__(self,filename,mode='rb'):
|
13
|
+
'''Read data from a MFT CArchive stored on disk
|
14
|
+
|
15
|
+
Works as a context manager
|
16
|
+
'''
|
17
|
+
with VerboseGuard(f'Opening archive {filename}'):
|
18
|
+
self._filename = filename
|
19
|
+
self._file = open(filename,mode)
|
20
|
+
self._i = 0
|
21
|
+
self.vmsg = lambda *args : Verbose().message(*args)
|
22
|
+
#self.vmsg = lambda *args : None
|
23
|
+
|
24
|
+
def __enter__(self):
|
25
|
+
'''Enter context'''
|
26
|
+
return self
|
27
|
+
|
28
|
+
def __exit__(self,*args,**kwargs):
|
29
|
+
'''Exit context'''
|
30
|
+
self._file.close()
|
31
|
+
|
32
|
+
def tell(self):
|
33
|
+
pass
|
34
|
+
|
35
|
+
def read(self,n):
|
36
|
+
'''Read n bytes from archive'''
|
37
|
+
pass
|
38
|
+
|
39
|
+
def chr(self,n):
|
40
|
+
'''Read n characters from archive'''
|
41
|
+
b = self.read(n)
|
42
|
+
try:
|
43
|
+
c = b.decode()
|
44
|
+
self.vmsg(f'char->{c}')
|
45
|
+
return c
|
46
|
+
except:
|
47
|
+
print(f'Failed at {b} ({self._file.tell()})')
|
48
|
+
raise
|
49
|
+
|
50
|
+
def int(self,n):
|
51
|
+
'''Read an (unsigned) integer from archive'''
|
52
|
+
b = self.read(n)
|
53
|
+
i = int.from_bytes(b,'little' if Features().little_endian else 'big')
|
54
|
+
self.vmsg(f'int->{i}')
|
55
|
+
return i
|
56
|
+
|
57
|
+
def byte(self):
|
58
|
+
'''Read a byte from archive'''
|
59
|
+
return self.int(1)
|
60
|
+
|
61
|
+
def word(self):
|
62
|
+
'''Read a word (16bit integer) from archive'''
|
63
|
+
w = self.int(BaseArchive.WORD_SIZE)
|
64
|
+
self.vmsg(f'word->{w}')
|
65
|
+
return w;
|
66
|
+
|
67
|
+
|
68
|
+
def dword(self):
|
69
|
+
'''Read a double word (32bit integer) from archive'''
|
70
|
+
d = self.int(2*BaseArchive.WORD_SIZE)
|
71
|
+
self.vmsg(f'dword->{d}')
|
72
|
+
return d
|
73
|
+
|
74
|
+
def size(self):
|
75
|
+
'''Read a size'''
|
76
|
+
s = self.int(Features().size_size)
|
77
|
+
self.vmsg(f'size->{s}')
|
78
|
+
return s
|
79
|
+
|
80
|
+
def sub_size(self):
|
81
|
+
'''Read a size'''
|
82
|
+
s = self.int(Features().sub_size)
|
83
|
+
self.vmsg(f'sub->{s}')
|
84
|
+
return s
|
85
|
+
|
86
|
+
def count(self):
|
87
|
+
'''Read a count'''
|
88
|
+
if Features().size_size == 4:
|
89
|
+
return self.word()
|
90
|
+
|
91
|
+
c = self.word()
|
92
|
+
if c != 0xFFFF:
|
93
|
+
return c
|
94
|
+
|
95
|
+
c = self.dword()
|
96
|
+
if c != 0xFFFFFFFF:
|
97
|
+
return c
|
98
|
+
|
99
|
+
return int(8)
|
100
|
+
|
101
|
+
def iden(self):
|
102
|
+
'''Read an identifier'''
|
103
|
+
i = self.int(Features().id_size)
|
104
|
+
self.vmsg(f'id->{i}')
|
105
|
+
return i
|
106
|
+
|
107
|
+
def _strlen(self):
|
108
|
+
'''Read length of following string from archive'''
|
109
|
+
# See https://github.com/pixelspark/corespark/blob/master/Libraries/atlmfc/src/mfc/arccore.cpp
|
110
|
+
|
111
|
+
s = 1
|
112
|
+
l = self.byte()
|
113
|
+
|
114
|
+
if l < 0xFF: # Small ASCII string
|
115
|
+
self.vmsg(f'slen->{l},{s}')
|
116
|
+
return l, s
|
117
|
+
|
118
|
+
l = self.word()
|
119
|
+
if l == 0xFFFE: # Unicode - try again
|
120
|
+
s = 2
|
121
|
+
l = self.byte()
|
122
|
+
|
123
|
+
if l < 0xFF: # Small unicode
|
124
|
+
self.vmsg(f'slen->{l},{s}')
|
125
|
+
return l, s
|
126
|
+
|
127
|
+
|
128
|
+
l = self.word() # Large unicode string
|
129
|
+
|
130
|
+
if l < 0xFFFF: # Not super long
|
131
|
+
self.vmsg(f'slen->{l},{s}')
|
132
|
+
return l, s
|
133
|
+
|
134
|
+
l = self.dword()
|
135
|
+
|
136
|
+
if l < 0xFFFFFFFF: # Not hyper long
|
137
|
+
self.vmsg(f'slen->{l},{s}')
|
138
|
+
return l, s
|
139
|
+
|
140
|
+
|
141
|
+
self.vmsg(f'slen->{8},fixed')
|
142
|
+
return self.int(8)
|
143
|
+
|
144
|
+
def str(self):
|
145
|
+
'''Read a string from the archive'''
|
146
|
+
# See https://github.com/pixelspark/corespark/blob/master/Libraries/atlmfc/src/mfc/arccore.cpp
|
147
|
+
l, s = self._strlen()
|
148
|
+
# print(f'Read string of length {l}*{s} at {self.tell()}')
|
149
|
+
ret = [self.read(s) for i in range(l)]
|
150
|
+
try:
|
151
|
+
ret = [c.decode() for c in ret]
|
152
|
+
except:
|
153
|
+
ret = ['']
|
154
|
+
self.vmsg(f'str->"{"".join(ret)}"')
|
155
|
+
return ''.join(ret)
|
156
|
+
|
157
|
+
@property
|
158
|
+
def filename(self):
|
159
|
+
return self._filename
|
160
|
+
|
161
|
+
@property
|
162
|
+
def path(self):
|
163
|
+
from pathlib import Path
|
164
|
+
return Path(self._filename)
|
165
|
+
|
166
|
+
# ====================================================================
|
167
|
+
class UnbufferedArchive(BaseArchive):
|
168
|
+
def __init__(self,filename,mode='rb'):
|
169
|
+
'''Read data from a MFT CArchive stored on dist
|
170
|
+
|
171
|
+
Works as a context manager
|
172
|
+
'''
|
173
|
+
super(UnbufferedArchive,self).__init__(filename,mode='rb')
|
174
|
+
|
175
|
+
def read(self,n):
|
176
|
+
'''Read n bytes from archive - directly from file'''
|
177
|
+
b = self._file.read(n)
|
178
|
+
self.vmsg(f'read->{list(b)}')
|
179
|
+
# print(f'{self._i:6d} -> {b}')
|
180
|
+
self._i += n
|
181
|
+
return b
|
182
|
+
|
183
|
+
def tell(self):
|
184
|
+
return self._file.tell()
|
185
|
+
|
186
|
+
# ====================================================================
|
187
|
+
class BufferedArchive(BaseArchive):
|
188
|
+
def __init__(self,filename,mode='rb'):
|
189
|
+
'''Read data from a MFT CArchive stored on dist
|
190
|
+
|
191
|
+
Works as a context manager
|
192
|
+
'''
|
193
|
+
super(BufferedArchive,self).__init__(filename,mode='rb')
|
194
|
+
self._size = 4096
|
195
|
+
self._max = self._size
|
196
|
+
self._current = self._max
|
197
|
+
self._buffer = []
|
198
|
+
|
199
|
+
def read(self,n):
|
200
|
+
with VerboseGuard(f'Read {n} bytes') as g:
|
201
|
+
'''Read n bytes from archive - buffered
|
202
|
+
|
203
|
+
This emulates the behaviour of MFC CArchive::Read
|
204
|
+
'''
|
205
|
+
|
206
|
+
nmax = n
|
207
|
+
ntmp = min(nmax, self._max - self._current)
|
208
|
+
b = self._buffer[self._current:self._current+ntmp]
|
209
|
+
g(f'Take {ntmp} bytes from buffer -> {b}')
|
210
|
+
self._current += ntmp
|
211
|
+
nmax -= ntmp
|
212
|
+
|
213
|
+
if nmax != 0:
|
214
|
+
g(f'Need to read at least {nmax} from file')
|
215
|
+
assert self._current == self._max,\
|
216
|
+
f'Something is wrong! {self._current} != ' \
|
217
|
+
f'{self._max} (1)'
|
218
|
+
|
219
|
+
g(f'Missing {nmax} bytes -> ({nmax % self._size})')
|
220
|
+
ntmp = nmax - (nmax % self._size)
|
221
|
+
nread = 0
|
222
|
+
nleft = ntmp
|
223
|
+
nbytes = 0
|
224
|
+
while True:
|
225
|
+
tmpbuf = self._file.read(nleft)
|
226
|
+
nbytes = len(tmpbuf)
|
227
|
+
nread += nbytes
|
228
|
+
nleft -= nbytes
|
229
|
+
b += tmpbuf
|
230
|
+
g(f'Read {nleft} -> {tmpbuf}')
|
231
|
+
|
232
|
+
if nbytes <= 0 or nleft <= 0:
|
233
|
+
break
|
234
|
+
|
235
|
+
nmax -= nread
|
236
|
+
|
237
|
+
if nmax > 0 and nread == ntmp:
|
238
|
+
# Last read chunk into buffer and copy
|
239
|
+
assert self._current == self._max,\
|
240
|
+
f'Something is wrong! {self._current} != ' \
|
241
|
+
f'{self._max} (2)'
|
242
|
+
|
243
|
+
assert nmax < self._size, \
|
244
|
+
f'Something is wrong {nmax} >= {self._size}'
|
245
|
+
|
246
|
+
nlastleft = max(nmax,self._size)
|
247
|
+
nlastbytes = 0
|
248
|
+
nread = 0
|
249
|
+
self._buffer = []
|
250
|
+
while True:
|
251
|
+
tmpbuf = self._file.read(nlastleft)
|
252
|
+
nlastbytes = len(tmpbuf)
|
253
|
+
nread += nlastbytes
|
254
|
+
nlastleft -= nlastbytes
|
255
|
+
self._buffer += tmpbuf
|
256
|
+
|
257
|
+
if (nlastbytes <= 0) or \
|
258
|
+
(nlastleft <= 0) or \
|
259
|
+
nread >= ntmp:
|
260
|
+
break
|
261
|
+
|
262
|
+
self._current = 0
|
263
|
+
self._max = nread
|
264
|
+
|
265
|
+
ntmp = min(nmax, self._max - self._current)
|
266
|
+
b += self._buffer[self._current:
|
267
|
+
self._current+ntmp]
|
268
|
+
self._current += ntmp
|
269
|
+
nmax -= ntmp
|
270
|
+
|
271
|
+
g(b)
|
272
|
+
return b''.join(b)
|
273
|
+
|
274
|
+
def tell(self):
|
275
|
+
return self._file.tell()
|
276
|
+
|
277
|
+
|
278
|
+
Archive = UnbufferedArchive
|
279
|
+
# Archive = BufferedArchive
|
280
|
+
|
281
|
+
#
|
282
|
+
# EOF
|
283
|
+
#
|
@@ -0,0 +1,63 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from common import VerboseGuard
|
3
|
+
## END_IMPORT
|
4
|
+
|
5
|
+
# --------------------------------------------------------------------
|
6
|
+
class CbFont:
|
7
|
+
def __init__(self,ar):
|
8
|
+
'''Shared structure that holds font information'''
|
9
|
+
# Fonts
|
10
|
+
with VerboseGuard('Reading font definition'):
|
11
|
+
self._size = ar.word()
|
12
|
+
self._flags = ar.word()
|
13
|
+
self._family = ar.word()
|
14
|
+
self._name = ar.str()
|
15
|
+
|
16
|
+
def isBold(self):
|
17
|
+
return self._flags & 0x1
|
18
|
+
|
19
|
+
def isItalic(self):
|
20
|
+
return self._flags & 0x2
|
21
|
+
|
22
|
+
def isUnderline(self):
|
23
|
+
return self._flags & 0x4
|
24
|
+
|
25
|
+
def __str__(self):
|
26
|
+
return (f'Font:{self._name} ({self._family}) @ '
|
27
|
+
f'{self._size} ({self._flags:08x})')
|
28
|
+
|
29
|
+
# --------------------------------------------------------------------
|
30
|
+
class CbManager:
|
31
|
+
def __init__(self,ar):
|
32
|
+
'''Base class for some managers'''
|
33
|
+
with VerboseGuard('Reading general manager'):
|
34
|
+
self._foreground = ar.dword()
|
35
|
+
self._background = ar.dword()
|
36
|
+
self._linewidth = ar.word()
|
37
|
+
self._font = CbFont(ar)
|
38
|
+
self._reserved = [ar.word() for _ in range(4)]
|
39
|
+
|
40
|
+
def _readNsub(self,ar,sub_size):
|
41
|
+
return ar.int(sub_size)
|
42
|
+
|
43
|
+
def _readSub(self,ar,cls,sub_size=None):
|
44
|
+
if sub_size is None:
|
45
|
+
sub_size = Features().sub_size
|
46
|
+
with VerboseGuard(f'Reading sub {cls} of manager ({sub_size})'):
|
47
|
+
n = self._readNsub(ar,sub_size)
|
48
|
+
return [cls(ar) for _ in range(n)]
|
49
|
+
|
50
|
+
def _strSub(self,title,subs):
|
51
|
+
subl = '\n '.join([str(s) for s in subs])
|
52
|
+
return f' # {title}: {len(subs)}\n {subl}'
|
53
|
+
|
54
|
+
def __str__(self):
|
55
|
+
return (f' Foreground: {self._foreground:08x}\n'
|
56
|
+
f' Background: {self._background:08x}\n'
|
57
|
+
f' Linewidth: {self._linewidth}\n'
|
58
|
+
f' Font: {self._font}\n'
|
59
|
+
f' Reserved: {self._reserved}\n')
|
60
|
+
|
61
|
+
#
|
62
|
+
# EOF
|
63
|
+
#
|