Commit cec038b

Nick committed on
Broken WIP deflemask import
commit cec038bdeb1d79d88ba90fe32a20f9337586b865 parent 2b8e91d
5 changed files +243−6
Addeddmfimport.pas +206−0
@@ -0,0 +1,206 @@
1 unit DMFImport;
2
3 {$mode objfpc}{$H+}
4
5 interface
6
7 uses
8 Classes, SysUtils, ZStream, Song, hUGEDataTypes;
9
10 type
11 EDMFException = class(Exception);
12
13 function LoadSongFromDmfStream(Stream: TStream): TSong;
14
15 implementation
16
17 type
18
19 { TDMFStream }
20
21 TDMFStream = class(Tdecompressionstream)
22 function ReadDMFString: String;
23 end;
24
25 function LoadSongFromDmfStream(Stream: TStream): TSong;
26 var
27 DS: TDMFStream;
28 I, J, Row: Integer;
29 TotalInstruments, TotalWavetables: Integer;
30 Temp: QWord;
31 TempS: String;
32 Pat: PPattern;
33 begin
34 InitializeSong(Result);
35
36 DS := TDMFStream.create(Stream);
37
38 //FORMAT FLAGS
39 // 16 Bytes: Format String, must be ".DelekDefleMask."
40 DS.Seek(16, soCurrent);
41 // 1 Byte: File Version, must be 24 (0x18) for DefleMask v0.12.0
42 Temp := DS.ReadByte;
43 Writeln(temp);
44 if Temp <> 24 then
45 raise EDMFException.Create('DMF file was invalid version!');
46
47 //SYSTEM SET
48 //1 Byte: System:
49 //SYSTEM_GAMEBOY 0x04 (SYSTEM_TOTAL_CHANNELS 4)
50 if DS.ReadByte <> $04 then
51 raise EDMFException.Create('DMF file is not a Game Boy module!');
52
53 //VISUAL INFORMATION
54 // 1 Byte: Song Name Chars Count (0-255)
55 // N Bytes: Song Name Chars
56 Result.Name := DS.ReadDMFString;
57 writeln(result.name);
58
59 // 1 Byte: Song Author Chars Count (0-255)
60 // N Bytes: Song Author Chars
61 Result.Artist := DS.ReadDMFString;
62 writeln(result.artist);
63
64 // 1 Byte: Highlight A in patterns
65 // 1 Byte: Highlight B in patterns
66 DS.Seek(2, soCurrent);
67
68 //MODULE INFORMATION
69 //1 Byte: Time Base
70 DS.Seek(1, soCurrent);
71 //1 Byte: Tick Time 1
72 //1 Byte: Tick Time 2
73 Result.TicksPerRow := DS.ReadByte;
74 DS.Seek(1, soCurrent);
75 //1 Byte: Frames Mode (0 = PAL, 1 = NTSC)
76 //1 Byte: Using Custom HZ (If set to 1, NTSC or PAL is ignored)
77 //1 Byte: Custom HZ value 1
78 //1 Byte: Custom HZ value 2
79 //1 Byte: Custom HZ value 3
80 DS.Seek(5, soCurrent);
81 //4 Bytes: TOTAL_ROWS_PER_PATTERN
82 Temp := DS.ReadQWord;
83 Writeln(temp);
84 if Temp <> 64 then
85 raise EDMFException.Create('DMF file needs 64 rows per pattern!');
86 //1 Byte: TOTAL_ROWS_IN_PATTERN_MATRIX
87 Temp := DS.ReadByte;
88 for I := Low(Result.OrderMatrix) to High(Result.OrderMatrix) do
89 SetLength(Result.OrderMatrix[I], Temp);
90
91 //PATTERN MATRIX VALUES (A matrix of SYSTEM_TOTAL_CHANNELS x TOTAL_ROWS_IN_PATTERN_MATRIX)
92 for I := Low(Result.OrderMatrix) to High(Result.OrderMatrix) do
93 for J := Low(Result.OrderMatrix[I]) to High(Result.OrderMatrix[I]) do
94 //1 Byte: Pattern Matrix Value: (Index from SYSTEM_TOTAL_CHANNELS loop, Index from TOTAL_ROWS_IN_PATTERN_MATRIX loop)
95 Result.OrderMatrix[I, J] := (100*(I+1))+DS.ReadByte;
96 //1 Byte: TOTAL_INSTRUMENTS
97 TotalInstruments := DS.ReadByte;
98 if TotalInstruments > 15 then
99 raise EDMFException.Create('DMF file must have 15 instruments or less!');
100
101 // Read all instruments
102 for I := 0 to TotalInstruments-1 do begin
103 // 1 Byte: Instrument Name Chars Count (0-255)
104 // N Bytes: Instrument Name Chars
105 TempS := DS.ReadDMFString;
106 Result.Instruments.Duty[I].Name := TempS;
107 Result.Instruments.Wave[I].Name := TempS;
108 Result.Instruments.Noise[I].Name := TempS;
109
110 // 1 Byte: Instrument Mode (0 = STANDARD INS, 1 = FM INS)
111 if DS.ReadByte <> 1 then
112 raise EDMFException.Create('DMF file contains a non-standard instrument!');
113
114 //ARPEGGIO MACRO
115 //1 Byte: ENVELOPE_SIZE (0 - 127)
116 //Repeat this ENVELOPE_SIZE times
117 //4 Bytes: ENVELOPE_VALUE (signed int, offset=12)
118 //IF ENVELOPE_SIZE > 0
119 //1 Byte: LOOP_POSITION (-1 = NO LOOP)
120 //1 Byte: ARPEGGIO MACRO MODE (0 = Normal, 1 = Fixed)
121 Temp := DS.ReadByte;
122 DS.Seek(4*Temp, soCurrent);
123 if Temp > 0 then
124 DS.Seek(1, soCurrent);
125 DS.Seek(1, soCurrent);
126
127 //DUTY/NOISE MACRO
128 //1 Byte: ENVELOPE_SIZE (0 - 127)
129 //Repeat this ENVELOPE_SIZE times
130 //4 Bytes: ENVELOPE_VALUE
131 //IF ENVELOPE_SIZE > 0
132 //1 Byte: LOOP_POSITION (-1 = NO LOOP)
133 Temp := DS.ReadByte;
134 DS.Seek(4*Temp, soCurrent);
135 if Temp > 0 then
136 DS.Seek(1, soCurrent);
137
138 //WAVETABLE MACRO
139 //1 Byte: ENVELOPE_SIZE (0 - 127)
140 //Repeat this ENVELOPE_SIZE times
141 //4 Bytes: ENVELOPE_VALUE
142 //IF ENVELOPE_SIZE > 0
143 //1 Byte: LOOP_POSITION (-1 = NO LOOP)
144 Temp := DS.ReadByte;
145 DS.Seek(4*Temp, soCurrent);
146 if Temp > 0 then
147 DS.Seek(1, soCurrent);
148
149 //1 Byte: Envelope Volume
150 //1 Byte: Envelope Direction
151 //1 Byte: Envelope Length
152 //1 Byte: Sound Length
153 DS.Seek(4, soCurrent);
154 end;
155
156 //WAVETABLES DATA
157 //1 Byte: TOTAL_WAVETABLES
158 //Repeat this TOTAL_WAVETABLES times
159 // 4 Bytes: WAVETABLE_SIZE
160 // Repeat this WAVETABLE_SIZE times
161 // 4 Bytes: Wavetable Data
162 TotalWavetables := DS.ReadByte;
163 for I := 0 to TotalWavetables-1 do
164 DS.Seek(DS.ReadQWord * 4, soCurrent);
165
166 for I := Low(TOrderMatrix) to High(TOrderMatrix) do begin
167 if DS.ReadByte <> 1 then
168 raise EDMFException.Create('DMF file contains patterns with more than one effect column!');
169
170 for J := Low(Result.OrderMatrix[I]) to High(Result.OrderMatrix[I]) do begin
171 Pat := Result.Patterns.GetOrCreateNew(((I+1)*100) + J);
172 for Row := Low(TPattern) to High(TPattern) do begin
173 with Pat^[Row] do begin
174 Note := DS.ReadWord + (12*DS.ReadWord);
175 EffectCode := DS.ReadWord;
176 EffectParams.Value := DS.ReadWord;
177 Instrument := DS.ReadWord;
178 end;
179 end;
180 end;
181 end;
182
183 DS.Free;
184 end;
185
186 { TDMFStream }
187
188 function TDMFStream.ReadDMFString: String;
189 Var
190 TheSize : Byte;
191 P : PByte ;
192 begin
193 ReadBuffer (TheSize,SizeOf(TheSize));
194 SetLength(Result,TheSize);
195 // Illegal typecast if no AnsiStrings defined.
196 if TheSize>0 then
197 begin
198 ReadBuffer (Pointer(Result)^,TheSize);
199 P:=Pointer(Result)+TheSize;
200 p^:=0;
201 end;
202 end;
203
204
205 end.
206
ModifiedhUGETracker.lpi +6−1
@@ -221,7 +221,7 @@
221 <PackageName Value="LCL"/> 221 <PackageName Value="LCL"/>
222 </Item5> 222 </Item5>
223 </RequiredPackages> 223 </RequiredPackages>
224 <Units Count="25"> 224 <Units Count="26">
225 <Unit0> 225 <Unit0>
226 <Filename Value="hUGETracker.lpr"/> 226 <Filename Value="hUGETracker.lpr"/>
227 <IsPartOfProject Value="True"/> 227 <IsPartOfProject Value="True"/>
@@ -353,6 +353,11 @@
353 <IsPartOfProject Value="True"/> 353 <IsPartOfProject Value="True"/>
354 <UnitName Value="BeepBoxImport"/> 354 <UnitName Value="BeepBoxImport"/>
355 </Unit24> 355 </Unit24>
356 <Unit25>
357 <Filename Value="dmfimport.pas"/>
358 <IsPartOfProject Value="True"/>
359 <UnitName Value="DMFImport"/>
360 </Unit25>
356 </Units> 361 </Units>
357 </ProjectOptions> 362 </ProjectOptions>
358 <CompilerOptions> 363 <CompilerOptions>
Modifiedmodimport.pas +4−4
@@ -8,6 +8,10 @@ uses
8 Classes, SysUtils, Song, hugedatatypes, fgl, math, constants, 8 Classes, SysUtils, Song, hugedatatypes, fgl, math, constants,
9 instruments; 9 instruments;
10 10
11 function LoadSongFromModStream(Stream: TStream): TSong;
12
13 implementation
14
11 type 15 type
12 TPeriodToCodeMap = specialize TFPGMap<Integer, Integer>; 16 TPeriodToCodeMap = specialize TFPGMap<Integer, Integer>;
13 17
@@ -64,8 +68,6 @@ type
64 Patterns: array of TMODPattern; 68 Patterns: array of TMODPattern;
65 end; 69 end;
66 70
67 function LoadSongFromModStream(Stream: TStream): TSong;
68
69 const 71 const
70 // https://github.com/AntonioND/gbt-player/blob/master/rgbds_example/gbt_player_bank1.asm 72 // https://github.com/AntonioND/gbt-player/blob/master/rgbds_example/gbt_player_bank1.asm
71 GBT_WAVEFORMS: array[0..7] of array[0..15] of Byte = 73 GBT_WAVEFORMS: array[0..7] of array[0..15] of Byte =
@@ -81,8 +83,6 @@ const
81 var 83 var
82 PeriodToCodeMap: TPeriodToCodeMap; 84 PeriodToCodeMap: TPeriodToCodeMap;
83 85
84 implementation
85
86 function RawRowToRegularRow(RawRow: TMODRawRow): TMODRow; 86 function RawRowToRegularRow(RawRow: TMODRawRow): TMODRow;
87 begin 87 begin
88 Result.Note := ((RawRow[0] and %1111) shl 8) or RawRow[1]; 88 Result.Note := ((RawRow[0] and %1111) shl 8) or RawRow[1];
Modifiedtracker.lfm +4−0
@@ -1912,6 +1912,10 @@ object frmTracker: TfrmTracker
1912 Caption = 'Import GBT Player ...' 1912 Caption = 'Import GBT Player ...'
1913 OnClick = MenuItem34Click 1913 OnClick = MenuItem34Click
1914 end 1914 end
1915 object MenuItem41: TMenuItem
1916 Caption = 'Import DefleMask ...'
1917 OnClick = MenuItem41Click
1918 end
1915 object MenuItem35: TMenuItem 1919 object MenuItem35: TMenuItem
1916 Caption = '-' 1920 Caption = '-'
1917 end 1921 end
Modifiedtracker.pas +23−1
@@ -10,7 +10,8 @@ uses
10 sound, vars, machine, about_hugetracker, TrackerGrid, lclintf, lmessages, 10 sound, vars, machine, about_hugetracker, TrackerGrid, lclintf, lmessages,
11 Buttons, Grids, DBCtrls, HugeDatatypes, LCLType, Clipbrd, RackCtls, Codegen, 11 Buttons, Grids, DBCtrls, HugeDatatypes, LCLType, Clipbrd, RackCtls, Codegen,
12 SymParser, options, bgrabitmap, effecteditor, RenderToWave, 12 SymParser, options, bgrabitmap, effecteditor, RenderToWave,
13 modimport, beepboximport, mainloop, strutils, Types, Keymap, hUGESettings; 13 modimport, beepboximport, mainloop, strutils, Types, Keymap, hUGESettings,
14 dmfimport;
14 15
15 // TODO: Move to config file? 16 // TODO: Move to config file?
16 const 17 const
@@ -43,6 +44,7 @@ type
43 ExportCMenuItem: TMenuItem; 44 ExportCMenuItem: TMenuItem;
44 MenuItem40: TMenuItem; 45 MenuItem40: TMenuItem;
45 ExportAsmMenuItem: TMenuItem; 46 ExportAsmMenuItem: TMenuItem;
47 MenuItem41: TMenuItem;
46 MenuItem43: TMenuItem; 48 MenuItem43: TMenuItem;
47 MenuItem44: TMenuItem; 49 MenuItem44: TMenuItem;
48 MenuItem54: TMenuItem; 50 MenuItem54: TMenuItem;
@@ -288,6 +290,7 @@ type
288 procedure MenuItem37Click(Sender: TObject); 290 procedure MenuItem37Click(Sender: TObject);
289 procedure MenuItem38Click(Sender: TObject); 291 procedure MenuItem38Click(Sender: TObject);
290 procedure ExportAsmMenuItemClick(Sender: TObject); 292 procedure ExportAsmMenuItemClick(Sender: TObject);
293 procedure MenuItem41Click(Sender: TObject);
291 procedure NoiseMacroPaintboxMouseDown(Sender: TObject; 294 procedure NoiseMacroPaintboxMouseDown(Sender: TObject;
292 Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 295 Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
293 procedure NoiseMacroPaintboxMouseMove(Sender: TObject; Shift: TShiftState; 296 procedure NoiseMacroPaintboxMouseMove(Sender: TObject; Shift: TShiftState;
@@ -1814,6 +1817,24 @@ begin
1814 ); 1817 );
1815 end; 1818 end;
1816 1819
1820 procedure TfrmTracker.MenuItem41Click(Sender: TObject);
1821 var
1822 Stream: TStream;
1823 begin
1824 if not CheckUnsavedChanges then Exit;
1825
1826 if True then begin
1827 DestroySong(Song);
1828
1829 Stream := TFileStream.Create('C:/test/pokemon2.dmf', fmOpenRead);
1830 Song := LoadSongFromDmfStream(Stream);
1831
1832 Stream.Free;
1833
1834 UpdateUIAfterLoad('Bargus');
1835 end;
1836 end;
1837
1817 procedure TfrmTracker.NoiseMacroPaintboxMouseDown(Sender: TObject; 1838 procedure TfrmTracker.NoiseMacroPaintboxMouseDown(Sender: TObject;
1818 Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 1839 Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
1819 begin 1840 begin
@@ -2268,6 +2289,7 @@ begin
2268 if MODOpenDialog.Execute then begin 2289 if MODOpenDialog.Execute then begin
2269 DestroySong(Song); 2290 DestroySong(Song);
2270 2291
2292 // TODO: Add error checking
2271 Stream := TFileStream.Create(MODOpenDialog.FileName, fmOpenRead); 2293 Stream := TFileStream.Create(MODOpenDialog.FileName, fmOpenRead);
2272 Song := LoadSongFromModStream(Stream); 2294 Song := LoadSongFromModStream(Stream);
2273 2295