@@ -13,20 +13,13 @@ type |
| 13 |
TExportMode = (emNormal, emPreview, emGBS); |
13 |
TExportMode = (emNormal, emPreview, emGBS); |
| 14 |
|
14 |
|
| 15 |
function RenderPreviewRom(Song: TSong): boolean; |
15 |
function RenderPreviewRom(Song: TSong): boolean; |
| 16 |
function RenderSongToFile(Song: TSong; Filename: string; |
16 |
function RenderSongToFile(Song: TSong; Filename: string; Mode: TExportMode = emNormal): boolean; |
| 17 |
Mode: TExportMode = emNormal; DescriptorName: String = 'song_descriptor'): boolean; |
|
|
| 18 |
procedure RenderSongToGBDKC(Song: TSong; DescriptorName: String; Filename: string); |
17 |
procedure RenderSongToGBDKC(Song: TSong; DescriptorName: String; Filename: string); |
|
|
18 |
procedure RenderSongToRGBDSAsm(Song: TSong; DescriptorName: String; Filename: string); |
| 19 |
|
19 |
|
| 20 |
implementation |
20 |
implementation |
| 21 |
|
21 |
|
| 22 |
procedure RenderSongToGBDKC(Song: TSong; DescriptorName: String; Filename: string); |
22 |
procedure RenderSongToGBDKC(Song: TSong; DescriptorName: String; Filename: string); |
| 23 |
var |
|
|
| 24 |
OrderMatrix: TOrderMatrix; |
|
|
| 25 |
OutSL: TStringList; |
|
|
| 26 |
OrderCnt: integer; |
|
|
| 27 |
I: integer; |
|
|
| 28 |
F: Text; |
|
|
| 29 |
|
|
|
| 30 |
function RenderGBDKCell(Cell: TCell): string; |
23 |
function RenderGBDKCell(Cell: TCell): string; |
| 31 |
var |
24 |
var |
| 32 |
SL: TStringList; |
25 |
SL: TStringList; |
@@ -142,6 +135,12 @@ var |
| 142 |
Result += '};'; |
135 |
Result += '};'; |
| 143 |
end; |
136 |
end; |
| 144 |
|
137 |
|
|
|
138 |
var |
|
|
139 |
OrderMatrix: TOrderMatrix; |
|
|
140 |
OutSL: TStringList; |
|
|
141 |
OrderCnt: integer; |
|
|
142 |
I: integer; |
|
|
143 |
F: Text; |
| 145 |
begin |
144 |
begin |
| 146 |
Song := OptimizeSong(Song); |
145 |
Song := OptimizeSong(Song); |
| 147 |
|
146 |
|
@@ -192,220 +191,218 @@ begin |
| 192 |
OutSL.Free; |
191 |
OutSL.Free; |
| 193 |
end; |
192 |
end; |
| 194 |
|
193 |
|
| 195 |
procedure RenderSongToHTTs(Song: TSong; DescriptorName: String; Filename: string); |
194 |
function RenderOrderTable(OrderMatrix: TOrderMatrix): string; |
| 196 |
function RenderOrderTable(OrderMatrix: TOrderMatrix): string; |
195 |
function ArrayHelper(Ints: array of integer): string; |
| 197 |
|
|
|
| 198 |
function ArrayHelper(Ints: array of integer): string; |
|
|
| 199 |
var |
|
|
| 200 |
I: integer; |
|
|
| 201 |
SL: TStringList; |
|
|
| 202 |
begin |
|
|
| 203 |
SL := TStringList.Create; |
|
|
| 204 |
SL.StrictDelimiter := True; |
|
|
| 205 |
SL.Delimiter := ','; |
|
|
| 206 |
for I := Low(Ints) to High(Ints)-1 do // HACK: account for the off-by-one error |
|
|
| 207 |
SL.Add('P' + IntToStr(Ints[I])); |
|
|
| 208 |
Result := SL.DelimitedText; |
|
|
| 209 |
SL.Free; |
|
|
| 210 |
end; |
|
|
| 211 |
|
|
|
| 212 |
var |
196 |
var |
| 213 |
Res: TStringList; |
197 |
I: integer; |
| 214 |
OrderCnt: integer; |
198 |
SL: TStringList; |
| 215 |
begin |
199 |
begin |
| 216 |
Res := TStringList.Create; |
200 |
SL := TStringList.Create; |
| 217 |
OrderCnt := MaxIntValue([High(OrderMatrix[0]), High(OrderMatrix[1]), |
201 |
SL.StrictDelimiter := True; |
| 218 |
High(OrderMatrix[2]), High(OrderMatrix[3])]); |
202 |
SL.Delimiter := ','; |
| 219 |
|
203 |
for I := Low(Ints) to High(Ints)-1 do // HACK: account for the off-by-one error |
| 220 |
Res.Add('order_cnt: db ' + IntToStr(OrderCnt * 2)); |
204 |
SL.Add('P' + IntToStr(Ints[I])); |
| 221 |
Res.Add('order1: dw ' + ArrayHelper(OrderMatrix[0])); |
205 |
Result := SL.DelimitedText; |
| 222 |
Res.Add('order2: dw ' + ArrayHelper(OrderMatrix[1])); |
206 |
SL.Free; |
| 223 |
Res.Add('order3: dw ' + ArrayHelper(OrderMatrix[2])); |
|
|
| 224 |
Res.Add('order4: dw ' + ArrayHelper(OrderMatrix[3])); |
|
|
| 225 |
|
|
|
| 226 |
Result := Res.Text; |
|
|
| 227 |
Res.Free; |
|
|
| 228 |
end; |
207 |
end; |
| 229 |
|
208 |
|
| 230 |
function RenderInstruments(Instruments: TInstrumentBank): string; |
209 |
var |
| 231 |
var |
210 |
Res: TStringList; |
| 232 |
SL, ResultSL: TStringList; |
211 |
OrderCnt: integer; |
| 233 |
AsmInstrument: TAsmInstrument; |
212 |
begin |
| 234 |
I, J: integer; |
213 |
Res := TStringList.Create; |
| 235 |
TypePrefix: string; |
214 |
OrderCnt := MaxIntValue([High(OrderMatrix[0]), High(OrderMatrix[1]), |
| 236 |
HighMask: byte; |
215 |
High(OrderMatrix[2]), High(OrderMatrix[3])]); |
| 237 |
begin |
|
|
| 238 |
ResultSL := TStringList.Create; |
|
|
| 239 |
|
216 |
|
| 240 |
for I := Low(Instruments) to High(Instruments) do |
217 |
Res.Add('order_cnt: db ' + IntToStr(OrderCnt * 2)); |
| 241 |
begin |
218 |
Res.Add('order1: dw ' + ArrayHelper(OrderMatrix[0])); |
| 242 |
AsmInstrument := InstrumentToBytes(Instruments[I]); |
219 |
Res.Add('order2: dw ' + ArrayHelper(OrderMatrix[1])); |
| 243 |
SL := TStringList.Create; |
220 |
Res.Add('order3: dw ' + ArrayHelper(OrderMatrix[2])); |
| 244 |
SL.StrictDelimiter := True; |
221 |
Res.Add('order4: dw ' + ArrayHelper(OrderMatrix[3])); |
| 245 |
SL.Delimiter := ','; |
|
|
| 246 |
|
222 |
|
| 247 |
if Instruments[I].Type_ = itNoise then |
223 |
Result := Res.Text; |
| 248 |
begin |
224 |
Res.Free; |
| 249 |
SL.Add(IntToStr(AsmInstrument[1])); // envelope |
225 |
end; |
| 250 |
|
|
|
| 251 |
HighMask := AsmInstrument[0]; |
|
|
| 252 |
if Instruments[I].LengthEnabled then |
|
|
| 253 |
HighMask := HighMask or %01000000; |
|
|
| 254 |
if Instruments[I].CounterStep = swSeven then |
|
|
| 255 |
HighMask := HighMask or %10000000; |
|
|
| 256 |
SL.Add(IntToStr(HighMask)); |
|
|
| 257 |
|
|
|
| 258 |
for J := Low(TNoiseMacro) to High(TNoiseMacro) do |
|
|
| 259 |
SL.Add(IntToStr(Instruments[I].NoiseMacro[J])); |
|
|
| 260 |
end |
|
|
| 261 |
else |
|
|
| 262 |
for J := Low(AsmInstrument) to High(AsmInstrument) do |
|
|
| 263 |
SL.Add(IntToStr(AsmInstrument[J])); |
|
|
| 264 |
|
|
|
| 265 |
WriteStr(TypePrefix, Instruments[I].Type_); |
|
|
| 266 |
ResultSL.Add(Format('%s%s: db %s', [TypePrefix, 'inst' + IntToStr(I), |
|
|
| 267 |
SL.DelimitedText])); |
|
|
| 268 |
SL.Free; |
|
|
| 269 |
end; |
|
|
| 270 |
|
226 |
|
| 271 |
Result := ResultSL.Text; |
227 |
function RenderInstruments(Instruments: TInstrumentBank): string; |
| 272 |
ResultSL.Free; |
228 |
var |
| 273 |
end; |
229 |
SL, ResultSL: TStringList; |
|
|
230 |
AsmInstrument: TAsmInstrument; |
|
|
231 |
I, J: integer; |
|
|
232 |
TypePrefix: string; |
|
|
233 |
HighMask: byte; |
|
|
234 |
begin |
|
|
235 |
ResultSL := TStringList.Create; |
| 274 |
|
236 |
|
| 275 |
function RenderCell(Cell: TCell): string; |
237 |
for I := Low(Instruments) to High(Instruments) do |
| 276 |
var |
|
|
| 277 |
SL: TStringList; |
|
|
| 278 |
begin |
238 |
begin |
|
|
239 |
AsmInstrument := InstrumentToBytes(Instruments[I]); |
| 279 |
SL := TStringList.Create; |
240 |
SL := TStringList.Create; |
| 280 |
SL.Delimiter := ','; |
|
|
| 281 |
SL.StrictDelimiter := True; |
241 |
SL.StrictDelimiter := True; |
|
|
242 |
SL.Delimiter := ','; |
| 282 |
|
243 |
|
| 283 |
if Cell.Note = NO_NOTE then |
244 |
if Instruments[I].Type_ = itNoise then |
| 284 |
SL.Add('___') |
245 |
begin |
| 285 |
else |
246 |
SL.Add(IntToStr(AsmInstrument[1])); // envelope |
| 286 |
SL.Add(NoteToDriverMap.KeyData[Cell.Note]); |
|
|
| 287 |
|
247 |
|
| 288 |
if InRange(Cell.Instrument, 0, 15) then |
248 |
HighMask := AsmInstrument[0]; |
| 289 |
SL.Add(IntToStr(Cell.Instrument)) |
249 |
if Instruments[I].LengthEnabled then |
| 290 |
else |
250 |
HighMask := HighMask or %01000000; |
| 291 |
SL.Add('0'); |
251 |
if Instruments[I].CounterStep = swSeven then |
|
|
252 |
HighMask := HighMask or %10000000; |
|
|
253 |
SL.Add(IntToStr(HighMask)); |
| 292 |
|
254 |
|
| 293 |
SL.Add('$' + EffectCodeToStr(Cell.EffectCode, Cell.EffectParams)); |
255 |
for J := Low(TNoiseMacro) to High(TNoiseMacro) do |
|
|
256 |
SL.Add(IntToStr(Instruments[I].NoiseMacro[J])); |
|
|
257 |
end |
|
|
258 |
else |
|
|
259 |
for J := Low(AsmInstrument) to High(AsmInstrument) do |
|
|
260 |
SL.Add(IntToStr(AsmInstrument[J])); |
| 294 |
|
261 |
|
| 295 |
// RGBDS thinks you're defining a new macro if you don't have a space first. |
262 |
WriteStr(TypePrefix, Instruments[I].Type_); |
| 296 |
Result := ' dn ' + SL.DelimitedText; |
263 |
ResultSL.Add(Format('%s%s: db %s', [TypePrefix, 'inst' + IntToStr(I), |
|
|
264 |
SL.DelimitedText])); |
| 297 |
SL.Free; |
265 |
SL.Free; |
| 298 |
end; |
266 |
end; |
| 299 |
|
267 |
|
| 300 |
function RenderPattern(Name: string; Pattern: TPattern): string; |
268 |
Result := ResultSL.Text; |
| 301 |
var |
269 |
ResultSL.Free; |
| 302 |
SL: TStringList; |
270 |
end; |
| 303 |
I: integer; |
|
|
| 304 |
begin |
|
|
| 305 |
SL := TStringList.Create; |
|
|
| 306 |
SL.Add(Name + ':'); |
|
|
| 307 |
|
271 |
|
| 308 |
for I := Low(TPattern) to High(TPattern) do |
272 |
function RenderCell(Cell: TCell): string; |
| 309 |
SL.Add(RenderCell(Pattern[I])); |
273 |
var |
|
|
274 |
SL: TStringList; |
|
|
275 |
begin |
|
|
276 |
SL := TStringList.Create; |
|
|
277 |
SL.Delimiter := ','; |
|
|
278 |
SL.StrictDelimiter := True; |
| 310 |
|
279 |
|
| 311 |
Result := SL.Text; |
280 |
if Cell.Note = NO_NOTE then |
| 312 |
SL.Free; |
281 |
SL.Add('___') |
| 313 |
end; |
282 |
else |
|
|
283 |
SL.Add(NoteToDriverMap.KeyData[Cell.Note]); |
| 314 |
|
284 |
|
| 315 |
function RenderWaveforms(Waves: TWaveBank): string; |
285 |
if InRange(Cell.Instrument, 0, 15) then |
| 316 |
var |
286 |
SL.Add(IntToStr(Cell.Instrument)) |
| 317 |
SL, ResultSL: TStringList; |
287 |
else |
| 318 |
I, J: integer; |
288 |
SL.Add('0'); |
| 319 |
begin |
|
|
| 320 |
ResultSL := TStringList.Create; |
|
|
| 321 |
|
289 |
|
| 322 |
for I := Low(Waves) to High(Waves) do |
290 |
SL.Add('$' + EffectCodeToStr(Cell.EffectCode, Cell.EffectParams)); |
| 323 |
begin |
|
|
| 324 |
SL := TStringList.Create; |
|
|
| 325 |
SL.StrictDelimiter := True; |
|
|
| 326 |
SL.Delimiter := ','; |
|
|
| 327 |
|
291 |
|
| 328 |
J := Low(Waves[I]); |
292 |
// RGBDS thinks you're defining a new macro if you don't have a space first. |
| 329 |
while J < High(Waves[I]) do |
293 |
Result := ' dn ' + SL.DelimitedText; |
| 330 |
begin |
294 |
SL.Free; |
| 331 |
SL.Add(IntToStr((Waves[I, J] shl 4) or Waves[I, J + 1])); |
295 |
end; |
| 332 |
Inc(J, 2); |
|
|
| 333 |
end; |
|
|
| 334 |
ResultSL.Add(Format('wave%d: db %s', [I, SL.DelimitedText])); |
|
|
| 335 |
SL.Free; |
|
|
| 336 |
end; |
|
|
| 337 |
|
296 |
|
| 338 |
Result := ResultSL.Text; |
297 |
function RenderPattern(Name: string; Pattern: TPattern): string; |
| 339 |
ResultSL.Free; |
298 |
var |
| 340 |
end; |
299 |
SL: TStringList; |
|
|
300 |
I: integer; |
|
|
301 |
begin |
|
|
302 |
SL := TStringList.Create; |
|
|
303 |
SL.Add(Name + ':'); |
| 341 |
|
304 |
|
| 342 |
function RenderConstants(Song: TSong; Mode: TExportMode): string; |
305 |
for I := Low(TPattern) to High(TPattern) do |
| 343 |
var |
306 |
SL.Add(RenderCell(Pattern[I])); |
| 344 |
SL: TStringList; |
307 |
|
|
|
308 |
Result := SL.Text; |
|
|
309 |
SL.Free; |
|
|
310 |
end; |
|
|
311 |
|
|
|
312 |
function RenderWaveforms(Waves: TWaveBank): string; |
|
|
313 |
var |
|
|
314 |
SL, ResultSL: TStringList; |
|
|
315 |
I, J: integer; |
|
|
316 |
begin |
|
|
317 |
ResultSL := TStringList.Create; |
|
|
318 |
|
|
|
319 |
for I := Low(Waves) to High(Waves) do |
| 345 |
begin |
320 |
begin |
| 346 |
SL := TStringList.Create; |
321 |
SL := TStringList.Create; |
| 347 |
SL.Add('STANDALONE_MODE EQU 1'); |
322 |
SL.StrictDelimiter := True; |
|
|
323 |
SL.Delimiter := ','; |
| 348 |
|
324 |
|
| 349 |
if Mode = emPreview then |
325 |
J := Low(Waves[I]); |
| 350 |
SL.Add('PREVIEW_MODE EQU 1') |
326 |
while J < High(Waves[I]) do |
| 351 |
else if Mode = emGBS then |
|
|
| 352 |
begin |
327 |
begin |
| 353 |
SL.Add('GBS_MODE EQU 1'); |
328 |
SL.Add(IntToStr((Waves[I, J] shl 4) or Waves[I, J + 1])); |
| 354 |
SL.Add('GBS_TITLE EQUS "\"' + PadRight(Song.Name, 32) + '\""'); |
329 |
Inc(J, 2); |
| 355 |
SL.Add('GBS_AUTHOR EQUS "\"' + PadRight(Song.Artist, 32) + '\""'); |
|
|
| 356 |
SL.Add('GBS_COPYRIGHT EQUS "\"' + PadRight(IntToStr(CurrentYear), 32) + '\""'); |
|
|
| 357 |
end; |
330 |
end; |
| 358 |
SL.Add('TICKS EQU ' + IntToStr(Song.TicksPerRow)); |
331 |
ResultSL.Add(Format('wave%d: db %s', [I, SL.DelimitedText])); |
| 359 |
|
|
|
| 360 |
Result := SL.Text; |
|
|
| 361 |
SL.Free; |
332 |
SL.Free; |
| 362 |
end; |
333 |
end; |
| 363 |
|
334 |
|
| 364 |
procedure WriteRoutinesToFile(Routines: TRoutineBank); |
335 |
Result := ResultSL.Text; |
| 365 |
var |
336 |
ResultSL.Free; |
| 366 |
I: integer; |
337 |
end; |
| 367 |
F: Text; |
|
|
| 368 |
begin |
|
|
| 369 |
for I := Low(TRoutineBank) to High(TRoutineBank) do |
|
|
| 370 |
begin |
|
|
| 371 |
AssignFile(F, './hUGEDriver/routine' + IntToStr(I) + '.htt'); |
|
|
| 372 |
Rewrite(F); |
|
|
| 373 |
Write(F, Routines[I]); |
|
|
| 374 |
CloseFile(F); |
|
|
| 375 |
end; |
|
|
| 376 |
end; |
|
|
| 377 |
|
338 |
|
| 378 |
procedure WriteHTT(F: string; S: string); |
339 |
procedure RenderSongToRGBDSAsm(Song: TSong; DescriptorName: String; Filename: string); |
| 379 |
begin |
340 |
var |
| 380 |
AssignFile(OutFile, F); |
341 |
OutSL: TStringList; |
| 381 |
Rewrite(OutFile); |
342 |
F: Text; |
| 382 |
Write(OutFile, S); |
343 |
I: Integer; |
| 383 |
CloseFile(OutFile); |
|
|
| 384 |
end; |
|
|
| 385 |
begin |
344 |
begin |
| 386 |
FilePath := Filename; |
345 |
OutSL := TStringList.Create; |
| 387 |
Filename := ExtractFileNameWithoutExt(ExtractFileNameOnly(Filename)); |
|
|
| 388 |
|
346 |
|
| 389 |
WriteHTT('./hUGEDriver/'+DescriptorName+'_constants.htt', RenderConstants(Song, Mode)); |
347 |
OutSL.Add('include "include/hUGE.inc"'); |
| 390 |
WriteHTT('./hUGEDriver/'+DescriptorName+'_wave.htt', RenderWaveforms(Song.Waves)); |
348 |
OutSL.Add(''); |
| 391 |
WriteHTT('./hUGEDriver/'+DescriptorName+'_order.htt', RenderOrderTable(Song.OrderMatrix)); |
349 |
OutSL.Add('SECTION "Song Data", ROMX'); |
| 392 |
WriteHTT('./hUGEDriver/'+DescriptorName+'_duty_instrument.htt', RenderInstruments(Song.Instruments.Duty)); |
350 |
OutSL.Add(''); |
| 393 |
WriteHTT('./hUGEDriver/'+DescriptorName+'_wave_instrument.htt', RenderInstruments(Song.Instruments.Wave)); |
|
|
| 394 |
WriteHTT('./hUGEDriver/'+DescriptorName+'_noise_instrument.htt', |
|
|
| 395 |
RenderInstruments(Song.Instruments.Noise)); |
|
|
| 396 |
WriteRoutinesToFile(Song.Routines); |
|
|
| 397 |
|
351 |
|
| 398 |
AssignFile(OutFile, './hUGEDriver/pattern.htt'); |
352 |
// Render song descriptor |
| 399 |
Rewrite(OutFile); |
353 |
OutSL.Add(DescriptorName+'::'); |
|
|
354 |
OutSL.Add('db '+IntToStr(Song.TicksPerRow)); |
|
|
355 |
OutSL.Add('dw order_cnt'); |
|
|
356 |
OutSL.Add('dw order1, order2, order3, order4'); |
|
|
357 |
OutSL.Add('dw duty_instruments, wave_instruments, noise_instruments'); |
|
|
358 |
OutSL.Add('dw routines'); |
|
|
359 |
OutSL.Add('dw waves'); |
|
|
360 |
OutSL.Add(''); |
|
|
361 |
|
|
|
362 |
// Render order matrix |
|
|
363 |
OutSL.Add(RenderOrderTable(Song.OrderMatrix)); |
|
|
364 |
|
|
|
365 |
// Render patterns |
| 400 |
|
366 |
|
| 401 |
// TODO: Are keys and data defined to be aligned? Seems like they are but |
367 |
// TODO: Are keys and data defined to be aligned? Seems like they are but |
| 402 |
// should probably find out if that's just an implementation detail... |
368 |
// should probably find out if that's just an implementation detail... |
| 403 |
for I := 0 to Song.Patterns.Count - 1 do |
369 |
for I := 0 to Song.Patterns.Count - 1 do |
| 404 |
if PatternIsUsed(Song.Patterns.Keys[I], Song) then |
370 |
if PatternIsUsed(Song.Patterns.Keys[I], Song) then |
| 405 |
Write(OutFile, RenderPattern('P' + IntToStr(Song.Patterns.Keys[I]), |
371 |
OutSL.Add(RenderPattern('P' + IntToStr(Song.Patterns.Keys[I]), Song.Patterns.Data[I]^)); |
| 406 |
Song.Patterns.Data[I]^)); |
|
|
| 407 |
|
372 |
|
| 408 |
CloseFile(OutFile); |
373 |
// Render instruments |
|
|
374 |
OutSL.Add('duty_instruments:'); |
|
|
375 |
OutSL.Add(RenderInstruments(Song.Instruments.Duty)); |
|
|
376 |
OutSL.Add(''); |
|
|
377 |
|
|
|
378 |
OutSL.Add('wave_instruments:'); |
|
|
379 |
OutSL.Add(RenderInstruments(Song.Instruments.Wave)); |
|
|
380 |
OutSL.Add(''); |
|
|
381 |
|
|
|
382 |
OutSL.Add('noise_instruments:'); |
|
|
383 |
OutSL.Add(RenderInstruments(Song.Instruments.Noise)); |
|
|
384 |
OutSL.Add(''); |
|
|
385 |
|
|
|
386 |
// Render routines |
|
|
387 |
OutSL.Add('routines:'); |
|
|
388 |
for I := Low(TRoutineBank) to High(TRoutineBank) do begin |
|
|
389 |
OutSL.Add('__hUGE_Routine_'+IntToStr(I)+':'); |
|
|
390 |
OutSL.Add(Song.Routines[I]); |
|
|
391 |
OutSL.Add('__end_hUGE_Routine_'+IntToStr(I)+':'); |
|
|
392 |
OutSL.Add('ret'); |
|
|
393 |
OutSL.Add(''); |
|
|
394 |
end; |
|
|
395 |
|
|
|
396 |
// Render waves |
|
|
397 |
OutSL.Add('waves:'); |
|
|
398 |
OutSL.Add(RenderWaveforms(Song.Waves)); |
|
|
399 |
|
|
|
400 |
AssignFile(F, Filename); |
|
|
401 |
Rewrite(F); |
|
|
402 |
Write(F, OutSL.Text); |
|
|
403 |
CloseFile(F); |
|
|
404 |
|
|
|
405 |
OutSL.Free; |
| 409 |
end; |
406 |
end; |
| 410 |
|
407 |
|
| 411 |
procedure RectifyGBSFile(GBSFile: string); |
408 |
procedure RectifyGBSFile(GBSFile: string); |
@@ -433,8 +430,7 @@ begin |
| 433 |
Result := RenderSongToFile(Song, 'preview.gb', emPreview); |
430 |
Result := RenderSongToFile(Song, 'preview.gb', emPreview); |
| 434 |
end; |
431 |
end; |
| 435 |
|
432 |
|
| 436 |
function RenderSongToFile(Song: TSong; Filename: String; |
433 |
function RenderSongToFile(Song: TSong; Filename: String; Mode: TExportMode = emNormal): boolean; |
| 437 |
Mode: TExportMode = emNormal; DescriptorName: String = 'song_descriptor'): boolean; |
|
|
| 438 |
{ TODO: Refactor this whole monstrosity. } |
434 |
{ TODO: Refactor this whole monstrosity. } |
| 439 |
var |
435 |
var |
| 440 |
OutFile: Text; |
436 |
OutFile: Text; |
@@ -444,6 +440,14 @@ var |
| 444 |
FilePath: string; |
440 |
FilePath: string; |
| 445 |
RenameSucceeded: Boolean; |
441 |
RenameSucceeded: Boolean; |
| 446 |
|
442 |
|
|
|
443 |
procedure WriteHTT(F: string; S: string); |
|
|
444 |
begin |
|
|
445 |
AssignFile(OutFile, F); |
|
|
446 |
Rewrite(OutFile); |
|
|
447 |
Write(OutFile, S); |
|
|
448 |
CloseFile(OutFile); |
|
|
449 |
end; |
|
|
450 |
|
| 447 |
function Assemble(OutFile: string; InFile: string; Defines: array of string): integer; |
451 |
function Assemble(OutFile: string; InFile: string; Defines: array of string): integer; |
| 448 |
var |
452 |
var |
| 449 |
Define: string; |
453 |
Define: string; |
@@ -495,66 +499,91 @@ label |
| 495 |
begin |
499 |
begin |
| 496 |
Song := OptimizeSong(Song); |
500 |
Song := OptimizeSong(Song); |
| 497 |
|
501 |
|
| 498 |
RenderSongToHTTs(Song, DescriptorName, Filename); |
502 |
FilePath := Filename; |
|
|
503 |
Filename := ExtractFileNameWithoutExt(ExtractFileNameOnly(Filename)); |
|
|
504 |
|
|
|
505 |
WriteHTT('./hUGEDriver/wave.htt', RenderWaveforms(Song.Waves)); |
|
|
506 |
WriteHTT('./hUGEDriver/order.htt', RenderOrderTable(Song.OrderMatrix)); |
|
|
507 |
WriteHTT('./hUGEDriver/duty_instrument.htt', RenderInstruments(Song.Instruments.Duty)); |
|
|
508 |
WriteHTT('./hUGEDriver/wave_instrument.htt', RenderInstruments(Song.Instruments.Wave)); |
|
|
509 |
WriteHTT('./hUGEDriver/noise_instrument.htt', RenderInstruments(Song.Instruments.Noise)); |
|
|
510 |
for I := Low(TRoutineBank) to High(TRoutineBank) do |
|
|
511 |
WriteHTT('./hUGEDriver/routine'+IntToStr(I)+'.htt', Song.Routines[I]); |
|
|
512 |
|
|
|
513 |
AssignFile(OutFile, './hUGEDriver/pattern.htt'); |
|
|
514 |
Rewrite(OutFile); |
|
|
515 |
|
|
|
516 |
// TODO: Are keys and data defined to be aligned? Seems like they are but |
|
|
517 |
// should probably find out if that's just an implementation detail... |
|
|
518 |
for I := 0 to Song.Patterns.Count - 1 do |
|
|
519 |
if PatternIsUsed(Song.Patterns.Keys[I], Song) then |
|
|
520 |
Write(OutFile, RenderPattern('P' + IntToStr(Song.Patterns.Keys[I]), |
|
|
521 |
Song.Patterns.Data[I]^)); |
|
|
522 |
|
|
|
523 |
CloseFile(OutFile); |
| 499 |
|
524 |
|
| 500 |
// Build the file |
525 |
// Build the file |
| 501 |
Chdir('hUGEDriver'); |
526 |
Chdir('hUGEDriver'); |
| 502 |
|
527 |
|
| 503 |
OutSL := TStringList.Create; |
528 |
OutSL := TStringList.Create; |
| 504 |
Proc := TProcess.Create(nil); |
529 |
Proc := TProcess.Create(nil); |
| 505 |
Proc.Options := Proc.Options + [poWaitOnExit, poUsePipes, poStdErrToOutput, |
530 |
Proc.Options := Proc.Options + [poWaitOnExit, poUsePipes, poStdErrToOutput, poNoConsole]; |
| 506 |
poNoConsole]; |
|
|
| 507 |
|
531 |
|
| 508 |
// Assemble |
532 |
// Assemble |
| 509 |
if Mode = emPreview then |
533 |
if Mode = emPreview then |
| 510 |
begin |
534 |
begin |
| 511 |
if Assemble(Filename + '_driver.obj', 'hUGEDriver.z80', ['PREVIEW_MODE']) <> 0 then |
535 |
if Assemble(Filename + '_driver.obj', 'hUGEDriver.asm', ['PREVIEW_MODE']) <> 0 then |
| 512 |
goto AssemblyError; |
536 |
goto AssemblyError; |
| 513 |
end |
537 |
end |
| 514 |
else |
538 |
else |
| 515 |
begin |
539 |
begin |
| 516 |
if Assemble(Filename + '_driver.obj', 'hUGEDriver.z80', []) <> 0 then |
540 |
if Assemble(Filename + '_driver.obj', 'hUGEDriver.asm', []) <> 0 then |
| 517 |
goto AssemblyError; |
541 |
goto AssemblyError; |
| 518 |
end; |
542 |
end; |
| 519 |
|
543 |
|
| 520 |
if Assemble(Filename + '_song.obj', 'song.z80', ['SONG_DESCRIPTOR=_'+DescriptorName]) <> 0 then |
544 |
if Assemble(Filename + '_song.obj', |
|
|
545 |
'song.asm', |
|
|
546 |
['SONG_DESCRIPTOR=song', 'TICKS='+IntToStr(Song.TicksPerRow)]) <> 0 then |
| 521 |
goto AssemblyError; |
547 |
goto AssemblyError; |
| 522 |
|
548 |
|
| 523 |
if Mode = emGBS then |
549 |
if Mode = emGBS then |
| 524 |
begin |
550 |
begin |
| 525 |
if Assemble(Filename + '_gbs.obj', 'gbs.z80', ['SONG_DESCRIPTOR=_'+DescriptorName]) <> 0 then |
551 |
if Assemble( |
| 526 |
goto AssemblyError; |
552 |
Filename + '_gbs.obj', 'gbs.asm', |
|
|
553 |
['SONG_DESCRIPTOR=song', |
|
|
554 |
'GBS_TITLE='+PadRight(Song.Name, 32), |
|
|
555 |
'GBS_AUTHOR='+PadRight(Song.Artist, 32), |
|
|
556 |
'GBS_COPYRIGHT='+PadRight(IntToStr(CurrentYear), 32)] |
|
|
557 |
) <> 0 then goto AssemblyError; |
| 527 |
end |
558 |
end |
| 528 |
else |
559 |
else |
| 529 |
begin |
560 |
begin |
| 530 |
if Assemble(Filename + '_player.obj', 'player.z80', ['SONG_DESCRIPTOR=_'+DescriptorName]) <> 0 then |
561 |
if Assemble(Filename + '_player.obj', 'player.asm', ['SONG_DESCRIPTOR=song']) <> 0 then |
| 531 |
goto AssemblyError; |
562 |
goto AssemblyError; |
| 532 |
end; |
563 |
end; |
| 533 |
|
564 |
|
| 534 |
if Mode in [emNormal, emPreview, emGBS] then begin |
565 |
// Link |
| 535 |
// Link |
566 |
if Mode = emGBS then |
| 536 |
if Mode = emGBS then |
567 |
begin |
| 537 |
begin |
568 |
if Link(Filename + '.gbs', [Filename + '_driver.obj', Filename + |
| 538 |
if Link(Filename + '.gbs', [Filename + '_driver.obj', Filename + |
569 |
'_song.obj', Filename + '_gbs.obj']) <> 0 then |
| 539 |
'_song.obj', Filename + '_gbs.obj']) <> 0 then |
570 |
goto AssemblyError; |
| 540 |
goto AssemblyError; |
571 |
end |
| 541 |
end |
572 |
else |
| 542 |
else |
573 |
begin |
| 543 |
begin |
574 |
if Link(Filename + '.gb', [Filename + '_driver.obj', Filename + |
| 544 |
if Link(Filename + '.gb', [Filename + '_driver.obj', Filename + |
575 |
'_song.obj', Filename + '_player.obj'], Filename + '.map', Filename + |
| 545 |
'_song.obj', Filename + '_player.obj'], Filename + '.map', Filename + |
576 |
'.sym') <> 0 then |
| 546 |
'.sym') <> 0 then |
577 |
goto AssemblyError; |
| 547 |
goto AssemblyError; |
578 |
end; |
| 548 |
end; |
|
|
| 549 |
|
579 |
|
| 550 |
// Fix |
580 |
// Fix |
| 551 |
if Mode = emGBS then |
581 |
if Mode = emGBS then |
| 552 |
RectifyGBSFile(Filename + '.gbs') |
582 |
RectifyGBSFile(Filename + '.gbs') |
| 553 |
else |
583 |
else |
| 554 |
begin |
584 |
begin |
| 555 |
if (Fix(Filename + '.gb') <> 0) then |
585 |
if (Fix(Filename + '.gb') <> 0) then |
| 556 |
goto AssemblyError; |
586 |
goto AssemblyError; |
| 557 |
end; |
|
|
| 558 |
end; |
587 |
end; |
| 559 |
|
588 |
|
| 560 |
// Move to destination |
589 |
// Move to destination |