Subversion Repositories plumbers

Rev

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