Subversion Repositories plumbers

Rev

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

  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
  57.  
  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);
  63.     function AddPictureBetween(Index: integer; assignToUpperScene: boolean=true): PPictureDef;
  64.     function RealPictureCount: integer;
  65.     function RealActionCount: integer;
  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));
  99.   Dec(Self.numScenes);
  100. end;
  101.  
  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.  
  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
  137.   iScene, iScene2: integer;
  138.   protection: integer; // prevents that two scenes get the same picture index when all pictures in a scene are deleted
  139. begin
  140.   if (PictureIndex < 0) or (PictureIndex >= Length(Self.pictures)) then raise Exception.Create('Invalid picture index');
  141.  
  142.   protection := 0;
  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);
  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;
  160.     end
  161.     else if (PictureIndex+protection < Self.scenes[iScene].pictureIndex) then
  162.     begin
  163.       Dec(Self.scenes[iScene].pictureIndex);
  164.     end;
  165.   end;
  166.  
  167.   If (PictureIndex+protection < Length(Self.pictures)-1) and (protection = 0) then
  168.   begin
  169.     CopyMemory(@Self.pictures[PictureIndex+protection], @Self.pictures[PictureIndex+protection+1], (Length(Self.pictures)-PictureIndex+protection-1)*SizeOf(TPictureDef));
  170.   end;
  171.   ZeroMemory(@Self.pictures[Length(Self.pictures)-1], SizeOf(TPictureDef));
  172.  
  173.   Dec(Self.numPics);
  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.  
  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.  
  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.  
  213.   if assignToUpperScene then
  214.   begin
  215.     // Sc1   Sc2       Sc1   Sc2
  216.     // A B | C    ==>  A B | X C
  217.     //       ^
  218.     for iScene := 0 to Self.numScenes-1 do
  219.     begin
  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
  237.     begin
  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;
  247.     end;
  248.   end;
  249.  
  250.   result := @Self.pictures[Index];
  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.    
  256.   ZeroMemory(result, SizeOf(TPictureDef));
  257.  
  258.   Inc(Self.numPics);
  259. end;
  260.  
  261. end.
  262.