Rev 12 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 12 | Rev 25 | ||
---|---|---|---|
1 | unit GameBinStruct; |
1 | unit GameBinStruct; |
2 | 2 | ||
- | 3 | (* |
|
- | 4 | * Plumbers Don't Wear Ties - Structure of GAME.BIN |
|
- | 5 | * Copyright 2017 - 2020 Daniel Marschall, ViaThinkSoft |
|
- | 6 | * |
|
- | 7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
- | 8 | * you may not use this file except in compliance with the License. |
|
- | 9 | * You may obtain a copy of the License at |
|
- | 10 | * |
|
- | 11 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
- | 12 | * |
|
- | 13 | * Unless required by applicable law or agreed to in writing, software |
|
- | 14 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
- | 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
- | 16 | * See the License for the specific language governing permissions and |
|
- | 17 | * limitations under the License. |
|
- | 18 | *) |
|
- | 19 | ||
3 | {$A-} |
20 | {$A-} |
4 | 21 | ||
5 | interface |
22 | interface |
6 | 23 | ||
7 | const |
24 | const |
8 | SCENEID_PREVDECISION = -1; |
25 | SCENEID_PREVDECISION = -1; |
9 | SCENEID_ENDGAME = 32767; |
26 | SCENEID_ENDGAME = 32767; |
10 | 27 | ||
11 | SEGMENT_BEGINNING = 0; |
28 | SEGMENT_BEGINNING = 0; |
12 | SEGMENT_DECISION = 1; |
29 | SEGMENT_DECISION = 1; |
13 | 30 | ||
14 | type |
31 | type |
15 | PCoord = ^TCoord; |
32 | PCoord = ^TCoord; |
16 | TCoord = packed record |
33 | TCoord = packed record |
17 | x: Word; |
34 | x: Word; |
18 | y: Word; |
35 | y: Word; |
19 | end; |
36 | end; |
20 | 37 | ||
21 | PActionDef = ^TActionDef; |
38 | PActionDef = ^TActionDef; |
22 | TActionDef = packed record |
39 | TActionDef = packed record |
23 | scoreDelta: Integer; |
40 | scoreDelta: Integer; |
24 | nextSceneID: SmallInt; // will jump to the scene with the name "SCxx", |
41 | nextSceneID: SmallInt; // will jump to the scene with the name "SCxx", |
25 | // where xx stands for nextSceneID (2 digits at least) |
42 | // where xx stands for nextSceneID (2 digits at least) |
26 | // 7FFF (32767) = end game |
43 | // 7FFF (32767) = end game |
27 | // FFFF ( -1) = go back to the last decision |
44 | // FFFF ( -1) = go back to the last decision |
28 | sceneSegment: SmallInt; // 0 = scene from beginning, 1 = decision page |
45 | sceneSegment: SmallInt; // 0 = scene from beginning, 1 = decision page |
29 | cHotspotTopLeft: TCoord; |
46 | cHotspotTopLeft: TCoord; |
30 | cHotspotBottomRight: TCoord; |
47 | cHotspotBottomRight: TCoord; |
31 | end; |
48 | end; |
32 | 49 | ||
33 | PAnsiFileName = ^TAnsiFileName; |
50 | PAnsiFileName = ^TAnsiFileName; |
34 | TAnsiFileName = array[0..13] of AnsiChar; |
51 | TAnsiFileName = array[0..13] of AnsiChar; |
35 | 52 | ||
36 | PSceneDef = ^TSceneDef; |
53 | PSceneDef = ^TSceneDef; |
37 | TSceneDef = packed record |
54 | TSceneDef = packed record |
38 | numPics: Word; |
55 | numPics: Word; |
39 | pictureIndex: Word; |
56 | pictureIndex: Word; |
40 | numActions: Word; |
57 | numActions: Word; |
41 | szSceneFolder: TAnsiFileName; // Foldername *must* be "SCxx" (case sensitive) where xx stands for a 2 digit ID |
58 | szSceneFolder: TAnsiFileName; // Foldername *must* be "SCxx" (case sensitive) where xx stands for a 2 digit ID |
42 | szDialogWav: TAnsiFileName; |
59 | szDialogWav: TAnsiFileName; |
43 | szDecisionBmp: TAnsiFileName; |
60 | szDecisionBmp: TAnsiFileName; |
44 | actions: array[0..2] of TActionDef; |
61 | actions: array[0..2] of TActionDef; |
45 | end; |
62 | end; |
46 | 63 | ||
47 | PPictureDef = ^TPictureDef; |
64 | PPictureDef = ^TPictureDef; |
48 | TPictureDef = packed record |
65 | TPictureDef = packed record |
49 | duration: Word; // deciseconds |
66 | duration: Word; // deciseconds |
50 | szBitmapFile: TAnsiFileName; |
67 | szBitmapFile: TAnsiFileName; |
51 | end; |
68 | end; |
52 | 69 | ||
53 | PGameBinFile = ^TGameBinFile; |
70 | PGameBinFile = ^TGameBinFile; |
54 | TGameBinFile = packed record |
71 | TGameBinFile = packed record |
55 | unknown1: array[0..6] of Word; |
72 | unknown1: array[0..6] of Word; |
56 | numScenes: Word; |
73 | numScenes: Word; |
57 | numPics: Word; |
74 | numPics: Word; |
58 | unknown2: array[0..1] of Word; |
75 | unknown2: array[0..1] of Word; |
59 | scenes: array[0..99] of TSceneDef; // Scenes start at 0x0016 |
76 | scenes: array[0..99] of TSceneDef; // Scenes start at 0x0016 |
60 | pictures: array[0..1999] of TPictureDef; // Pictures start at 0x2596 |
77 | pictures: array[0..1999] of TPictureDef; // Pictures start at 0x2596 |
61 | 78 | ||
62 | class function MaxSceneCount: integer; static; |
79 | class function MaxSceneCount: integer; static; |
63 | class function MaxPictureCount: integer; static; |
80 | class function MaxPictureCount: integer; static; |
64 | 81 | ||
65 | function AddSceneAtEnd(SceneID: integer): PSceneDef; |
82 | function AddSceneAtEnd(SceneID: integer): PSceneDef; |
66 | function FindScene(SceneID: integer): PSceneDef; |
83 | function FindScene(SceneID: integer): PSceneDef; |
67 | function FindSceneIndex(SceneID: integer): integer; |
84 | function FindSceneIndex(SceneID: integer): integer; |
68 | procedure DeleteScene(SceneIndex: integer); |
85 | procedure DeleteScene(SceneIndex: integer); |
69 | procedure SwapScene(IndexA, IndexB: integer); |
86 | procedure SwapScene(IndexA, IndexB: integer); |
70 | procedure DeletePicture(PictureIndex: integer); |
87 | procedure DeletePicture(PictureIndex: integer); |
71 | procedure SwapPicture(IndexA, IndexB: integer); |
88 | procedure SwapPicture(IndexA, IndexB: integer); |
72 | function AddPictureBetween(Index: integer; assignToUpperScene: boolean=true): PPictureDef; |
89 | function AddPictureBetween(Index: integer; assignToUpperScene: boolean=true): PPictureDef; |
73 | function RealPictureCount: integer; |
90 | function RealPictureCount: integer; |
74 | function RealActionCount: integer; |
91 | function RealActionCount: integer; |
75 | function Defrag(OnlyCheck: boolean=false): boolean; |
92 | function Defrag(OnlyCheck: boolean=false): boolean; |
76 | function BiggestUsedPictureIndex: integer; |
93 | function BiggestUsedPictureIndex: integer; |
77 | end; |
94 | end; |
78 | 95 | ||
79 | procedure _WriteStringToFilename(x: PAnsiFileName; s: AnsiString); |
96 | procedure _WriteStringToFilename(x: PAnsiFileName; s: AnsiString); |
80 | 97 | ||
81 | implementation |
98 | implementation |
82 | 99 | ||
83 | uses |
100 | uses |
84 | Windows, SysUtils, Math; |
101 | Windows, SysUtils, Math; |
85 | 102 | ||
86 | procedure _WriteStringToFilename(x: PAnsiFileName; s: AnsiString); |
103 | procedure _WriteStringToFilename(x: PAnsiFileName; s: AnsiString); |
87 | begin |
104 | begin |
88 | ZeroMemory(x, Length(x^)); |
105 | ZeroMemory(x, Length(x^)); |
89 | StrPLCopy(x^, s, Length(x^)); |
106 | StrPLCopy(x^, s, Length(x^)); |
90 | end; |
107 | end; |
91 | 108 | ||
92 | { TGameBinFile } |
109 | { TGameBinFile } |
93 | 110 | ||
94 | function TGameBinFile.BiggestUsedPictureIndex: integer; |
111 | function TGameBinFile.BiggestUsedPictureIndex: integer; |
95 | var |
112 | var |
96 | iScene, candidate: integer; |
113 | iScene, candidate: integer; |
97 | begin |
114 | begin |
98 | result := -1; |
115 | result := -1; |
99 | for iScene := 0 to Self.numScenes - 1 do |
116 | for iScene := 0 to Self.numScenes - 1 do |
100 | begin |
117 | begin |
101 | candidate := Self.scenes[iScene].pictureIndex + Self.scenes[iScene].numPics - 1; |
118 | candidate := Self.scenes[iScene].pictureIndex + Self.scenes[iScene].numPics - 1; |
102 | if candidate > result then result := candidate; |
119 | if candidate > result then result := candidate; |
103 | end; |
120 | end; |
104 | end; |
121 | end; |
105 | 122 | ||
106 | resourcestring |
123 | resourcestring |
107 | S_INVALID_SCENE_ID = 'Invalid scene ID'; |
124 | S_INVALID_SCENE_ID = 'Invalid scene ID'; |
108 | 125 | ||
109 | function TGameBinFile.AddSceneAtEnd(SceneID: integer): PSceneDef; |
126 | function TGameBinFile.AddSceneAtEnd(SceneID: integer): PSceneDef; |
110 | resourcestring |
127 | resourcestring |
111 | S_SCENE_FULL = 'Not enough space for another scene'; |
128 | S_SCENE_FULL = 'Not enough space for another scene'; |
112 | begin |
129 | begin |
113 | if Self.numScenes >= Length(Self.scenes) then raise Exception.Create(S_SCENE_FULL); |
130 | if Self.numScenes >= Length(Self.scenes) then raise Exception.Create(S_SCENE_FULL); |
114 | if SceneID = $7FFF {Terminate program} then raise Exception.Create(S_INVALID_SCENE_ID); |
131 | if SceneID = $7FFF {Terminate program} then raise Exception.Create(S_INVALID_SCENE_ID); |
115 | if SceneID = $FFFF {Previous decision} then raise Exception.Create(S_INVALID_SCENE_ID); |
132 | if SceneID = $FFFF {Previous decision} then raise Exception.Create(S_INVALID_SCENE_ID); |
116 | result := @Self.scenes[Self.numScenes]; |
133 | result := @Self.scenes[Self.numScenes]; |
117 | ZeroMemory(result, SizeOf(TSceneDef)); |
134 | ZeroMemory(result, SizeOf(TSceneDef)); |
118 | _WriteStringToFilename(@result.szSceneFolder, AnsiString(Format('SC%.2d', [sceneID]))); |
135 | _WriteStringToFilename(@result.szSceneFolder, AnsiString(Format('SC%.2d', [sceneID]))); |
119 | Inc(Self.numScenes); |
136 | Inc(Self.numScenes); |
120 | end; |
137 | end; |
121 | 138 | ||
122 | function TGameBinFile.FindSceneIndex(SceneID: integer): integer; |
139 | function TGameBinFile.FindSceneIndex(SceneID: integer): integer; |
123 | var |
140 | var |
124 | i: integer; |
141 | i: integer; |
125 | begin |
142 | begin |
126 | if SceneID = $7FFF {Terminate program} then raise Exception.Create(S_INVALID_SCENE_ID); |
143 | if SceneID = $7FFF {Terminate program} then raise Exception.Create(S_INVALID_SCENE_ID); |
127 | if SceneID = $FFFF {Previous decision} then raise Exception.Create(S_INVALID_SCENE_ID); |
144 | if SceneID = $FFFF {Previous decision} then raise Exception.Create(S_INVALID_SCENE_ID); |
128 | for i := 0 to Self.numScenes - 1 do |
145 | for i := 0 to Self.numScenes - 1 do |
129 | begin |
146 | begin |
130 | if Self.scenes[i].szSceneFolder = Format('SC%.2d', [SceneID]) then |
147 | if Self.scenes[i].szSceneFolder = Format('SC%.2d', [SceneID]) then |
131 | begin |
148 | begin |
132 | result := i; |
149 | result := i; |
133 | exit; |
150 | exit; |
134 | end; |
151 | end; |
135 | end; |
152 | end; |
136 | result := -1; |
153 | result := -1; |
137 | end; |
154 | end; |
138 | 155 | ||
139 | function TGameBinFile.FindScene(SceneID: integer): PSceneDef; |
156 | function TGameBinFile.FindScene(SceneID: integer): PSceneDef; |
140 | var |
157 | var |
141 | idx: integer; |
158 | idx: integer; |
142 | begin |
159 | begin |
143 | idx := FindSceneIndex(SceneID); |
160 | idx := FindSceneIndex(SceneID); |
144 | if idx >= 0 then |
161 | if idx >= 0 then |
145 | result := @Self.scenes[idx] |
162 | result := @Self.scenes[idx] |
146 | else |
163 | else |
147 | result := nil; |
164 | result := nil; |
148 | end; |
165 | end; |
149 | 166 | ||
150 | procedure TGameBinFile.DeleteScene(SceneIndex: integer); |
167 | procedure TGameBinFile.DeleteScene(SceneIndex: integer); |
151 | resourcestring |
168 | resourcestring |
152 | S_INVALID_SC_IDX = 'Invalid scene index'; |
169 | S_INVALID_SC_IDX = 'Invalid scene index'; |
153 | begin |
170 | begin |
154 | if ((SceneIndex < 0) or (SceneIndex >= Length(Self.scenes))) then raise Exception.Create(S_INVALID_SC_IDX); |
171 | if ((SceneIndex < 0) or (SceneIndex >= Length(Self.scenes))) then raise Exception.Create(S_INVALID_SC_IDX); |
155 | If SceneIndex < Length(Self.scenes)-1 then |
172 | If SceneIndex < Length(Self.scenes)-1 then |
156 | begin |
173 | begin |
157 | CopyMemory(@Self.scenes[SceneIndex], @Self.scenes[SceneIndex+1], (Length(Self.scenes)-SceneIndex-1)*SizeOf(TSceneDef)); |
174 | CopyMemory(@Self.scenes[SceneIndex], @Self.scenes[SceneIndex+1], (Length(Self.scenes)-SceneIndex-1)*SizeOf(TSceneDef)); |
158 | end; |
175 | end; |
159 | ZeroMemory(@Self.scenes[Length(Self.scenes)-1], SizeOf(TSceneDef)); |
176 | ZeroMemory(@Self.scenes[Length(Self.scenes)-1], SizeOf(TSceneDef)); |
160 | Dec(Self.numScenes); |
177 | Dec(Self.numScenes); |
161 | end; |
178 | end; |
162 | 179 | ||
163 | class function TGameBinFile.MaxPictureCount: integer; |
180 | class function TGameBinFile.MaxPictureCount: integer; |
164 | var |
181 | var |
165 | dummy: TGameBinFile; |
182 | dummy: TGameBinFile; |
166 | begin |
183 | begin |
167 | dummy.numScenes := 1; // avoid compiler give a warning |
184 | dummy.numScenes := 1; // avoid compiler give a warning |
168 | result := Length(dummy.pictures); |
185 | result := Length(dummy.pictures); |
169 | end; |
186 | end; |
170 | 187 | ||
171 | class function TGameBinFile.MaxSceneCount: integer; |
188 | class function TGameBinFile.MaxSceneCount: integer; |
172 | var |
189 | var |
173 | dummy: TGameBinFile; |
190 | dummy: TGameBinFile; |
174 | begin |
191 | begin |
175 | dummy.numScenes := 1; // avoid compiler give a warning |
192 | dummy.numScenes := 1; // avoid compiler give a warning |
176 | result := Length(dummy.scenes); |
193 | result := Length(dummy.scenes); |
177 | end; |
194 | end; |
178 | 195 | ||
179 | function TGameBinFile.RealActionCount: integer; |
196 | function TGameBinFile.RealActionCount: integer; |
180 | var |
197 | var |
181 | iScene: integer; |
198 | iScene: integer; |
182 | begin |
199 | begin |
183 | result := 0; |
200 | result := 0; |
184 | for iScene := 0 to Self.numScenes - 1 do |
201 | for iScene := 0 to Self.numScenes - 1 do |
185 | begin |
202 | begin |
186 | result := result + Self.scenes[iScene].numActions; |
203 | result := result + Self.scenes[iScene].numActions; |
187 | end; |
204 | end; |
188 | end; |
205 | end; |
189 | 206 | ||
190 | function TGameBinFile.RealPictureCount: integer; |
207 | function TGameBinFile.RealPictureCount: integer; |
191 | var |
208 | var |
192 | iScene: integer; |
209 | iScene: integer; |
193 | begin |
210 | begin |
194 | result := 0; |
211 | result := 0; |
195 | for iScene := 0 to Self.numScenes - 1 do |
212 | for iScene := 0 to Self.numScenes - 1 do |
196 | begin |
213 | begin |
197 | result := result + Self.scenes[iScene].numPics; |
214 | result := result + Self.scenes[iScene].numPics; |
198 | end; |
215 | end; |
199 | end; |
216 | end; |
200 | 217 | ||
201 | procedure TGameBinFile.SwapScene(IndexA, IndexB: integer); |
218 | procedure TGameBinFile.SwapScene(IndexA, IndexB: integer); |
202 | var |
219 | var |
203 | bakScene: TSceneDef; |
220 | bakScene: TSceneDef; |
204 | begin |
221 | begin |
205 | if IndexA = IndexB then exit; |
222 | if IndexA = IndexB then exit; |
206 | if ((Min(IndexA, IndexB) < 0) or (Max(IndexA, IndexB) >= Length(Self.scenes))) then raise Exception.Create('Invalid scene index'); |
223 | if ((Min(IndexA, IndexB) < 0) or (Max(IndexA, IndexB) >= Length(Self.scenes))) then raise Exception.Create('Invalid scene index'); |
207 | CopyMemory(@bakScene, @Self.scenes[IndexA], SizeOf(TSceneDef)); |
224 | CopyMemory(@bakScene, @Self.scenes[IndexA], SizeOf(TSceneDef)); |
208 | CopyMemory(@Self.scenes[IndexA], @Self.scenes[IndexB], SizeOf(TSceneDef)); |
225 | CopyMemory(@Self.scenes[IndexA], @Self.scenes[IndexB], SizeOf(TSceneDef)); |
209 | CopyMemory(@Self.scenes[IndexB], @bakScene, SizeOf(TSceneDef)); |
226 | CopyMemory(@Self.scenes[IndexB], @bakScene, SizeOf(TSceneDef)); |
210 | end; |
227 | end; |
211 | 228 | ||
212 | function TGameBinFile.Defrag(OnlyCheck: boolean=false): boolean; |
229 | function TGameBinFile.Defrag(OnlyCheck: boolean=false): boolean; |
213 | var |
230 | var |
214 | iPicture, iScene, iScene2: integer; |
231 | iPicture, iScene, iScene2: integer; |
215 | isGap: boolean; |
232 | isGap: boolean; |
216 | begin |
233 | begin |
217 | result := false; |
234 | result := false; |
218 | for iPicture := MaxPictureCount - 1 downto 0 do |
235 | for iPicture := MaxPictureCount - 1 downto 0 do |
219 | begin |
236 | begin |
220 | isGap := true; |
237 | isGap := true; |
221 | for iScene := 0 to Self.numScenes - 1 do |
238 | for iScene := 0 to Self.numScenes - 1 do |
222 | begin |
239 | begin |
223 | if (iPicture >= Self.scenes[iScene].pictureIndex) and |
240 | if (iPicture >= Self.scenes[iScene].pictureIndex) and |
224 | (iPicture <= Self.scenes[iScene].pictureIndex + Self.scenes[iScene].numPics - 1) then |
241 | (iPicture <= Self.scenes[iScene].pictureIndex + Self.scenes[iScene].numPics - 1) then |
225 | begin |
242 | begin |
226 | isGap := false; |
243 | isGap := false; |
227 | break; |
244 | break; |
228 | end; |
245 | end; |
229 | end; |
246 | end; |
230 | if isGap then |
247 | if isGap then |
231 | begin |
248 | begin |
232 | result := true; |
249 | result := true; |
233 | if not OnlyCheck then |
250 | if not OnlyCheck then |
234 | begin |
251 | begin |
235 | {$REGION 'Close the gap'} |
252 | {$REGION 'Close the gap'} |
236 | for iScene2 := 0 to Self.numScenes - 1 do |
253 | for iScene2 := 0 to Self.numScenes - 1 do |
237 | begin |
254 | begin |
238 | if (iPicture < Self.scenes[iScene2].pictureIndex) then |
255 | if (iPicture < Self.scenes[iScene2].pictureIndex) then |
239 | begin |
256 | begin |
240 | Dec(Self.scenes[iScene2].pictureIndex); |
257 | Dec(Self.scenes[iScene2].pictureIndex); |
241 | end; |
258 | end; |
242 | end; |
259 | end; |
243 | CopyMemory(@Self.pictures[iPicture], @Self.pictures[iPicture+1], (Length(Self.pictures)-iPicture-1)*SizeOf(TPictureDef)); |
260 | CopyMemory(@Self.pictures[iPicture], @Self.pictures[iPicture+1], (Length(Self.pictures)-iPicture-1)*SizeOf(TPictureDef)); |
244 | ZeroMemory(@Self.pictures[Length(Self.pictures)-1], SizeOf(TPictureDef)); |
261 | ZeroMemory(@Self.pictures[Length(Self.pictures)-1], SizeOf(TPictureDef)); |
245 | {$ENDREGION} |
262 | {$ENDREGION} |
246 | end; |
263 | end; |
247 | end; |
264 | end; |
248 | end; |
265 | end; |
249 | end; |
266 | end; |
250 | 267 | ||
251 | procedure TGameBinFile.DeletePicture(PictureIndex: integer); |
268 | procedure TGameBinFile.DeletePicture(PictureIndex: integer); |
252 | var |
269 | var |
253 | iScene, iScene2: integer; |
270 | iScene, iScene2: integer; |
254 | protection: integer; // prevents that two scenes get the same picture index when all pictures in a scene are deleted |
271 | protection: integer; // prevents that two scenes get the same picture index when all pictures in a scene are deleted |
255 | begin |
272 | begin |
256 | if (PictureIndex < 0) or (PictureIndex >= Length(Self.pictures)) then raise Exception.Create('Invalid picture index'); |
273 | if (PictureIndex < 0) or (PictureIndex >= Length(Self.pictures)) then raise Exception.Create('Invalid picture index'); |
257 | 274 | ||
258 | protection := 0; |
275 | protection := 0; |
259 | for iScene := 0 to Self.numScenes-1 do |
276 | for iScene := 0 to Self.numScenes-1 do |
260 | begin |
277 | begin |
261 | if (PictureIndex >= Self.scenes[iScene].pictureIndex) and |
278 | if (PictureIndex >= Self.scenes[iScene].pictureIndex) and |
262 | (PictureIndex <= Self.scenes[iScene].pictureIndex + Self.scenes[iScene].numPics - 1) then |
279 | (PictureIndex <= Self.scenes[iScene].pictureIndex + Self.scenes[iScene].numPics - 1) then |
263 | begin |
280 | begin |
264 | Dec(Self.scenes[iScene].numPics); |
281 | Dec(Self.scenes[iScene].numPics); |
265 | if Self.scenes[iScene].numPics = 0 then |
282 | if Self.scenes[iScene].numPics = 0 then |
266 | begin |
283 | begin |
267 | for iScene2 := 0 to Self.numScenes-1 do |
284 | for iScene2 := 0 to Self.numScenes-1 do |
268 | begin |
285 | begin |
269 | if Self.scenes[iScene2].pictureIndex = PictureIndex+1 then |
286 | if Self.scenes[iScene2].pictureIndex = PictureIndex+1 then |
270 | begin |
287 | begin |
271 | protection := 1; |
288 | protection := 1; |
272 | break; |
289 | break; |
273 | end; |
290 | end; |
274 | end; |
291 | end; |
275 | end; |
292 | end; |
276 | end |
293 | end |
277 | else if (PictureIndex+protection < Self.scenes[iScene].pictureIndex) then |
294 | else if (PictureIndex+protection < Self.scenes[iScene].pictureIndex) then |
278 | begin |
295 | begin |
279 | Dec(Self.scenes[iScene].pictureIndex); |
296 | Dec(Self.scenes[iScene].pictureIndex); |
280 | end; |
297 | end; |
281 | end; |
298 | end; |
282 | 299 | ||
283 | If (PictureIndex < Length(Self.pictures)-1) and (protection = 0) then |
300 | If (PictureIndex < Length(Self.pictures)-1) and (protection = 0) then |
284 | begin |
301 | begin |
285 | CopyMemory(@Self.pictures[PictureIndex], @Self.pictures[PictureIndex+1], (Length(Self.pictures)-PictureIndex-1)*SizeOf(TPictureDef)); |
302 | CopyMemory(@Self.pictures[PictureIndex], @Self.pictures[PictureIndex+1], (Length(Self.pictures)-PictureIndex-1)*SizeOf(TPictureDef)); |
286 | ZeroMemory(@Self.pictures[Length(Self.pictures)-1], SizeOf(TPictureDef)); |
303 | ZeroMemory(@Self.pictures[Length(Self.pictures)-1], SizeOf(TPictureDef)); |
287 | end; |
304 | end; |
288 | 305 | ||
289 | Dec(Self.numPics); |
306 | Dec(Self.numPics); |
290 | end; |
307 | end; |
291 | 308 | ||
292 | procedure TGameBinFile.SwapPicture(IndexA, IndexB: integer); |
309 | procedure TGameBinFile.SwapPicture(IndexA, IndexB: integer); |
293 | var |
310 | var |
294 | bakPicture: TPictureDef; |
311 | bakPicture: TPictureDef; |
295 | begin |
312 | begin |
296 | // QUE: should we forbid that a picture between "scene borders" are swapped? |
313 | // QUE: should we forbid that a picture between "scene borders" are swapped? |
297 | 314 | ||
298 | if IndexA = IndexB then exit; |
315 | if IndexA = IndexB then exit; |
299 | if ((Min(IndexA, IndexB) < 0) or (Max(IndexA, IndexB) >= Length(Self.pictures))) then raise Exception.Create('Invalid picture index'); |
316 | if ((Min(IndexA, IndexB) < 0) or (Max(IndexA, IndexB) >= Length(Self.pictures))) then raise Exception.Create('Invalid picture index'); |
300 | 317 | ||
301 | CopyMemory(@bakPicture, @Self.pictures[IndexA], SizeOf(TPictureDef)); |
318 | CopyMemory(@bakPicture, @Self.pictures[IndexA], SizeOf(TPictureDef)); |
302 | CopyMemory(@Self.pictures[IndexA], @Self.pictures[IndexB], SizeOf(TPictureDef)); |
319 | CopyMemory(@Self.pictures[IndexA], @Self.pictures[IndexB], SizeOf(TPictureDef)); |
303 | CopyMemory(@Self.pictures[IndexB], @bakPicture, SizeOf(TPictureDef)); |
320 | CopyMemory(@Self.pictures[IndexB], @bakPicture, SizeOf(TPictureDef)); |
304 | end; |
321 | end; |
305 | 322 | ||
306 | function TGameBinFile.AddPictureBetween(Index: integer; assignToUpperScene: boolean=true): PPictureDef; |
323 | function TGameBinFile.AddPictureBetween(Index: integer; assignToUpperScene: boolean=true): PPictureDef; |
307 | 324 | ||
308 | function _HasBuffer(Index: integer): boolean; |
325 | function _HasBuffer(Index: integer): boolean; |
309 | var |
326 | var |
310 | iScene: integer; |
327 | iScene: integer; |
311 | begin |
328 | begin |
312 | for iScene := 0 to Self.numScenes-1 do |
329 | for iScene := 0 to Self.numScenes-1 do |
313 | begin |
330 | begin |
314 | if Self.scenes[iScene].pictureIndex = Index+1 then |
331 | if Self.scenes[iScene].pictureIndex = Index+1 then |
315 | begin |
332 | begin |
316 | result := true; |
333 | result := true; |
317 | exit; |
334 | exit; |
318 | end; |
335 | end; |
319 | end; |
336 | end; |
320 | result := false; |
337 | result := false; |
321 | end; |
338 | end; |
322 | 339 | ||
323 | resourcestring |
340 | resourcestring |
324 | S_INVALID_PIC_IDX = 'Invalid picture index'; |
341 | S_INVALID_PIC_IDX = 'Invalid picture index'; |
325 | S_PIC_FULL_DEFRAG = 'Not enough space for another picture. Please defrag to fill the gaps first.'; |
342 | S_PIC_FULL_DEFRAG = 'Not enough space for another picture. Please defrag to fill the gaps first.'; |
326 | S_PIC_FULL = 'Not enough space for another picture. Maximum limit reached.'; |
343 | S_PIC_FULL = 'Not enough space for another picture. Maximum limit reached.'; |
327 | var |
344 | var |
328 | iScene: integer; |
345 | iScene: integer; |
329 | begin |
346 | begin |
330 | if ((Index < 0) or (Index >= Length(Self.pictures))) then raise Exception.Create(S_INVALID_PIC_IDX); |
347 | if ((Index < 0) or (Index >= Length(Self.pictures))) then raise Exception.Create(S_INVALID_PIC_IDX); |
331 | 348 | ||
332 | if (BiggestUsedPictureIndex = MaxPictureCount-1) and Defrag(true) then |
349 | if (BiggestUsedPictureIndex = MaxPictureCount-1) and Defrag(true) then |
333 | raise Exception.Create(S_PIC_FULL_DEFRAG); |
350 | raise Exception.Create(S_PIC_FULL_DEFRAG); |
334 | 351 | ||
335 | if Self.numPics >= MaxPictureCount then |
352 | if Self.numPics >= MaxPictureCount then |
336 | raise Exception.Create(S_PIC_FULL); |
353 | raise Exception.Create(S_PIC_FULL); |
337 | 354 | ||
338 | if assignToUpperScene then |
355 | if assignToUpperScene then |
339 | begin |
356 | begin |
340 | // Sc1 Sc2 Sc1 Sc2 |
357 | // Sc1 Sc2 Sc1 Sc2 |
341 | // A B | C ==> A B | X C |
358 | // A B | C ==> A B | X C |
342 | // ^ |
359 | // ^ |
343 | for iScene := 0 to Self.numScenes-1 do |
360 | for iScene := 0 to Self.numScenes-1 do |
344 | begin |
361 | begin |
345 | if (Index >= Self.scenes[iScene].pictureIndex) and |
362 | if (Index >= Self.scenes[iScene].pictureIndex) and |
346 | (index <= Self.scenes[iScene].pictureIndex + Max(0,Self.scenes[iScene].numPics - 1)) then |
363 | (index <= Self.scenes[iScene].pictureIndex + Max(0,Self.scenes[iScene].numPics - 1)) then |
347 | begin |
364 | begin |
348 | Inc(Self.scenes[iScene].numPics); |
365 | Inc(Self.scenes[iScene].numPics); |
349 | end |
366 | end |
350 | else if (index < Self.scenes[iScene].pictureIndex) and not _HasBuffer(index) then |
367 | else if (index < Self.scenes[iScene].pictureIndex) and not _HasBuffer(index) then |
351 | begin |
368 | begin |
352 | Inc(Self.scenes[iScene].pictureIndex); |
369 | Inc(Self.scenes[iScene].pictureIndex); |
353 | end; |
370 | end; |
354 | end; |
371 | end; |
355 | end |
372 | end |
356 | else |
373 | else |
357 | begin |
374 | begin |
358 | // Sc1 Sc2 Sc1 Sc2 |
375 | // Sc1 Sc2 Sc1 Sc2 |
359 | // A B | C ==> A B X | C |
376 | // A B | C ==> A B X | C |
360 | // ^ |
377 | // ^ |
361 | for iScene := 0 to Self.numScenes-1 do |
378 | for iScene := 0 to Self.numScenes-1 do |
362 | begin |
379 | begin |
363 | if (Index >= 1 + Self.scenes[iScene].pictureIndex) and |
380 | if (Index >= 1 + Self.scenes[iScene].pictureIndex) and |
364 | (index <= 1 + Self.scenes[iScene].pictureIndex + Max(0,Self.scenes[iScene].numPics-1)) then |
381 | (index <= 1 + Self.scenes[iScene].pictureIndex + Max(0,Self.scenes[iScene].numPics-1)) then |
365 | begin |
382 | begin |
366 | Inc(Self.scenes[iScene].numPics); |
383 | Inc(Self.scenes[iScene].numPics); |
367 | end |
384 | end |
368 | else if (index <= Self.scenes[iScene].pictureIndex) and not _HasBuffer(index) then |
385 | else if (index <= Self.scenes[iScene].pictureIndex) and not _HasBuffer(index) then |
369 | begin |
386 | begin |
370 | Inc(Self.scenes[iScene].pictureIndex); |
387 | Inc(Self.scenes[iScene].pictureIndex); |
371 | end; |
388 | end; |
372 | end; |
389 | end; |
373 | end; |
390 | end; |
374 | 391 | ||
375 | result := @Self.pictures[Index]; |
392 | result := @Self.pictures[Index]; |
376 | if not _HasBuffer(index) then |
393 | if not _HasBuffer(index) then |
377 | begin |
394 | begin |
378 | CopyMemory(@Self.pictures[Index+1], result, (Length(Self.pictures)-Index-1)*SizeOf(TPictureDef)); |
395 | CopyMemory(@Self.pictures[Index+1], result, (Length(Self.pictures)-Index-1)*SizeOf(TPictureDef)); |
379 | end; |
396 | end; |
380 | 397 | ||
381 | ZeroMemory(result, SizeOf(TPictureDef)); |
398 | ZeroMemory(result, SizeOf(TPictureDef)); |
382 | 399 | ||
383 | Inc(Self.numPics); |
400 | Inc(Self.numPics); |
384 | end; |
401 | end; |
385 | 402 | ||
386 | end. |
403 | end. |
387 | 404 |