Subversion Repositories spacemission

Rev

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

Rev Author Line No. Line
14 daniel-mar 1
unit ComLevelReader;
2
 
3
interface
4
 
40 daniel-mar 5
uses
6
  Classes;
7
 
27 daniel-mar 8
const
9
  NumEnemyTypes = 7;
10
 
14 daniel-mar 11
type
12
  TEnemyType = (
13
    etUnknown,
14
    etEnemyAttacker,
15
    etEnemyAttacker2,
16
    etEnemyAttacker3,
17
    etEnemyMeteor,
18
    etEnemyUFO,
19
    etEnemyUFO2,
20
    etEnemyBoss
21
  );
22
 
23
  TEnemyAdvent = record
24
    enemyType: TEnemyType;
25
    x: integer;
26
    y: integer;
27
    lifes: integer;
28
  end;
29
 
40 daniel-mar 30
  TLevelData = class(TPersistent)
27 daniel-mar 31
  strict private
32
    procedure SortEnemies;
40 daniel-mar 33
  strict protected
34
    procedure AssignTo(Dest: TPersistent); override;
14 daniel-mar 35
  public
40 daniel-mar 36
    RasterErzwingen: boolean;
14 daniel-mar 37
    LevelEditorLength: integer;
42 daniel-mar 38
    LevelName: string;
39
    LevelAuthor: string;
14 daniel-mar 40
    EnemyAdventTable: array of TEnemyAdvent;
27 daniel-mar 41
    function IndexOfEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer): integer;
42
    procedure AddEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer);
43
    procedure DeleteEnemy(i: integer); overload;
44
    procedure DeleteEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer); overload;
45
    function CountEnemies: integer;
46
    function HasBoss: boolean;
14 daniel-mar 47
    procedure Clear;
40 daniel-mar 48
    procedure LoadFromStrings(sl: TStrings); // version 0.3 - version 1.2 files
49
    procedure LoadFromFile(filename: string); // version 0.2 - version 1.2 files
50
    procedure SaveToStrings(sl: TStrings);
51
    procedure SaveToFile(filename: string);
52
    destructor Destroy; override;
14 daniel-mar 53
  end;
54
 
40 daniel-mar 55
  TGameMode = (gmUnknown, gmLevels, gmRandom);
56
 
57
  TSaveData = class(TPersistent)
58
  strict protected
59
    procedure AssignTo(Dest: TPersistent); override;
60
  public
61
    Score: integer;
62
    Life: integer;
63
    Level: integer;
64
    GameMode: TGameMode;
65
    LevelData: TLevelData;
66
    procedure Clear;
67
    procedure LoadFromStrings(sl: TStrings);
68
    procedure LoadFromFile(filename: string);
69
    procedure SaveToStrings(sl: TStrings);
70
    procedure SaveToFile(filename: string);
71
    destructor Destroy; override;
72
  end;
73
 
51 daniel-mar 74
function GetLevelFileName(lev: integer; forceuserdir: boolean): string;
17 daniel-mar 75
 
14 daniel-mar 76
implementation
77
 
78
uses
52 daniel-mar 79
  SysUtils, StrUtils, Global, Windows, System.Types;
14 daniel-mar 80
 
52 daniel-mar 81
const
82
  // { iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) 37476 products(2) spacemission(8) file-format(1) lev-sav-v12(1) }
83
  // https://hosted.oidplus.com/viathinksoft/?goto=oid%3A1.3.6.1.4.1.37476.2.8.1.1
84
  OID_LEVSAV_VER12 = '1.3.6.1.4.1.37476.2.8.1.1';
85
 
51 daniel-mar 86
function GetLevelFileName(lev: integer; forceuserdir: boolean): string;
27 daniel-mar 87
 
51 daniel-mar 88
  function _GetLevelVerzeichnisSystem: string;
89
  begin
90
    // Für die Auslieferungs-Levels
91
    result := OwnDirectory + 'Levels';
92
  end;
93
 
94
  function _GetLevelVerzeichnisUser: string;
95
  begin
96
    try
97
      result := GetKnownFolderPath(FOLDERID_SavedGames);
98
    except
99
      result := '';
100
    end;
101
    if result = '' then
102
    begin
103
      // Pre Vista
104
      result := OwnDirectory + 'Levels';
105
    end
106
    else
107
    begin
108
      result := IncludeTrailingPathDelimiter(result);
109
      result := result + 'SpaceMission';
110
    end;
111
    result := IncludeTrailingPathDelimiter(result);
112
    ForceDirectories(result);
113
  end;
114
 
115
  function _GetLevelFileNameUser(lev: integer): string;
116
  var
117
    old, new: string;
118
  begin
119
    new := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisUser)+'Level '+inttostr(lev)+'.lev'; // Version 0.3+ Level Files
120
    old := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisUser)+'Lev'+inttostr(lev)+'A1.lev'; // Version 0.2 Level Files
121
    if fileexists(new) then exit(new);
122
    if fileexists(old) then exit(old);
123
    exit(new);
124
  end;
125
 
126
  function _GetLevelFileNameSystem(lev: integer): string;
127
  var
128
    old, new: string;
129
  begin
130
    new := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisSystem)+'Level '+inttostr(lev)+'.lev'; // Version 0.3+ Level Files
131
    old := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisSystem)+'Lev'+inttostr(lev)+'A1.lev'; // Version 0.2 Level Files
132
    if fileexists(new) then exit(new);
133
    if fileexists(old) then exit(old);
134
    exit(new);
135
  end;
136
 
137
var
138
  usr, sys: string;
17 daniel-mar 139
begin
51 daniel-mar 140
  usr := _GetLevelFileNameUser(lev);
141
  sys := _GetLevelFileNameSystem(lev);
142
  if fileexists(usr) or forceuserdir then exit(usr);
143
  if fileexists(sys) then exit(sys);
144
  exit(usr);
17 daniel-mar 145
end;
146
 
52 daniel-mar 147
// this is just an example, there are many
148
// different ways you can implement this
149
// more efficiently, ie using a TStringBuilder,
150
// or even modifying the String in-place...
151
function CollapseSpaces(const S: string): string;
152
var
153
  P: PChar;
154
  AddSpace: Boolean;
155
begin
156
  Result := '';
157
  AddSpace := False;
158
  P := PChar(S);
159
  while P^ <> #0 do
160
  begin
161
    while CharInSet(P^, [#1..' ']) do Inc(P);
162
    if P^ = #0 then Exit;
163
    if AddSpace then
164
      Result := Result + ' '
165
    else
166
      AddSpace := True;
167
    repeat
168
      Result := Result + P^;
169
      Inc(P);
170
    until P^ <= ' ';
171
  end;
172
end;
173
 
14 daniel-mar 174
{ TLevelData }
175
 
40 daniel-mar 176
procedure TLevelData.AssignTo(Dest: TPersistent);
177
var
178
  DestLevelData: TLevelData;
179
  i: integer;
180
begin
181
  DestLevelData := Dest as TLevelData;
182
  if Assigned(DestLevelData) then
183
  begin
184
    DestLevelData.RasterErzwingen := Self.RasterErzwingen;
185
    DestLevelData.LevelEditorLength := Self.LevelEditorLength;
42 daniel-mar 186
    DestLevelData.LevelName := Self.LevelName;
187
    DestLevelData.LevelAuthor := Self.LevelAuthor;
40 daniel-mar 188
    SetLength(DestLevelData.EnemyAdventTable, Length(Self.EnemyAdventTable));
189
    for i := 0 to Length(Self.EnemyAdventTable) do
190
    begin
191
      DestLevelData.EnemyAdventTable[i] := Self.EnemyAdventTable[i];
192
    end;
193
  end
194
  else
195
  begin
196
    inherited;
197
  end;
198
end;
199
 
14 daniel-mar 200
procedure TLevelData.Clear;
201
begin
202
  SetLength(EnemyAdventTable, 0);
27 daniel-mar 203
  LevelEditorLength := DefaultLevelLength;
42 daniel-mar 204
  LevelName := '';
205
  LevelAuthor := '';
14 daniel-mar 206
end;
207
 
27 daniel-mar 208
function TLevelData.CountEnemies: integer;
209
begin
210
  result := Length(EnemyAdventTable);
211
end;
212
 
213
procedure TLevelData.DeleteEnemy(i: integer);
214
var
215
  j: integer;
216
begin
217
  for j := i+1 to CountEnemies-1 do
218
  begin
219
    EnemyAdventTable[j-1] := EnemyAdventTable[j];
220
  end;
221
  SetLength(EnemyAdventTable, Length(EnemyAdventTable)-1);
222
end;
223
 
224
procedure TLevelData.DeleteEnemy(x, y: integer; enemyType: TEnemyType;
225
  lifes: integer);
226
begin
227
  DeleteEnemy(IndexOfEnemy(x, y, enemyType, lifes));
228
end;
229
 
40 daniel-mar 230
destructor TLevelData.Destroy;
231
begin
232
  Clear;
233
  inherited;
234
end;
235
 
27 daniel-mar 236
function TLevelData.HasBoss: boolean;
237
var
238
  i: integer;
239
begin
240
  for i := 0 to Length(EnemyAdventTable) - 1 do
241
  begin
242
    if EnemyAdventTable[i].enemyType = etEnemyBoss then
243
    begin
244
      result := true;
245
      exit;
246
    end;
247
  end;
248
  result := false;
249
end;
250
 
251
procedure TLevelData.AddEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer);
252
begin
253
  SetLength(EnemyAdventTable, Length(EnemyAdventTable)+1);
28 daniel-mar 254
 
255
  if enemyType = etEnemyMeteor then lifes := 0;
40 daniel-mar 256
  if RasterErzwingen then
257
  begin
258
    if x mod RasterW <> 0 then raise Exception.CreateFmt('X-Koordinate muss ohne Rest durch %d teilbar sein', [RasterW]);
259
    if y mod RasterH <> 0 then raise Exception.CreateFmt('Y-Koordinate muss ohne Rest durch %d teilbar sein', [RasterH]);
260
  end;
261
  if lifes > MaxPossibleEnemyLives then lifes := MaxPossibleEnemyLives;
28 daniel-mar 262
 
27 daniel-mar 263
  EnemyAdventTable[Length(EnemyAdventTable)-1].x         := x;
264
  EnemyAdventTable[Length(EnemyAdventTable)-1].y         := y;
265
  EnemyAdventTable[Length(EnemyAdventTable)-1].enemyType := enemyType;
266
  EnemyAdventTable[Length(EnemyAdventTable)-1].lifes     := lifes;
267
end;
268
 
269
function TLevelData.IndexOfEnemy(x, y: integer; enemyType: TEnemyType;
270
  lifes: integer): integer;
271
var
272
  i: integer;
273
begin
274
  for i := 0 to Length(EnemyAdventTable) - 1 do
275
  begin
276
    if (EnemyAdventTable[i].x = x) and
277
       (EnemyAdventTable[i].y = y) and
278
       (EnemyAdventTable[i].enemyType = enemyType) and
279
       (EnemyAdventTable[i].lifes = lifes) then
280
    begin
281
      result := i;
282
      exit;
283
    end;
284
  end;
285
  result := -1;
286
end;
287
 
40 daniel-mar 288
procedure TLevelData.LoadFromStrings(sl: TStrings);
14 daniel-mar 289
var
290
  curline: integer;
17 daniel-mar 291
  z, act: integer;
40 daniel-mar 292
  sl2: TStringList;
27 daniel-mar 293
  tmpX, tmpY, tmpLifes: integer;
294
  tmpEnemy: TEnemyType;
40 daniel-mar 295
  ergebnis: string;
52 daniel-mar 296
  ary: TStringDynArray;
297
  sLine: string;
14 daniel-mar 298
begin
27 daniel-mar 299
  Clear;
300
 
42 daniel-mar 301
  LevelEditorLength := DefaultLevelLength;
302
  LevelName := '';
303
  LevelAuthor := '';
304
 
52 daniel-mar 305
  if sl.Strings[0] = '; SpaceMission 0.3' then // do not localize
40 daniel-mar 306
  begin
307
    {$REGION 'Backwards compatibility level format 0.3 (convert to 0.4)'}
52 daniel-mar 308
    sl.Strings[0] := '; SpaceMission 0.4'; // do not localize
309
    sl.Insert(1, '; LEV-File'); // do not localize
40 daniel-mar 310
    {$ENDREGION}
311
  end;
312
 
52 daniel-mar 313
  if (sl.Strings[0] = '; SpaceMission 0.4') and // do not localize
314
     (sl.Strings[1] = '; LEV-File') then // do not localize
40 daniel-mar 315
  begin
316
    {$REGION 'Backwards compatibility level format 0.4 (convert to 1.0)'}
317
    sl2 := TStringList.Create;
318
    try
319
      z := 0;
320
      act := 0;
321
      while z < sl.Count do
322
      begin
323
        inc(z);
324
        if z > 2 then inc(act);
325
        if act = 5 then act := 1;
326
        ergebnis := sl.Strings[z-1];
327
        if ergebnis = '; SpaceMission 0.4' then
328
          sl2.Add('; SpaceMission 1.0')
329
        else
330
        begin
331
          if (ergebnis = '30000') and (z = 3) then
332
            sl2.Add(IntTostr(DefaultLevelLength))
333
          else
334
          begin
335
            //if not (((ergebnis = '0') and (z = 4)) or ((ergebnis = '-624') and (z = 5)) or ((ergebnis = '222') and (z = 6)) or ((ergebnis = '3') and (z = 7))) then
336
            if (z < 4) or (z > 7) then
337
            begin
338
              if act = 4 then
339
                sl2.Add(inttostr(strtoint(ergebnis) + 32 - (5 * (strtoint(ergebnis) div 37))))
340
              else
341
                sl2.Add(Ergebnis);
342
            end;
343
          end;
344
        end;
345
      end;
346
      sl.Text := sl2.Text;
347
    finally
348
      FreeAndNil(sl2);
349
    end;
350
    {$ENDREGION}
351
  end;
352
 
52 daniel-mar 353
  if (sl.Strings[0] = '; SpaceMission 1.0') and // do not localize
354
     (sl.Strings[1] = '; LEV-File') then // do not localize
40 daniel-mar 355
  begin
356
    {$REGION 'Level format 1.0'}
357
    LevelEditorLength := StrToInt(sl.Strings[2]);
358
    curline := 3;
359
    while curline < sl.Count do
360
    begin
361
      tmpEnemy := TEnemyType(strtoint(sl.Strings[curline]));
362
      Inc(curline);
363
      tmpX := strtoint(sl.Strings[curline]);
364
      Inc(curline);
365
      tmpY := strtoint(sl.Strings[curline]);
366
      Inc(curline);
367
      tmpLifes := strtoint(sl.Strings[curline]);
368
      Inc(curline);
369
      AddEnemy(tmpX, tmpY, tmpEnemy, tmpLifes);
370
    end;
371
    {$ENDREGION}
372
  end
52 daniel-mar 373
  else if (SameText(sl.Strings[0], '['+OID_LEVSAV_VER12+']')) then
40 daniel-mar 374
  begin
375
    {$REGION 'Level format 1.2'}
376
    for curline := 1 to sl.Count-1 do
377
    begin
52 daniel-mar 378
      sLine := sl.Strings[curline].Trim;
379
      if (sLine = '') or (Copy(sLine, 1, 1) = ';') then continue;
380
      ary := SplitString(CollapseSpaces(sLine), ' ');
381
      if ary[0] = 'Width' then // do not localize
40 daniel-mar 382
      begin
52 daniel-mar 383
        LevelEditorLength := StrToInt(ary[1]);
384
        if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then
385
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
40 daniel-mar 386
      end
52 daniel-mar 387
      else if ary[0] = 'Name' then // do not localize
42 daniel-mar 388
      begin
52 daniel-mar 389
        LevelName := Copy(sLine, Length(ary[0])+2, Length(sLine));
42 daniel-mar 390
      end
52 daniel-mar 391
      else if ary[0] = 'Author' then // do not localize
42 daniel-mar 392
      begin
52 daniel-mar 393
        LevelAuthor := Copy(sLine, Length(ary[0])+2, Length(sLine));
42 daniel-mar 394
      end
52 daniel-mar 395
      else if ary[0] = 'Enemy' then // do not localize
40 daniel-mar 396
      begin
52 daniel-mar 397
        tmpEnemy := TEnemyType(strtoint(ary[1]));
398
        tmpX     := strtoint(ary[2]);
399
        tmpY     := strtoint(ary[3]);
400
        tmpLifes := strtoint(ary[4]);
401
        if (Length(ary) > 5) and (Copy(ary[5], 1, 1) <> ';') then
40 daniel-mar 402
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
403
        AddEnemy(tmpX, tmpY, tmpEnemy, tmpLifes);
404
      end;
405
    end;
406
    {$ENDREGION}
407
  end
408
  else
409
  begin
410
    raise Exception.Create('Level-Format nicht unterstützt oder Datei ist beschädigt');
411
  end;
412
 
413
  SortEnemies; // Sortierung nach X-Koordinate ist sehr wichtig für das Spiel!
414
end;
415
 
416
procedure TLevelData.LoadFromFile(filename: string);
417
var
418
  sl: TStringList;
419
  i, j: integer;
420
  temp: string;
421
  m: array[1..6] of tstrings;
422
begin
15 daniel-mar 423
  sl := TStringList.Create;
14 daniel-mar 424
  try
52 daniel-mar 425
    if EndsText('A1.lev', filename) then // do not localize
17 daniel-mar 426
    begin
30 daniel-mar 427
      {$REGION 'Backwards compatibility level format 0.2 (split into 5-6 files; convert to 0.3)'}
17 daniel-mar 428
      m[1] := TStringList.create;
429
      m[2] := TStringList.create;
430
      m[3] := TStringList.create;
431
      m[4] := TStringList.create;
432
      m[5] := TStringList.create;
433
      m[6] := TStringList.create;
434
      try
435
        for i := 1 to 6 do
436
        begin
437
          filename[Length(filename)-4] := IntToStr(i)[1]; // ...A2.sav, ...A3.sav, etc.
438
          if FileExists(filename) then
439
            m[i].loadfromfile(filename);
440
        end;
441
        m[1].strings[0] := '-624';
442
        if m[6].Text = '' then m[6].Text := '30000';
14 daniel-mar 443
 
52 daniel-mar 444
        sl.Add('; SpaceMission 0.3'); // do not localize
17 daniel-mar 445
        sl.Add(temp);
446
        for j := 0 to m[1].count-2 do
447
        begin
448
          for i := 0 to m[1].count-2 do
449
          begin
450
            if strtoint(m[1].strings[i]) > strtoint(m[1].strings[i+1]) then
451
            begin
452
              m[1].exchange(i, i+1);
453
              m[2].exchange(i, i+1);
454
              m[3].exchange(i, i+1);
455
              m[4].exchange(i, i+1);
456
              m[5].exchange(i, i+1);
457
            end;
458
          end;
459
        end;
460
        for i := 0 to m[3].count-1 do
461
        begin
462
          for j := 1 to 4 do
463
          begin
464
            if j = 1 then sl.Add(m[3].strings[i]);
465
            if j = 2 then sl.Add(m[1].strings[i]);
466
            if j = 3 then sl.Add(m[2].strings[i]);
467
            if j = 4 then sl.Add(m[4].strings[i]);
468
          end;
469
        end;
470
      finally
471
        FreeAndNil(m[1]);
472
        FreeAndNil(m[2]);
473
        FreeAndNil(m[3]);
474
        FreeAndNil(m[4]);
475
        FreeAndNil(m[5]);
476
        FreeAndNil(m[6]);
477
      end;
478
      {$ENDREGION}
479
    end
480
    else
14 daniel-mar 481
    begin
17 daniel-mar 482
      sl.LoadFromFile(filename);
483
    end;
484
 
40 daniel-mar 485
    LoadFromStrings(sl);
14 daniel-mar 486
  finally
15 daniel-mar 487
    FreeAndNil(sl);
14 daniel-mar 488
  end;
489
end;
490
 
40 daniel-mar 491
procedure TLevelData.SaveToStrings(sl: TStrings);
15 daniel-mar 492
var
493
  i: integer;
14 daniel-mar 494
begin
40 daniel-mar 495
  sl.Clear;
52 daniel-mar 496
  sl.Add('['+OID_LEVSAV_VER12+']');
497
  if LevelName   <> '' then sl.Add('Name   ' + LevelName); // do not localize
498
  if LevelAuthor <> '' then sl.Add('Author ' + LevelAuthor); // do not localize
499
  sl.Add('Width  ' + IntToStr(LevelEditorLength)); // do not localize
40 daniel-mar 500
  SortEnemies;
48 daniel-mar 501
  sl.Add(';      Type   XCoord YCoord Lives');
40 daniel-mar 502
  for i := 0 to Length(EnemyAdventTable)-1 do
503
  begin
52 daniel-mar 504
    sl.Add(Trim(
505
      'Enemy'.PadRight(6, ' ')+ // do not localize
30 daniel-mar 506
      ' '+
40 daniel-mar 507
      IntToStr(Ord(EnemyAdventTable[i].enemyType)).PadRight(6, ' ')+
508
      ' '+
509
      IntToStr(EnemyAdventTable[i].x).PadRight(6, ' ')+
510
      ' '+
511
      IntToStr(EnemyAdventTable[i].y).PadRight(6, ' ')+
512
      ' '+
513
      IntToStr(EnemyAdventTable[i].lifes).PadRight(6, ' ')+
30 daniel-mar 514
      ' '
52 daniel-mar 515
    ));
40 daniel-mar 516
  end;
517
end;
518
 
519
procedure TLevelData.SaveToFile(filename: string);
520
var
521
  sl: TStringList;
522
begin
523
  sl := TStringList.Create;
524
  try
525
    SaveToStrings(sl);
15 daniel-mar 526
    sl.SaveToFile(filename);
527
  finally
528
    FreeAndNil(sl);
529
  end;
14 daniel-mar 530
end;
531
 
27 daniel-mar 532
procedure TLevelData.SortEnemies;
533
var
534
  i, n: integer;
535
  e: TEnemyAdvent;
536
begin
537
  // Bubble Sort Algorithmus
538
  for n := Length(EnemyAdventTable) downto 2 do
539
  begin
540
    for i := 0 to n - 2 do
541
    begin
542
      if
543
        // Sort by X-coord (important for the game!)
544
        (EnemyAdventTable[i].x > EnemyAdventTable[i+1].x)
545
        or
546
        // Sort by Y-coord (just cosmetics)
547
        ((EnemyAdventTable[i].x = EnemyAdventTable[i+1].x) and (EnemyAdventTable[i].y > EnemyAdventTable[i+1].y))
548
      then
549
      begin
550
        e := EnemyAdventTable[i];
551
        EnemyAdventTable[i] := EnemyAdventTable[i + 1];
552
        EnemyAdventTable[i + 1] := e;
553
      end;
554
    end;
555
  end;
556
end;
557
 
40 daniel-mar 558
{ TSaveData }
559
 
560
procedure TSaveData.AssignTo(Dest: TPersistent);
561
var
562
  DestSaveData: TSaveData;
563
begin
564
  DestSaveData := Dest as TSaveData;
565
  if Assigned(DestSaveData) then
566
  begin
567
    DestSaveData.Score := Self.Score;
568
    DestSaveData.Life := Self.Life;
569
    DestSaveData.Level := Self.Level;
570
    DestSaveData.GameMode := Self.GameMode;
571
    if not Assigned(DestSaveData.LevelData) then DestSaveData.LevelData := TLevelData.Create;
572
    DestSaveData.LevelData.Assign(Self.LevelData);
573
  end
574
  else
575
  begin
576
    inherited;
577
  end;
578
end;
579
 
580
procedure TSaveData.Clear;
581
begin
582
  Score := 0;
583
  Life := 0;
584
  Level := 0;
585
  GameMode := gmUnknown;
586
  FreeAndNil(LevelData);
587
end;
588
 
589
destructor TSaveData.Destroy;
590
begin
591
  Clear;
592
  inherited;
593
end;
594
 
595
procedure TSaveData.SaveToStrings(sl: TStrings);
596
var
597
  sl2: TStringList;
598
begin
599
  sl2 := TStringList.Create;
600
  try
52 daniel-mar 601
    sl.Add('['+OID_LEVSAV_VER12+']');
602
    sl.Add('Score  ' + IntToStr(Score)); // do not localize
603
    sl.Add('Lives  ' + IntToStr(Life)); // do not localize
604
    sl.Add('Level  ' + IntToStr(Level)); // do not localize
605
    sl.Add('Mode   ' + IntToStr(Ord(GameMode))); // do not localize
40 daniel-mar 606
    LevelData.SaveToStrings(sl2);
52 daniel-mar 607
    sl2.Delete(0); // Delete additional level signature
40 daniel-mar 608
    sl.AddStrings(sl2);
609
  finally
610
    FreeAndNil(sl2);
611
  end;
612
end;
613
 
614
procedure TSaveData.LoadFromStrings(sl: TStrings);
615
var
616
  curline: Integer;
52 daniel-mar 617
  ary: TStringDynArray;
618
  sLine: string;
40 daniel-mar 619
begin
52 daniel-mar 620
  if (sl.Strings[0] = '; SpaceMission 1.0') and // do not localize
621
     (sl.Strings[1] = '; SAV-File') then // do not localize
40 daniel-mar 622
  begin
623
    Score    := StrToInt(sl.Strings[2]);
624
    Life     := StrToInt(sl.Strings[3]);
625
    Level    := StrToInt(sl.Strings[4]);
626
    GameMode := TGameMode(StrToInt(sl.Strings[5]));
627
    if Assigned(LevelData) then FreeAndNil(LevelData);
628
  end
52 daniel-mar 629
  else if SameText(sl.Strings[0], '['+OID_LEVSAV_VER12+']') then
40 daniel-mar 630
  begin
631
    Score    := 0;
632
    Life     := 0;
633
    Level    := 0;
634
    GameMode := gmUnknown;
635
    for curline := 1 to sl.Count-1 do
636
    begin
52 daniel-mar 637
      sLine := sl.Strings[curline].Trim;
638
      if (sLine = '') or (Copy(sLine, 1, 1) = ';') then continue;
639
      ary := SplitString(CollapseSpaces(sLine), ' ');
640
      if ary[0] = 'Score' then // do not localize
40 daniel-mar 641
      begin
52 daniel-mar 642
        Score := StrToInt(ary[1]);
643
        if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then
644
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
40 daniel-mar 645
      end
52 daniel-mar 646
      else if ary[0] = 'Lives' then // do not localize
40 daniel-mar 647
      begin
52 daniel-mar 648
        Life := StrToInt(ary[1]);
649
        if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then
650
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
40 daniel-mar 651
      end
52 daniel-mar 652
      else if ary[0] = 'Level' then // do not localize
40 daniel-mar 653
      begin
52 daniel-mar 654
        Level := StrToInt(ary[1]);
655
        if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then
656
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
40 daniel-mar 657
      end
52 daniel-mar 658
      else if ary[0] = 'Mode' then // do not localize
40 daniel-mar 659
      begin
52 daniel-mar 660
        GameMode := TGameMode(StrToInt(ary[1]));
661
        if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then
662
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
40 daniel-mar 663
      end;
664
    end;
665
    if Assigned(LevelData) then FreeAndNil(LevelData);
666
    LevelData := TLevelData.Create;
44 daniel-mar 667
    LevelData.RasterErzwingen := false;
40 daniel-mar 668
    LevelData.LoadFromStrings(sl);
669
  end
670
  else
671
  begin
672
    raise Exception.Create('Spielstand-Format nicht unterstützt oder Datei beschädigt');
673
  end;
674
end;
675
 
676
procedure TSaveData.LoadFromFile(filename: string);
677
var
678
  sl: TStringList;
679
begin
680
  sl := TStringList.Create;
681
  try
682
    sl.LoadFromFile(filename);
683
    LoadFromStrings(sl);
684
  finally
685
    FreeAndNil(sl);
686
  end;
687
end;
688
 
689
procedure TSaveData.SaveToFile(filename: string);
690
var
691
  sl: TStringList;
692
begin
693
  sl := TStringList.Create;
694
  try
695
    SaveToStrings(sl);
696
    sl.SaveToFile(filename);
697
  finally
698
    FreeAndNil(sl);
699
  end;
700
end;
701
 
14 daniel-mar 702
end.