Subversion Repositories plumbers

Rev

Rev 2 | Rev 11 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 1
unit GameBinStruct;
2
 
3
{$A-}
4
 
5
interface
6
 
7
const
8
  SCENEID_PREVDECISION = -1;
9
  SCENEID_ENDGAME      = 32767;
10
 
11
type
12
  PCoord = ^TCoord;
13
  TCoord = packed record
14
    x: Word;
15
    y: Word;
16
  end;
17
 
18
  PActionDef = ^TActionDef;
19
  TActionDef = packed record
20
    scoreDelta: Integer;
21
    nextSceneID: SmallInt;  // will jump to the scene with the name "SC<nextSceneID>"
22
                            // 7FFF (32767) = end game
23
                            // FFFF (   -1) = go back to the last decision
24
    sceneSegment: SmallInt; // 0 = scene from beginning, 1 = decision page
25
    cHotspotTopLeft: TCoord;
26
    cHotspotBottomRight: TCoord;
27
  end;
28
 
29
  PAnsiFileName = ^TAnsiFileName;
30
  TAnsiFileName = array[0..13] of AnsiChar;
31
 
32
  PSceneDef = ^TSceneDef;
33
  TSceneDef = packed record
34
    numPics: Word;
35
    pictureIndex: Word;
36
    numActions: Word;
37
    szSceneFolder: TAnsiFileName; // Foldername *must* be "SCxx" (case sensitive) where xx stands for a 2 digit ID
38
    szDialogWav:   TAnsiFileName;
39
    szDecisionBmp: TAnsiFileName;
40
    actions: array[0..2] of TActionDef;
41
  end;
42
 
43
  PPictureDef = ^TPictureDef;
44
  TPictureDef = packed record
45
    duration: Word; // deciseconds
46
    szBitmapFile: TAnsiFileName;
47
  end;
48
 
49
  PGameBinFile = ^TGameBinFile;
50
  TGameBinFile = packed record
51
    unknown1: array[0..6] of Word;
52
    numScenes: Word;
53
    numPics: Word;
54
    unknown2: array[0..1] of Word;
55
    scenes: array[0..99] of TSceneDef;        // Scenes start at 0x0016
56
    pictures: array[0..1999] of TPictureDef;  // Pictures start at 0x2596
10 daniel-mar 57
 
2 daniel-mar 58
    function AddSceneAtEnd(SceneID: integer): PSceneDef;
59
    procedure DeleteScene(SceneIndex: integer);
60
    procedure SwapScene(IndexA, IndexB: integer);
61
    procedure DeletePicture(PictureIndex: integer);
62
    procedure SwapPicture(IndexA, IndexB: integer);
10 daniel-mar 63
    function AddPictureBetween(Index: integer; assignToUpperScene: boolean=true): PPictureDef;
64
    function RealPictureCount: integer;
65
    function RealActionCount: integer;
2 daniel-mar 66
  end;
67
 
68
procedure _WriteStringToFilename(x: PAnsiFileName; s: AnsiString);
69
 
70
implementation
71
 
72
uses
73
  Windows, SysUtils, Math;
74
 
75
procedure _WriteStringToFilename(x: PAnsiFileName; s: AnsiString);
76
begin
77
  ZeroMemory(x, Length(x^));
78
  StrPLCopy(x^, s, Length(x^));
79
end;
80
 
81
function TGameBinFile.AddSceneAtEnd(SceneID: integer): PSceneDef;
82
begin
83
  if Self.numScenes >= Length(Self.scenes) then raise Exception.Create('No more space for another scene');
84
  if sceneID >= Length(Self.scenes) then raise Exception.Create('SceneID is too large.');
85
  result := @Self.scenes[Self.numScenes];
86
  ZeroMemory(result, SizeOf(TSceneDef));
87
  _WriteStringToFilename(@result.szSceneFolder, AnsiString(Format('SC%.2d', [sceneID])));
88
  Inc(Self.numScenes);
89
end;
90
 
91
procedure TGameBinFile.DeleteScene(SceneIndex: integer);
92
begin
93
  if ((SceneIndex < 0) or (SceneIndex >= Length(Self.scenes))) then raise Exception.Create('Invalid scene index');
94
  If SceneIndex < Length(Self.scenes)-1 then
95
  begin
96
    CopyMemory(@Self.scenes[SceneIndex], @Self.scenes[SceneIndex+1], (Length(Self.scenes)-SceneIndex-1)*SizeOf(TSceneDef));
97
  end;
98
  ZeroMemory(@Self.scenes[Length(Self.scenes)-1], SizeOf(TSceneDef));
10 daniel-mar 99
  Dec(Self.numScenes);
2 daniel-mar 100
end;
101
 
10 daniel-mar 102
function TGameBinFile.RealActionCount: integer;
103
var
104
  iScene: integer;
105
begin
106
  result := 0;
107
  for iScene := 0 to Self.numScenes - 1 do
108
  begin
109
    result := result + Self.scenes[iScene].numActions;
110
  end;
111
end;
112
 
113
function TGameBinFile.RealPictureCount: integer;
114
var
115
  iScene: integer;
116
begin
117
  result := 0;
118
  for iScene := 0 to Self.numScenes - 1 do
119
  begin
120
    result := result + Self.scenes[iScene].numPics;
121
  end;
122
end;
123
 
2 daniel-mar 124
procedure TGameBinFile.SwapScene(IndexA, IndexB: integer);
125
var
126
  bakScene: TSceneDef;
127
begin
128
  if IndexA = IndexB then exit;
129
  if ((Min(IndexA, IndexB) < 0) or (Max(IndexA, IndexB) >= Length(Self.scenes))) then raise Exception.Create('Invalid scene index');
130
  CopyMemory(@bakScene, @Self.scenes[IndexA], SizeOf(TSceneDef));
131
  CopyMemory(@Self.scenes[IndexA], @Self.scenes[IndexB], SizeOf(TSceneDef));
132
  CopyMemory(@Self.scenes[IndexB], @bakScene, SizeOf(TSceneDef));
133
end;
134
 
135
procedure TGameBinFile.DeletePicture(PictureIndex: integer);
136
var
10 daniel-mar 137
  iScene, iScene2: integer;
138
  protection: integer; // prevents that two scenes get the same picture index when all pictures in a scene are deleted
2 daniel-mar 139
begin
140
  if (PictureIndex < 0) or (PictureIndex >= Length(Self.pictures)) then raise Exception.Create('Invalid picture index');
141
 
10 daniel-mar 142
  protection := 0;
2 daniel-mar 143
  for iScene := 0 to Self.numScenes-1 do
144
  begin
145
    if (PictureIndex >= Self.scenes[iScene].pictureIndex) and
146
       (PictureIndex <= Self.scenes[iScene].pictureIndex + Self.scenes[iScene].numPics - 1) then
147
    begin
148
      Dec(Self.scenes[iScene].numPics);
10 daniel-mar 149
      if Self.scenes[iScene].numPics = 0 then
150
      begin
151
         for iScene2 := 0 to Self.numScenes-1 do
152
         begin
153
           if Self.scenes[iScene2].pictureIndex = PictureIndex+1 then
154
           begin
155
             protection := 1;
156
             break;
157
           end;
158
         end;
159
      end;
2 daniel-mar 160
    end
10 daniel-mar 161
    else if (PictureIndex+protection < Self.scenes[iScene].pictureIndex) then
2 daniel-mar 162
    begin
163
      Dec(Self.scenes[iScene].pictureIndex);
164
    end;
165
  end;
166
 
10 daniel-mar 167
  If (PictureIndex+protection < Length(Self.pictures)-1) and (protection = 0) then
2 daniel-mar 168
  begin
10 daniel-mar 169
    CopyMemory(@Self.pictures[PictureIndex+protection], @Self.pictures[PictureIndex+protection+1], (Length(Self.pictures)-PictureIndex+protection-1)*SizeOf(TPictureDef));
2 daniel-mar 170
  end;
171
  ZeroMemory(@Self.pictures[Length(Self.pictures)-1], SizeOf(TPictureDef));
10 daniel-mar 172
 
173
  Dec(Self.numPics);
2 daniel-mar 174
end;
175
 
176
procedure TGameBinFile.SwapPicture(IndexA, IndexB: integer);
177
var
178
  bakPicture: TPictureDef;
179
begin
180
  // QUE: should we forbid that a picture between "scene borders" are swapped?
181
 
182
  if IndexA = IndexB then exit;
183
  if ((Min(IndexA, IndexB) < 0) or (Max(IndexA, IndexB) >= Length(Self.pictures))) then raise Exception.Create('Invalid picture index');
184
 
185
  CopyMemory(@bakPicture, @Self.pictures[IndexA], SizeOf(TPictureDef));
186
  CopyMemory(@Self.pictures[IndexA], @Self.pictures[IndexB], SizeOf(TPictureDef));
187
  CopyMemory(@Self.pictures[IndexB], @bakPicture, SizeOf(TPictureDef));
188
end;
189
 
10 daniel-mar 190
function TGameBinFile.AddPictureBetween(Index: integer; assignToUpperScene: boolean=true): PPictureDef;
191
 
192
  function _HasBuffer(Index: integer): boolean;
193
  var
194
    iScene: integer;
195
  begin
196
    for iScene := 0 to Self.numScenes-1 do
197
    begin
198
      if Self.scenes[iScene].pictureIndex = Index+1 then
199
      begin
200
        result := true;
201
        exit;
202
      end;
203
    end;
204
    result := false;
205
  end;
206
 
2 daniel-mar 207
var
208
  iScene: integer;
209
begin
210
  if Self.numPics >= Length(Self.pictures) then raise Exception.Create('No more space for another picture');
211
  if ((Index < 0) or (Index >= Length(Self.pictures))) then raise Exception.Create('Invalid picture index');
212
 
10 daniel-mar 213
  if assignToUpperScene then
2 daniel-mar 214
  begin
10 daniel-mar 215
    // Sc1   Sc2       Sc1   Sc2
216
    // A B | C    ==>  A B | X C
217
    //       ^
218
    for iScene := 0 to Self.numScenes-1 do
2 daniel-mar 219
    begin
10 daniel-mar 220
      if (Index >= Self.scenes[iScene].pictureIndex) and
221
         (index <= Self.scenes[iScene].pictureIndex + Max(0,Self.scenes[iScene].numPics - 1)) then
222
      begin
223
        Inc(Self.scenes[iScene].numPics);
224
      end
225
      else if (index < Self.scenes[iScene].pictureIndex) and not _HasBuffer(index) then
226
      begin
227
        Inc(Self.scenes[iScene].pictureIndex);
228
      end;
229
    end;
230
  end
231
  else
232
  begin
233
    // Sc1   Sc2       Sc1     Sc2
234
    // A B | C    ==>  A B X | C
235
    //       ^
236
    for iScene := 0 to Self.numScenes-1 do
2 daniel-mar 237
    begin
10 daniel-mar 238
      if (Index >= 1 + Self.scenes[iScene].pictureIndex) and
239
         (index <= 1 + Self.scenes[iScene].pictureIndex + Max(0,Self.scenes[iScene].numPics-1)) then
240
      begin
241
        Inc(Self.scenes[iScene].numPics);
242
      end
243
      else if (index <= Self.scenes[iScene].pictureIndex) and not _HasBuffer(index) then
244
      begin
245
        Inc(Self.scenes[iScene].pictureIndex);
246
      end;
2 daniel-mar 247
    end;
248
  end;
249
 
250
  result := @Self.pictures[Index];
10 daniel-mar 251
  if not _HasBuffer(index) then
252
  begin
253
    CopyMemory(@Self.pictures[Index+1], result, (Length(Self.pictures)-Index-1)*SizeOf(TPictureDef));
254
  end;
255
 
2 daniel-mar 256
  ZeroMemory(result, SizeOf(TPictureDef));
10 daniel-mar 257
 
258
  Inc(Self.numPics);
2 daniel-mar 259
end;
260
 
261
end.