@@ -6,10 +6,27 @@ interface |
| 6 |
|
6 |
|
| 7 |
uses Classes, HugeDatatypes, instruments; |
7 |
uses Classes, HugeDatatypes, instruments; |
| 8 |
|
8 |
|
|
|
9 |
const |
|
|
10 |
CURRENT_VERSION = 2; |
|
|
11 |
|
| 9 |
type |
12 |
type |
| 10 |
{ TSong } |
13 |
TSongV1 = packed record |
|
|
14 |
Version: Integer; |
|
|
15 |
|
|
|
16 |
Name: ShortString; |
|
|
17 |
Artist: ShortString; |
|
|
18 |
Comment: ShortString; |
| 11 |
|
19 |
|
| 12 |
TSong = packed record |
20 |
Instruments: TInstrumentBank; |
|
|
21 |
Waves: TWaveBank; |
|
|
22 |
|
|
|
23 |
TicksPerRow: Integer; |
|
|
24 |
|
|
|
25 |
Patterns: TPatternMap; |
|
|
26 |
OrderMatrix: TOrderMatrix; |
|
|
27 |
end; |
|
|
28 |
|
|
|
29 |
TSongV2 = packed record |
| 13 |
Version: Integer; |
30 |
Version: Integer; |
| 14 |
|
31 |
|
| 15 |
Name: ShortString; |
32 |
Name: ShortString; |
@@ -23,52 +40,34 @@ type |
| 23 |
|
40 |
|
| 24 |
Patterns: TPatternMap; |
41 |
Patterns: TPatternMap; |
| 25 |
OrderMatrix: TOrderMatrix; |
42 |
OrderMatrix: TOrderMatrix; |
|
|
43 |
|
|
|
44 |
Routines: TRoutineBank; |
| 26 |
end; |
45 |
end; |
| 27 |
|
46 |
|
|
|
47 |
{ TSong } |
|
|
48 |
|
|
|
49 |
TSong = TSongV2; |
|
|
50 |
|
| 28 |
procedure WriteSongToStream(S: TStream; const ASong: TSong); |
51 |
procedure WriteSongToStream(S: TStream; const ASong: TSong); |
| 29 |
procedure ReadSongFromStream(S: TStream; var ASong: TSong); |
52 |
procedure ReadSongFromStream(S: TStream; out ASong: TSong); |
| 30 |
procedure InitializeSong(var S: TSong); |
53 |
procedure InitializeSong(var S: TSong); |
| 31 |
procedure DestroySong(var S: TSong); |
54 |
procedure DestroySong(var S: TSong); |
| 32 |
|
55 |
|
|
|
56 |
function UpgradeSong(S: TSongV1): TSong; overload; |
|
|
57 |
function UpgradeSong(S: TSongV2): TSong; overload; |
|
|
58 |
|
| 33 |
implementation |
59 |
implementation |
| 34 |
|
60 |
|
| 35 |
// Thanks to WP on the FreePascal forums for this code! |
61 |
// Thanks to WP on the FreePascal forums for this code! |
| 36 |
// https://forum.lazarus.freepascal.org/index.php/topic,47892.msg344152.html#msg344152 |
62 |
// https://forum.lazarus.freepascal.org/index.php/topic,47892.msg344152.html#msg344152 |
| 37 |
|
63 |
|
| 38 |
procedure WriteSongToStream(S: TStream; const ASong: TSong); |
64 |
procedure ReadSongFromStreamV1(S: TStream; out ASong: TSongV1); |
| 39 |
var |
|
|
| 40 |
i, n: Integer; |
|
|
| 41 |
pPat: PPattern; |
|
|
| 42 |
begin |
|
|
| 43 |
// Write the fixed record elements first |
|
|
| 44 |
n := SizeOf(TSong) - SizeOf(TPatternMap) - SizeOf(TOrderMatrix); |
|
|
| 45 |
S.Write(ASong, n); |
|
|
| 46 |
|
|
|
| 47 |
// Write the pattern count |
|
|
| 48 |
S.Write(ASong.Patterns.Count, SizeOf(Integer)); |
|
|
| 49 |
// Write the patterns |
|
|
| 50 |
for i := 0 to ASong.Patterns.Count-1 do |
|
|
| 51 |
begin |
|
|
| 52 |
pPat := ASong.Patterns[i]; |
|
|
| 53 |
S.Write(pPat^, SizeOf(pPat^)); |
|
|
| 54 |
end; |
|
|
| 55 |
|
|
|
| 56 |
// Write the OrderMatrix arrays |
|
|
| 57 |
for i := 0 to 3 do |
|
|
| 58 |
begin |
|
|
| 59 |
n := Length(ASong.OrderMatrix[i]); |
|
|
| 60 |
S.Write(n, SizeOf(Integer)); |
|
|
| 61 |
S.Write(ASong.OrderMatrix[i][0], n*SizeOf(Integer)); |
|
|
| 62 |
end; |
|
|
| 63 |
end; |
|
|
| 64 |
|
|
|
| 65 |
procedure ReadSongFromStream(S: TStream; var ASong: TSong); |
|
|
| 66 |
var |
65 |
var |
| 67 |
i, n: Integer; |
66 |
i, n: Integer; |
| 68 |
pat: PPattern; |
67 |
pat: PPattern; |
| 69 |
begin |
68 |
begin |
| 70 |
// Read the fixed elements first |
69 |
// Read the fixed elements first |
| 71 |
n := SizeOf(TSong) - SizeOf(TPatternMap) - SizeOf(TOrderMatrix); |
70 |
n := SizeOf(TSongV1) - SizeOf(TPatternMap) - SizeOf(TOrderMatrix); |
| 72 |
S.Read(ASong, n); |
71 |
S.Read(ASong, n); |
| 73 |
|
72 |
|
| 74 |
// ASong.Patterns.Clear; |
73 |
// ASong.Patterns.Clear; |
@@ -98,12 +97,71 @@ begin |
| 98 |
end; |
97 |
end; |
| 99 |
end; |
98 |
end; |
| 100 |
|
99 |
|
|
|
100 |
procedure ReadSongFromStreamV2(S: TStream; out ASong: TSongV2); |
|
|
101 |
var |
|
|
102 |
i: Integer; |
|
|
103 |
SongV1: TSongV1 absolute ASong; |
|
|
104 |
begin |
|
|
105 |
ReadSongFromStreamV1(S, SongV1); |
|
|
106 |
|
|
|
107 |
// Read routines |
|
|
108 |
for I := Low(TRoutineBank) to High(TRoutineBank) do |
|
|
109 |
ASong.Routines[I] := S.ReadAnsiString; |
|
|
110 |
end; |
|
|
111 |
|
|
|
112 |
procedure WriteSongToStream(S: TStream; const ASong: TSong); |
|
|
113 |
var |
|
|
114 |
i, n: Integer; |
|
|
115 |
pPat: PPattern; |
|
|
116 |
begin |
|
|
117 |
// Write the fixed record elements first |
|
|
118 |
n := SizeOf(TSong) - SizeOf(TPatternMap) - SizeOf(TOrderMatrix); |
|
|
119 |
S.Write(ASong, n); |
|
|
120 |
|
|
|
121 |
// Write the pattern count |
|
|
122 |
S.Write(ASong.Patterns.Count, SizeOf(Integer)); |
|
|
123 |
// Write the patterns |
|
|
124 |
for i := 0 to ASong.Patterns.Count-1 do |
|
|
125 |
begin |
|
|
126 |
pPat := ASong.Patterns[i]; |
|
|
127 |
S.Write(pPat^, SizeOf(pPat^)); |
|
|
128 |
end; |
|
|
129 |
|
|
|
130 |
// Write the OrderMatrix arrays |
|
|
131 |
for i := 0 to 3 do |
|
|
132 |
begin |
|
|
133 |
n := Length(ASong.OrderMatrix[i]); |
|
|
134 |
S.Write(n, SizeOf(Integer)); |
|
|
135 |
S.Write(ASong.OrderMatrix[i][0], n*SizeOf(Integer)); |
|
|
136 |
end; |
|
|
137 |
|
|
|
138 |
// Write the routines |
|
|
139 |
for i := Low(TRoutineBank) to High(TRoutineBank) do |
|
|
140 |
S.WriteAnsiString(ASong.Routines[i]); |
|
|
141 |
end; |
|
|
142 |
|
|
|
143 |
procedure ReadSongFromStream(S: TStream; out ASong: TSong); |
|
|
144 |
var |
|
|
145 |
Version: Integer; |
|
|
146 |
SV1: TSongV1; |
|
|
147 |
begin |
|
|
148 |
S.Read(Version, SizeOf(Integer)); |
|
|
149 |
S.Seek(0, soBeginning); |
|
|
150 |
case Version of |
|
|
151 |
0..1: begin |
|
|
152 |
ReadSongFromStreamV1(S, SV1); |
|
|
153 |
ASong := UpgradeSong(SV1); |
|
|
154 |
end; |
|
|
155 |
2: ReadSongFromStreamV2(S, ASong); |
|
|
156 |
end; |
|
|
157 |
end; |
|
|
158 |
|
| 101 |
procedure InitializeSong(var S: TSong); |
159 |
procedure InitializeSong(var S: TSong); |
| 102 |
var |
160 |
var |
| 103 |
I, J: Integer; |
161 |
I, J: Integer; |
| 104 |
begin |
162 |
begin |
| 105 |
with S do begin |
163 |
with S do begin |
| 106 |
Version := 1; |
164 |
Version := CURRENT_VERSION; |
| 107 |
Name := ''; |
165 |
Name := ''; |
| 108 |
Artist := ''; |
166 |
Artist := ''; |
| 109 |
Comment := ''; |
167 |
Comment := ''; |
@@ -148,5 +206,25 @@ begin |
| 148 |
SetLength(S.OrderMatrix[I], 0); |
206 |
SetLength(S.OrderMatrix[I], 0); |
| 149 |
end; |
207 |
end; |
| 150 |
|
208 |
|
|
|
209 |
function UpgradeSong(S: TSongV1): TSong; |
|
|
210 |
var |
|
|
211 |
SV2: TSongV2; |
|
|
212 |
begin |
|
|
213 |
SV2 := Default(TSongV2); |
|
|
214 |
Move(S, SV2, SizeOf(TSongV1) - SizeOf(TPatternMap) - SizeOf(TOrderMatrix)); |
|
|
215 |
|
|
|
216 |
// Gotta preserve reference count so can't include it in the move... |
|
|
217 |
SV2.Patterns := S.Patterns; |
|
|
218 |
SV2.OrderMatrix := S.OrderMatrix; |
|
|
219 |
Inc(SV2.Version); // Bump version |
|
|
220 |
|
|
|
221 |
Result := UpgradeSong(SV2); |
|
|
222 |
end; |
|
|
223 |
|
|
|
224 |
function UpgradeSong(S: TSongV2): TSong; |
|
|
225 |
begin |
|
|
226 |
Result := S; |
|
|
227 |
end; |
|
|
228 |
|
| 151 |
end. |
229 |
end. |
| 152 |
|
230 |
|