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,115 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from common import VerboseGuard
|
3
|
+
from . head import *
|
4
|
+
from . player import GSNPlayerManager
|
5
|
+
from . windows import GSNWindows
|
6
|
+
from . palette import GSNTrayPalette
|
7
|
+
from . palette import GSNMarkPalette
|
8
|
+
from . board import GSNBoardManager
|
9
|
+
from . tray import GSNTrayManager
|
10
|
+
from . piece import GSNPieceTable
|
11
|
+
from . gamebox import GameBox
|
12
|
+
## END_IMPORT
|
13
|
+
|
14
|
+
class GSNInfo:
|
15
|
+
def __init__(self,ar):
|
16
|
+
'''Scenario information'''
|
17
|
+
self._disableOwnerTips = ar.word()
|
18
|
+
self._reserved = [ar.word() for _ in range(3)]
|
19
|
+
self._gameID = ar.dword() # 4 -> 14
|
20
|
+
self._majorRevs = ar.dword() # 4 -> 6
|
21
|
+
self._minorRevs = ar.dword() # 4 -> 10
|
22
|
+
self._gbxFilename = ar.str()
|
23
|
+
self._scenarioID = ar.dword()
|
24
|
+
self._title = ar.str() # Y -> 30+X+Y
|
25
|
+
self._author = ar.str() # X -> 30+X
|
26
|
+
self._description = ar.str() # Z -> 30+X+Y+Z
|
27
|
+
self._keepBackup = ar.word()
|
28
|
+
self._keepHistory = ar.word()
|
29
|
+
self._verifyState = ar.word()
|
30
|
+
self._verifySave = ar.word()
|
31
|
+
self._showObjectTip = ar.word()
|
32
|
+
|
33
|
+
def __str__(self):
|
34
|
+
return ('Information:\n'
|
35
|
+
f' Disable owner tips: {self._disableOwnerTips}\n'
|
36
|
+
f' Reserved: {self._reserved}\n'
|
37
|
+
f' Game ID: {self._gameID}\n'
|
38
|
+
f' Major revision: {self._majorRevs}\n'
|
39
|
+
f' Minor revision: {self._minorRevs}\n'
|
40
|
+
f' Game box filename: {self._gbxFilename}\n'
|
41
|
+
f' Scenario ID: {self._scenarioID}\n'
|
42
|
+
f' Title: {self._title}\n'
|
43
|
+
f' Author: {self._author}\n'
|
44
|
+
f' Description: {self._description}\n'
|
45
|
+
f' Keep backup: {self._keepBackup}\n'
|
46
|
+
f' Keep history: {self._keepHistory}\n'
|
47
|
+
f' Verify state: {self._verifyState}\n'
|
48
|
+
f' Verify save: {self._verifySave}\n'
|
49
|
+
f' Show object tips: {self._showObjectTip}\n')
|
50
|
+
|
51
|
+
# ====================================================================
|
52
|
+
class Scenario:
|
53
|
+
def __init__(self,ar,gbxfilename=None):
|
54
|
+
'''Container of game'''
|
55
|
+
with VerboseGuard(f'Reading scenario'):
|
56
|
+
self._header = GBXHeader (ar,GBXHeader.SCENARIO)
|
57
|
+
self._info = GSNInfo (ar)
|
58
|
+
self._strings = GSNStrings (ar)
|
59
|
+
self._playerManager = GSNPlayerManager(ar)
|
60
|
+
self._windows = GSNWindows (ar)
|
61
|
+
self._trayA = GSNTrayPalette (ar,self._header._vers,'A')
|
62
|
+
self._trayB = GSNTrayPalette (ar,self._header._vers,'B')
|
63
|
+
self._mark = GSNMarkPalette (ar,self._header._vers)
|
64
|
+
self._boards = GSNBoardManager (ar,self._header._vers)
|
65
|
+
self._trayManager = GSNTrayManager (ar,self._header._vers)
|
66
|
+
self._pieceTable = GSNPieceTable (ar,self._header._vers)
|
67
|
+
# Possibly override GBX file name
|
68
|
+
if gbxfilename is not None and gbxfilename != '':
|
69
|
+
self._info._gbxFilename = gbxfilename
|
70
|
+
|
71
|
+
self.readGameBox(ar)
|
72
|
+
|
73
|
+
def readGameBox(self,ar):
|
74
|
+
from pathlib import Path
|
75
|
+
with VerboseGuard(f'Read game box file {self._info._gbxFilename}') as v:
|
76
|
+
gbxname = self._info._gbxFilename
|
77
|
+
gbxpath = Path(gbxname)
|
78
|
+
|
79
|
+
if not gbxpath.exists():
|
80
|
+
v(f'GameBox file {gbxpath} does not exist')
|
81
|
+
if '\\' in gbxname:
|
82
|
+
gbxname = gbxname.replace('\\','/')
|
83
|
+
gbxpath = Path(gbxname)
|
84
|
+
gbxpath = ar.path.parent / Path(gbxpath.name)
|
85
|
+
if not gbxpath.exists():
|
86
|
+
raise RuntimeError(f'GameBox file {gbxpath} cannot be found')
|
87
|
+
|
88
|
+
self._gbx = GameBox.fromFile(str(gbxpath))
|
89
|
+
|
90
|
+
if self._gbx._info._gameID != self._info._gameID:
|
91
|
+
raise RuntimeError(f'Game IDs from GBX and GSN does not match: '
|
92
|
+
f'{self._gbx._info._gameID} versus '
|
93
|
+
f'{self._header._gameID}')
|
94
|
+
|
95
|
+
def __str__(self):
|
96
|
+
return (str(self._header)+
|
97
|
+
str(self._info)+
|
98
|
+
str(self._strings)+
|
99
|
+
str(self._playerManager)+
|
100
|
+
str(self._windows)+
|
101
|
+
str(self._trayA)+
|
102
|
+
str(self._trayB)+
|
103
|
+
str(self._mark)+
|
104
|
+
str(self._boards)+
|
105
|
+
str(self._trayManager)+
|
106
|
+
str(self._pieceTable)+
|
107
|
+
str(self._gbx)+
|
108
|
+
''
|
109
|
+
)
|
110
|
+
|
111
|
+
|
112
|
+
@classmethod
|
113
|
+
def fromFile(cls,filename,gbxfile=None):
|
114
|
+
with Archive(filename,'rb') as ar:
|
115
|
+
return Scenario(ar,gbxfile)
|
@@ -0,0 +1,156 @@
|
|
1
|
+
class Geometry:
|
2
|
+
RECTANGLE = 0
|
3
|
+
HORIZONTAL_BRICK = 1
|
4
|
+
VERTICAL_BRICK = 2
|
5
|
+
HEXAGON = 3
|
6
|
+
SIDEWAYS_HEXAGON = 4
|
7
|
+
STAGGER_OUT = 0
|
8
|
+
STAGGER_IN = 1
|
9
|
+
TYPES = {
|
10
|
+
RECTANGLE : 'rectangle',
|
11
|
+
HORIZONTAL_BRICK: 'horizontal brick',
|
12
|
+
VERTICAL_BRICK : 'vertical brick',
|
13
|
+
HEXAGON : 'hexagon',
|
14
|
+
SIDEWAYS_HEXAGON: 'sideways hexagon'
|
15
|
+
}
|
16
|
+
STAGGERS = {
|
17
|
+
STAGGER_OUT: 'out',
|
18
|
+
STAGGER_IN: 'in'
|
19
|
+
}
|
20
|
+
SKETCHES = {
|
21
|
+
RECTANGLE : r'''
|
22
|
+
|---|---|
|
23
|
+
|---|---|''',
|
24
|
+
HORIZONTAL_BRICK: r'''
|
25
|
+
|---|---|
|
26
|
+
|---|''',
|
27
|
+
VERTICAL_BRICK : r'''
|
28
|
+
|--| |
|
29
|
+
| |--|
|
30
|
+
|--| |''',
|
31
|
+
HEXAGON : r''' _
|
32
|
+
/ \
|
33
|
+
\_/''' ,
|
34
|
+
SIDEWAYS_HEXAGON: r'''/\
|
35
|
+
||
|
36
|
+
\/'''
|
37
|
+
}
|
38
|
+
|
39
|
+
def __init__(self,nrows,ncols,tpe,stagger,w,h):
|
40
|
+
self._nrows = nrows
|
41
|
+
self._ncols = ncols
|
42
|
+
self._type = tpe
|
43
|
+
self._stagger = stagger
|
44
|
+
self._width = w
|
45
|
+
self._height = h
|
46
|
+
self._dx = w
|
47
|
+
self._dy = h
|
48
|
+
|
49
|
+
if self._type == self.HEXAGON:
|
50
|
+
self._dx = int(0.75 * self._dx)
|
51
|
+
elif self._type == self.SIDEWAYS_HEXAGON:
|
52
|
+
self._dy = int(0.75 * self._dy)
|
53
|
+
|
54
|
+
def toPixel(self,row,col):
|
55
|
+
x = col * self._dx
|
56
|
+
y = row * self._dy
|
57
|
+
if self._type == self.RECTANGLE: # No offset for rectangles
|
58
|
+
return x,y
|
59
|
+
elif self._type in [self.HORIZONTAL_BRICK,self.SIDEWAYS_HEXAGON]:
|
60
|
+
x += self._dx//2 if (row % 2) != self._stagger else 0
|
61
|
+
elif self._type in [self.VERTICAL_BRICK,self.HEXAGON]:
|
62
|
+
y += self._dy//2 if (col % 2) != self._stagger else 0
|
63
|
+
return x,y
|
64
|
+
|
65
|
+
def toGrid(self,x,y):
|
66
|
+
col = x / self._dx
|
67
|
+
row = y / self._dy
|
68
|
+
if self._type in [self.HORIZONTAL_BRICK,self.SIDEWAYS_HEXAGON]:
|
69
|
+
col -= .5 if (int(row) % 2) != self._stagger else 0
|
70
|
+
if self._type in [self.VERTICAL_BRICK,self.HEXAGON]:
|
71
|
+
row -= .5 if (int(col) % 2) != self._stagger else 0
|
72
|
+
|
73
|
+
return int(row), int(col)
|
74
|
+
|
75
|
+
def boardSize(self,nrows,ncols):
|
76
|
+
w = ncols * self._dx
|
77
|
+
h = nrows * self._dy
|
78
|
+
|
79
|
+
if self._type in [2,3]:
|
80
|
+
h += self._dy // 2
|
81
|
+
if self._type in [1,4]:
|
82
|
+
w += self._dx // 2
|
83
|
+
if self._type == 3:
|
84
|
+
w += self._dx // 3
|
85
|
+
if self._type == 4:
|
86
|
+
h += self._dy // 3
|
87
|
+
|
88
|
+
return w+1,h+1
|
89
|
+
|
90
|
+
def test(self,stepX=None,stepY=None):
|
91
|
+
from numpy import linspace
|
92
|
+
|
93
|
+
width, height = self.boardSize(self._nrows,self._ncols)
|
94
|
+
print(f'Test: {self._type},{self._stagger}')
|
95
|
+
print(f'{self._width}x{self._height} ({self._dx},{self._dy})')
|
96
|
+
print(f' {self.TYPES[self._type]} {self.STAGGERS[self._stagger]}')
|
97
|
+
print(self.SKETCHES[self._type])
|
98
|
+
|
99
|
+
for row in range(self._nrows):
|
100
|
+
for col in range(self._ncols):
|
101
|
+
x, y = self.toPixel(row,col)
|
102
|
+
cx, cy = x + self._width // 2, y + self._height // 2
|
103
|
+
r, c = self.toGrid(cx,cy)
|
104
|
+
print(f'== {row:3d},{col:3d} -> {cx:6d},{cy:6d} -> {r:3d},{c:3d}')
|
105
|
+
assert r == row, f'Rows (from {row} to {r}) inconsistent'
|
106
|
+
assert c == col, f'Columns (from {col} to {c}) inconsistent'
|
107
|
+
|
108
|
+
if row >= 3 or col >= 3:
|
109
|
+
continue
|
110
|
+
for dx in linspace(-self._width/4,self._width/4,5):
|
111
|
+
idx = int(dx)
|
112
|
+
for dy in linspace(-self._height/4,self._height/4,5):
|
113
|
+
idy = int(dy)
|
114
|
+
xx = cx + idx
|
115
|
+
yy = cy + idy
|
116
|
+
r, c = self.toGrid(xx,yy)
|
117
|
+
if r != row or c != col:
|
118
|
+
print(f'{row:3d},{col:3d} -> {cx:6d}+{idx:3d}={xx:6d},{cy:6d}+{idy:3d}={yy:6d} -> {r:3d},{c:3d}')
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
|
123
|
+
def testit(tpe,stagger):
|
124
|
+
w = 100
|
125
|
+
h = 100
|
126
|
+
if tpe == 3:
|
127
|
+
w = 80
|
128
|
+
h = 68
|
129
|
+
elif tpe == 4:
|
130
|
+
w = 68
|
131
|
+
h = 80
|
132
|
+
|
133
|
+
geo = Geometry(5,5,tpe,stagger,w,h)
|
134
|
+
geo.test()
|
135
|
+
|
136
|
+
|
137
|
+
def testall():
|
138
|
+
for t in [Geometry.RECTANGLE,
|
139
|
+
Geometry.HORIZONTAL_BRICK,
|
140
|
+
Geometry.VERTICAL_BRICK,
|
141
|
+
Geometry.HEXAGON,
|
142
|
+
Geometry.SIDEWAYS_HEXAGON]:
|
143
|
+
for s in [Geometry.STAGGER_IN,
|
144
|
+
Geometry.STAGGER_OUT]:
|
145
|
+
testit(t,s)
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
testall()
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
|
154
|
+
|
155
|
+
|
156
|
+
|
@@ -0,0 +1,121 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from common import VerboseGuard
|
3
|
+
from . image import GBXImage
|
4
|
+
from . base import CbManager
|
5
|
+
## END_IMPORT
|
6
|
+
|
7
|
+
# ====================================================================
|
8
|
+
class GBXTileLocation:
|
9
|
+
def __init__(self,ar):
|
10
|
+
'''Where a tile can be found'''
|
11
|
+
with VerboseGuard('Reading tile location') as g:
|
12
|
+
self._sheet = (ar.word() if not Features().size_size == 8 else
|
13
|
+
ar.size())
|
14
|
+
self._offset = ar.word()
|
15
|
+
g(f'Tile location at sheet={self._sheet} offset={self._offset}')
|
16
|
+
if self._sheet == 65535: self._sheet = -1
|
17
|
+
|
18
|
+
def __str__(self):
|
19
|
+
return f'{self._sheet:3d} @ {self._offset:6d}'
|
20
|
+
|
21
|
+
|
22
|
+
# --------------------------------------------------------------------
|
23
|
+
class GBXTileDefinition:
|
24
|
+
def __init__(self,ar):
|
25
|
+
'''The definition of a tile'''
|
26
|
+
with VerboseGuard('Reading tile definition'):
|
27
|
+
self._full = GBXTileLocation(ar)
|
28
|
+
self._half = GBXTileLocation(ar)
|
29
|
+
self._fill = ar.dword()
|
30
|
+
|
31
|
+
def __str__(self):
|
32
|
+
return f'Full: {self._full}, Half: {self._half}, Fill: {self._fill:08x}'
|
33
|
+
|
34
|
+
|
35
|
+
# --------------------------------------------------------------------
|
36
|
+
class GBXTileSet:
|
37
|
+
def __init__(self,ar):
|
38
|
+
'''A set of tiles'''
|
39
|
+
with VerboseGuard('Reading tile set'):
|
40
|
+
self._name = ar.str()
|
41
|
+
n = ar.word() if Features().size_size != 8 else ar.size()
|
42
|
+
self._ids = [ar.iden()
|
43
|
+
for _ in range(n)]
|
44
|
+
|
45
|
+
def __str__(self):
|
46
|
+
return (self._name + ':' + ','.join([str(i) for i in self._ids]))
|
47
|
+
|
48
|
+
|
49
|
+
# --------------------------------------------------------------------
|
50
|
+
class GBXTileSheet:
|
51
|
+
def __init__(self,ar,transparent):
|
52
|
+
'''A single image that has multiple tile images in it
|
53
|
+
|
54
|
+
X,Y are the tile sizes
|
55
|
+
'''
|
56
|
+
with VerboseGuard('Reading tile sheet'):
|
57
|
+
self._x = ar.word()
|
58
|
+
self._y = ar.word()
|
59
|
+
hasBM = ar.word()
|
60
|
+
self._img = GBXImage(ar,transparent) if hasBM else None
|
61
|
+
|
62
|
+
def sub(self,off):
|
63
|
+
if self._img is None:
|
64
|
+
return None
|
65
|
+
|
66
|
+
return self._img._img.crop((0,off,self._x,off+self._y))
|
67
|
+
|
68
|
+
def __str__(self):
|
69
|
+
bm = str(self._img) if self._img is not None else 'None'
|
70
|
+
return (f'c=({self._x:4d},{self._y:4d}) bitmap={bm}')
|
71
|
+
|
72
|
+
|
73
|
+
# --------------------------------------------------------------------
|
74
|
+
class GBXTileManager(CbManager):
|
75
|
+
def __init__(self,ar):
|
76
|
+
'''Tile manager (including tiles)'''
|
77
|
+
with VerboseGuard('Reading tile manager'):
|
78
|
+
self._transparent = ar.dword()
|
79
|
+
super(GBXTileManager,self).__init__(ar)
|
80
|
+
|
81
|
+
ts = lambda ar : GBXTileSheet(ar, self._transparent)
|
82
|
+
self._tiledefs = self._readSub(ar,GBXTileDefinition,
|
83
|
+
Features().id_size)
|
84
|
+
self._tilesets = self._readSub(ar,GBXTileSet)
|
85
|
+
self._tilesheets = self._readSub(ar,ts)
|
86
|
+
self._toStore = {} # Used in boards, not elsewhere
|
87
|
+
|
88
|
+
def image(self,tileID):
|
89
|
+
if tileID is None:
|
90
|
+
return None
|
91
|
+
if tileID == 0xFFFF:
|
92
|
+
return None
|
93
|
+
|
94
|
+
tileDef = self._tiledefs[tileID]
|
95
|
+
tileSht = self._tilesheets[tileDef._full._sheet]
|
96
|
+
img = tileSht.sub(tileDef._full._offset)
|
97
|
+
return img
|
98
|
+
|
99
|
+
def store(self,tileID):
|
100
|
+
filename = self._toStore.get(tileID,{}).get('filename',None)
|
101
|
+
if filename is None:
|
102
|
+
filename = f'tile_{tileID:04d}.png'
|
103
|
+
self._toStore[tileID] = {
|
104
|
+
'filename': filename,
|
105
|
+
'image' : self.image(tileID)
|
106
|
+
}
|
107
|
+
|
108
|
+
return filename
|
109
|
+
|
110
|
+
|
111
|
+
def __str__(self):
|
112
|
+
return ('Tile manager:\n'
|
113
|
+
+ f' Transparent: {self._transparent:08x}\n'
|
114
|
+
+ super(GBXTileManager,self).__str__()
|
115
|
+
+ self._strSub('tiles',self._tiledefs) + '\n'
|
116
|
+
+ self._strSub('tile sets',self._tilesets) + '\n'
|
117
|
+
+ self._strSub('tile sheets',self._tilesheets))
|
118
|
+
|
119
|
+
#
|
120
|
+
# EOF
|
121
|
+
#
|
@@ -0,0 +1,68 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from common import VerboseGuard
|
3
|
+
from . head import num_version, readVector
|
4
|
+
## END_IMPORT
|
5
|
+
|
6
|
+
class GSNTraySet:
|
7
|
+
def __init__(self,ar,vers):
|
8
|
+
with VerboseGuard(f'Reading tray set') as g:
|
9
|
+
self._name = ar.str()
|
10
|
+
g(f'Tray set: {self._name}')
|
11
|
+
self._random = (ar.word() if Features().piece_100 else 0)
|
12
|
+
self._visibleFlags = ar.dword()
|
13
|
+
self._ownerMask = (ar.word() if vers < num_version(3,10) else
|
14
|
+
ar.dword())
|
15
|
+
self._restrict = ar.word()
|
16
|
+
self._pieces = readVector(ar,lambda ar: ar.iden())
|
17
|
+
|
18
|
+
def __len__(self):
|
19
|
+
return len(self._pieces)
|
20
|
+
|
21
|
+
def toDict(self):
|
22
|
+
viz = self._visibleFlags & ~0xFFFF8000
|
23
|
+
# print(f'{self._visibleFlags:08x} -> {viz}')
|
24
|
+
vizStr = {0: 'all',
|
25
|
+
1: 'owner',
|
26
|
+
2: 'generic',
|
27
|
+
3: 'none'}.get(viz,'')
|
28
|
+
return {'name': self._name,
|
29
|
+
'visible': vizStr,
|
30
|
+
'owner': self._ownerMask,
|
31
|
+
'restrict': self._restrict,
|
32
|
+
'pieces': self._pieces }
|
33
|
+
|
34
|
+
def __str__(self):
|
35
|
+
return (f'Tray set: {self._name} '
|
36
|
+
f'[visible={self._visibleFlags},'
|
37
|
+
f'ownerMask={self._ownerMask},'
|
38
|
+
f'resrict={self._restrict}] '
|
39
|
+
f'({len(self)}): {self._pieces}')
|
40
|
+
|
41
|
+
|
42
|
+
class GSNTrayManager:
|
43
|
+
def __init__(self,ar,vers,iden=''):
|
44
|
+
with VerboseGuard(f'Reading tray {iden} manager @ {ar.tell()}') as g:
|
45
|
+
self._iden = iden
|
46
|
+
self._reserved = [ar.word() for _ in range(4)]
|
47
|
+
g(f'{self._reserved}')
|
48
|
+
self._dummy = (ar.byte() if vers >= num_version(4,0) else 0)
|
49
|
+
self._sets = readVector(ar, lambda ar : GSNTraySet(ar,vers))
|
50
|
+
|
51
|
+
def __len__(self):
|
52
|
+
return len(self._sets)
|
53
|
+
|
54
|
+
def toDict(self):
|
55
|
+
return [s.toDict() for s in self._sets]
|
56
|
+
|
57
|
+
def __str__(self):
|
58
|
+
pl = '\n '.join([str(s) for s in self._sets])
|
59
|
+
return f'TrayManager: {self._iden} ({len(self)})\n {pl}\n'
|
60
|
+
|
61
|
+
|
62
|
+
#
|
63
|
+
# EOF
|
64
|
+
#
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
## BEGIN_IMPORT
|
2
|
+
from common import VerboseGuard
|
3
|
+
## END_IMPORT
|
4
|
+
|
5
|
+
class GSNWindow:
|
6
|
+
def __init__(self,ar):
|
7
|
+
with VerboseGuard('Reading window state') as g:
|
8
|
+
self._code = ar.word()
|
9
|
+
self._user = ar.word()
|
10
|
+
self._board = ar.iden()
|
11
|
+
self._state = [ar.dword() for _ in range(10)]
|
12
|
+
n = ar.size()
|
13
|
+
g(f'Read {self._code} {self._user} {self._board} {self._state}')
|
14
|
+
g(f'Reading {n} bytes at {ar.tell()}')
|
15
|
+
self._buffer = ar.read(n)
|
16
|
+
|
17
|
+
def __str__(self):
|
18
|
+
return (f'code={self._code:04x} '
|
19
|
+
f'user={self._user:04x} '
|
20
|
+
f'board={self._board:04x} '
|
21
|
+
f'buffer={len(self._buffer)}')
|
22
|
+
|
23
|
+
class GSNWindows:
|
24
|
+
def __init__(self,ar):
|
25
|
+
with VerboseGuard(f'Reading window states') as g:
|
26
|
+
self._savePositions = ar.word()
|
27
|
+
self._enable = ar.byte()
|
28
|
+
n = ar.size() if self._enable else 0
|
29
|
+
g(f'Save position: {self._savePositions}, '
|
30
|
+
f'enable {self._enable} '
|
31
|
+
f'n={n}')
|
32
|
+
self._states = [GSNWindow(ar) for _ in range(n)]
|
33
|
+
|
34
|
+
def __str__(self):
|
35
|
+
pl = '\n '.join([str(s) for s in self._states])
|
36
|
+
return f'Windows states ({len(self._states)})\n {pl}\n'
|
37
|
+
|
38
|
+
|
39
|
+
#
|
40
|
+
# EOF
|
41
|
+
#
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
## BEGIN_IMPORT
|
3
|
+
from gamebox import GBXInfo
|
4
|
+
from head import GBXHeader
|
5
|
+
from archive import Archive
|
6
|
+
## END_IMPORT
|
7
|
+
|
8
|
+
nullpwd = b'\xee\n\xcbg\xbc\xdb\x92\x1a\x0c\xd2\xf1y\x83*\x96\xc9'
|
9
|
+
|
10
|
+
def zeropwd(filename):
|
11
|
+
from pathlib import Path
|
12
|
+
|
13
|
+
pos = None
|
14
|
+
with Archive(filename,'rb') as ar:
|
15
|
+
header = GBXHeader(ar,GBXHeader.BOX)
|
16
|
+
box = GBXInfo(ar)
|
17
|
+
|
18
|
+
|
19
|
+
pos = ar.tell() - 4*2 - 2 - 2 - 16
|
20
|
+
|
21
|
+
with open(filename,'rb') as file:
|
22
|
+
cnt = file.read()
|
23
|
+
old = cnt[pos:pos+16]
|
24
|
+
|
25
|
+
lcnt = list(cnt)
|
26
|
+
lcnt[pos:pos+16] = list(nullpwd)
|
27
|
+
ncnt = bytes(lcnt)
|
28
|
+
|
29
|
+
on = Path(filename)
|
30
|
+
on = on.with_stem(on.stem + '-new')
|
31
|
+
with open(on,'wb') as file:
|
32
|
+
file.write(ncnt)
|
33
|
+
|
34
|
+
|
35
|
+
if __name__ == '__main__':
|
36
|
+
from argparse import ArgumentParser, FileType
|
37
|
+
ap = ArgumentParser(description='Disable password in gamebox')
|
38
|
+
ap.add_argument('input', type=str, help='The file')
|
39
|
+
|
40
|
+
args = ap.parse_args()
|
41
|
+
|
42
|
+
zeropwd(args.input)
|
43
|
+
|
44
|
+
|
45
|
+
|