// Project: FL_S3M_PLAYER // by Xaby aka Folker, converted from s3mplayer.pdf (by Joel Yliluoma) // C++ to App Game Kit 2 Tier 1 Basic // Beginn : 2017-03-09 // Update : 2017-03-23 //---- legal information --- /* // thanks for the work, I used: // Bisqwit S3M player example for Youtube ― © Joel Yliluoma) // https://www.youtube.com/watch?v=Qvy_qXBHVYY // wxHexEditor // tested with mods from https://modarchive.org/ */ /*---------------* Change History FL /----------------/ Version: 0.1 (no playing at this time) [2017-03-23] Struct -> Type Types global, no classes, so I decided to have as Type S3M_file_type [2017-03-09] Project started, to be able to use S3M-Modules in AGK2 *---------------*/ // show all errors SetErrorMode(2) // set window properties SetWindowTitle( "FL_S3M_PLAYER" ) SetWindowSize( 320, 640, 0 ) // set display properties SetVirtualResolution( 320, 640 ) SetOrientationAllowed( 1, 1, 1, 1 ) SetSyncRate( 30, 0 ) // 30fps instead of 60 to save battery UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts /* From C-help Datentyp Bitanzahl n Wertebereich ---------------------------------------------------------- char 8 -127 .. 127 oder 0 .. 255 short 16 -32767 .. 32767 long 32 -2147483647 .. 2147483647 int 16 oder 32 unsigned char 8 0 .. 255 unsigned short 16 0 .. 65535 unsigned long 32 0 .. 4294967295 unsigned int 16 oder 32 */ // -------- by xaby --------------------------------- // helper functions function GetMemblockUnsignedShort(memID, offset) a = GetMemblockByte( memID, offset ) b = GetMemblockByte( memID, offset+1 ) result = b*256+a // or a*256+b endfunction result //--------------------------------------------------- //--------------- S3M-Loader -------------- Type S3M_file_Header Name$ as String //[28] eofchar as integer // unsigned char / byte 0..255 typ as integer // unsigned char / byte 0..255 dummy as integer [2] // unsigned char / byte 0..255 ordnum as integer //unsigned short / word 0..65535 insnum as integer //unsigned short / word 0..65535 patnum as integer //unsigned short / word 0..65535 flags as integer //unsigned short / word 0..65535 cwtv as integer //unsigned short / word 0..65535 ffi as integer //unsigned short / word 0..65535 scrm$ as String //[4] Vxx as integer // unsigned char / byte 0..255 Axx as integer // unsigned char / byte 0..255 Txx as integer // unsigned char / byte 0..255 Mastervolume as integer // unsigned char / byte 0..255 uc as integer // unsigned char / byte 0..255 dp as integer // unsigned char / byte 0..255 // reading 8 bytes here Tracker$ as String //dummy2 as integer [8] // unsigned char / byte 0..255 special as integer //unsigned short / word 0..65535 channelsettings as integer [32] // unsigned char / byte 0..255 orders as integer [256] // unsigned char / byte 0..255 patdata as integer [100] patend as integer [100] /* // The rest of this struct is filled on demand unsigned char orders[256], *patdata[100], *patend[100]; */ // Notice to me: *Pointer?! how to in AGK2? //--------------- by FL ------------------------- error$ as String // for Error-Handling EndType Type S3M_Instrument_Header typ as Integer // unsigned char / byte 0..255 filename$ as String //[12] filepos as Integer // 3 Byte length as Integer // long loopbegin as Integer // long loopend as Integer // long volume as Integer // Byte dummy as Integer // Byte packflag as Integer // Byte flags as Integer // Byte c4spd as Integer // unsigned long sampledata as Integer // Pointer as Byte c4spd_factor as float // double name$ as String //[28] scri$ as String //[4] //--------------- by FL ------------------------- error$ as String // for Error-Handling EndType Type S3M_Channel last_ins as Integer // unsigned? base_note as Integer // unsigned? sample_offset as float // double vib_offsett as Integer // unsigned? live_period as float // double slideto_period as float // double stable_period as float // double live_hz as float // double volume as Integer // int arpeggio as Integer // unsigned? // Cache Last command parameters last_volumeslide as Integer // as unsigned? last_putchslide as Integer // as unsigned? last_vibrato_hi as Integer // as unsigned? last_vibrato_lo as Integer // as unsigned? last_tremolo_hi as Integer // as unsigned? last_tremolo_lo as Integer // as unsigned? last_portamento as Integer // as unsigned? last_arpeggio as Integer // as unsigned? EndType Function S3M_init_Channel(channel as S3M_Channel) channel.last_ins = 0 channel.base_note = 255 channel.sample_offset = 0 channel.stable_period = 0 channel.volume = 0 channel.arpeggio = 0 EndFunction Channel Type S3M_file_type S3M as S3M_file_Header ins as S3M_Instrument_Header[99] // Array of 99+0 instruments chn as S3M_Channel[32] error$ as String EndType Function LoadS3M(FileName$) S3Mf as S3M_file_type mem=CreateMemblockFromFile(FileName$) // only for small files! // S3M-Header, 96 Bytes S3Mf.S3M.Name$=GetMemblockString(mem,0,28) S3Mf.S3M.eofchar=GetMemblockByte(mem,28) S3Mf.S3M.typ =GetMemblockByte(mem,29) //S3M.dummy1 =GetMemblockByte(mem,30) //S3M.dummy2 =GetMemblockByte(mem,31) S3Mf.S3M.ordnum=GetMemblockUnsignedShort(mem,32) //<= 256, number of orders S3Mf.S3M.insnum=GetMemblockUnsignedShort(mem,34) //<= 99 S3Mf.S3M.patnum=GetMemblockUnsignedShort(mem,36) //<= 100 S3Mf.S3M.flags =GetMemblockUnsignedShort(mem,38) S3Mf.S3M.cwtv =GetMemblockUnsignedShort(mem,40) S3Mf.S3M.ffi =GetMemblockUnsignedShort(mem,42) S3Mf.S3M.scrm$=GetMemblockString(mem,44,4) // "SCRM" S3Mf.S3M.Vxx = GetMemblockByte(mem,48) S3Mf.S3M.Axx = GetMemblockByte(mem,49) S3Mf.S3M.Txx = GetMemblockByte(mem,50) S3Mf.S3M.Mastervolume = GetMemblockByte(mem,51) S3Mf.S3M.uc = GetMemblockByte(mem,52) S3Mf.S3M.dp = GetMemblockByte(mem,53) S3Mf.S3M.Tracker$ = GetMemblockString(mem,54,8) //S3M.dummy[8] .. 61 S3Mf.S3M.special = GetMemblockUnsignedShort(mem,62) For c=0 to 31 S3Mf.S3M.channelsettings[c]=GetMemblockByte(mem,64+c) // 96 Next // filled on demand!!! S3Mf.error$="okay" inspointers as integer [99] patpointers as integer [100] if S3Mf.S3M.ordnum<= 256 if S3Mf.S3M.insnum <=99 if S3Mf.S3M.patnum <=100 For o=0 to S3Mf.S3M.ordnum // max 255 // MemblocktoArray(mem,array,2,count) S3Mf.S3M.orders[o]=GetMemblockByte(mem,96+o) Next mseek=96+o S3Mf.error$=Str(mseek) For in=0 to S3Mf.S3M.insnum // max 255 inspointers[in]=GetMemblockUnsignedShort(mem,mseek+2*in) Next mseek = mseek+2*in S3Mf.error$=S3Mf.error$+" "+Str(mseek) For pa=0 to S3Mf.S3M.patnum // max 255 patpointers[pa]=GetMemblockUnsignedShort(mem,mseek+2*pa) Next mseek=mseek+2*pa S3Mf.error$=S3Mf.error$+" "+Str(mseek) for i=0 to S3Mf.S3M.insnum-1 // Read PCM // S3M-Instrument-Header S3Mf.ins[i].filename$=GetMemblockString(mem,mseek+i*28,28) next else S3Mf.error$ = "Patnum?" endif else S3Mf.error$ = "Insnum?" endif else S3Mf.error$ = "Ordnum?" endif // Here are the 100 Patterndatas and Patternends, but as Pointer? // ... //----------------------------------------- EndFunction s3mf S3Mi as S3M_File_Type // S3Minfo //S3Mi = LoadS3M("grymmfett_-_cestlavie.s3m") //S3Mi = LoadS3M("Absolutly_Empty.S3M") //S3Mi = LoadS3M("Rim Shot 3 - 4 Times C1.S3M") //S3Mi = LoadS3M("aryx.s3m") //S3Mi = LoadS3M("chip-chop.s3m") //S3Mi = LoadS3M("voom.s3m") //S3Mi = LoadS3M("the_last_cow.s3m") //S3Mi = LoadS3M("mman6_f.s3m") S3Mi = LoadS3M("dune_air.s3m") SetPrintSize(22) do Print( ScreenFPS() ) Print("Name: "+S3Mi.S3M.Name$) Print("Instruments: "+Str(S3Mi.S3M.insnum)) Print("S3M-Type : "+S3Mi.S3M.scrm$+" Tracker: "+S3Mi.S3M.Tracker$) Print("Special : "+Str(S3Mi.S3M.special)) Print("Ch1-Setting: "+Str(S3Mi.S3M.channelsettings[0])) Print("S3M-Orders : "+Str(S3Mi.S3M.ordnum)) for k=0 to S3Mi.S3M.insnum-1 Print("Inst "+Str(k)+": "+S3Mi.ins[k].filename$) Next Print("Errors : "+S3Mi.error$) Sync() loop