From c53dd4b735e2801b37e91c7f40f82bb936c43dbc Mon Sep 17 00:00:00 2001 From: Thomas Jepp Date: Mon, 16 Sep 2024 03:52:25 +0100 Subject: [PATCH] adplug: update upstream library and add OPL synth options * Updated adplug library to https://github.com/adplug/adplug/commit/a6706ba256976eedf73e06d67ad74c5985271c1f (current master as of 2024-09-16) * Removed surround option - changed to automatically apply based on synth type * Set default synth sample rate to the same as a real OPL chip * Added support for more synth types * Set the default synth to Nuked OPL3 --- osx/deadbeef.xcodeproj/project.pbxproj | 1206 +++--- plugins/adplug/adplug-db.cpp | 81 +- plugins/adplug/adplug/a2m-v2.cpp | 4034 +++++++++++++++++++ plugins/adplug/adplug/a2m-v2.h | 868 +++++ plugins/adplug/adplug/a2m.cpp | 520 +-- plugins/adplug/adplug/a2m.h | 42 +- plugins/adplug/adplug/adl.cpp | 4490 ++++++++++++---------- plugins/adplug/adplug/adl.h | 27 +- plugins/adplug/adplug/adlib.cpp | 708 ---- plugins/adplug/adplug/adlib.h | 175 - plugins/adplug/adplug/adlibemu.c | 114 +- plugins/adplug/adplug/adlibemu.h | 54 +- plugins/adplug/adplug/adplug.cpp | 17 +- plugins/adplug/adplug/amd.cpp | 236 +- plugins/adplug/adplug/amd.h | 13 +- plugins/adplug/adplug/bam.cpp | 2 +- plugins/adplug/adplug/bmf.cpp | 788 ++-- plugins/adplug/adplug/bmf.h | 3 +- plugins/adplug/adplug/cff.cpp | 398 +- plugins/adplug/adplug/cff.h | 19 +- plugins/adplug/adplug/cmf.cpp | 39 +- plugins/adplug/adplug/cmfmcsop.cpp | 22 +- plugins/adplug/adplug/cmfmcsop.h | 12 +- plugins/adplug/adplug/coktel.cpp | 272 ++ plugins/adplug/adplug/coktel.h | 105 + plugins/adplug/adplug/composer.cpp | 646 ++++ plugins/adplug/adplug/composer.h | 239 ++ plugins/adplug/adplug/d00.cpp | 143 +- plugins/adplug/adplug/d00.h | 1 + plugins/adplug/adplug/database.cpp | 9 +- plugins/adplug/adplug/depack.c | 201 + plugins/adplug/adplug/depack.h | 30 + plugins/adplug/adplug/dfm.cpp | 18 +- plugins/adplug/adplug/dfm.h | 4 +- plugins/adplug/adplug/dmo.cpp | 309 +- plugins/adplug/adplug/dmo.h | 15 +- plugins/adplug/adplug/dro.cpp | 14 +- plugins/adplug/adplug/dro.h | 6 +- plugins/adplug/adplug/dro2.cpp | 14 +- plugins/adplug/adplug/dro2.h | 8 +- plugins/adplug/adplug/dtm.cpp | 254 +- plugins/adplug/adplug/dtm.h | 15 +- plugins/adplug/adplug/emuopl.cpp | 8 +- plugins/adplug/adplug/emuopl.h | 2 +- plugins/adplug/adplug/flash.cpp | 211 +- plugins/adplug/adplug/flash.h | 8 +- plugins/adplug/adplug/fmc.cpp | 30 +- plugins/adplug/adplug/fmopl.c | 70 +- plugins/adplug/adplug/fmopl.h | 4 +- plugins/adplug/adplug/got.h | 5 + plugins/adplug/adplug/herad.cpp | 212 +- plugins/adplug/adplug/herad.h | 10 +- plugins/adplug/adplug/hsc.cpp | 11 +- plugins/adplug/adplug/hybrid.cpp | 6 +- plugins/adplug/adplug/hyp.cpp | 2 +- plugins/adplug/adplug/hyp.h | 2 +- plugins/adplug/adplug/imf.cpp | 109 +- plugins/adplug/adplug/jbm.cpp | 13 +- plugins/adplug/adplug/jbm.h | 4 +- plugins/adplug/adplug/kemuopl.cpp | 70 + plugins/adplug/adplug/kemuopl.h | 40 +- plugins/adplug/adplug/ksm.cpp | 13 +- plugins/adplug/adplug/lds.cpp | 33 +- plugins/adplug/adplug/load_helper.h | 58 + plugins/adplug/adplug/mdi.cpp | 46 +- plugins/adplug/adplug/mdi.h | 13 +- plugins/adplug/adplug/mid.cpp | 203 +- plugins/adplug/adplug/mid.h | 6 +- plugins/adplug/adplug/mididata.h | 6 +- plugins/adplug/adplug/mkj.cpp | 51 +- plugins/adplug/adplug/mkj.h | 4 + plugins/adplug/adplug/msc.cpp | 2 +- plugins/adplug/adplug/msc.h | 2 +- plugins/adplug/adplug/mtk.cpp | 139 +- plugins/adplug/adplug/mtk.h | 2 +- plugins/adplug/adplug/mtr.cpp | 226 ++ plugins/adplug/adplug/mtr.h | 48 + plugins/adplug/adplug/mus.cpp | 349 +- plugins/adplug/adplug/mus.h | 34 +- plugins/adplug/adplug/nemuopl.cpp | 9 +- plugins/adplug/adplug/nemuopl.h | 1 + plugins/adplug/adplug/nukedopl.c | 1385 ++++--- plugins/adplug/adplug/nukedopl.h | 244 +- plugins/adplug/adplug/opl.h | 5 +- plugins/adplug/adplug/pch.h | 11 + plugins/adplug/adplug/pis.cpp | 697 ++++ plugins/adplug/adplug/pis.h | 153 + plugins/adplug/adplug/player.h | 4 + plugins/adplug/adplug/protrack.cpp | 59 +- plugins/adplug/adplug/psi.cpp | 173 +- plugins/adplug/adplug/psi.h | 20 +- plugins/adplug/adplug/rad.cpp | 125 - plugins/adplug/adplug/rad2.cpp | 1936 ++++++++++ plugins/adplug/adplug/rad2.h | 64 + plugins/adplug/adplug/rat.cpp | 213 +- plugins/adplug/adplug/rat.h | 8 +- plugins/adplug/adplug/raw.cpp | 6 +- plugins/adplug/adplug/realopl.cpp | 4 +- plugins/adplug/adplug/rix.cpp | 106 +- plugins/adplug/adplug/rix.h | 6 +- plugins/adplug/adplug/rol.cpp | 474 +-- plugins/adplug/adplug/rol.h | 177 +- plugins/adplug/adplug/s3m.cpp | 787 ++-- plugins/adplug/adplug/s3m.h | 13 +- plugins/adplug/adplug/sa2.cpp | 135 +- plugins/adplug/adplug/sa2.h | 4 +- plugins/adplug/adplug/sixdepack.cpp | 237 ++ plugins/adplug/adplug/sixdepack.h | 71 + plugins/adplug/adplug/sng.cpp | 4 +- plugins/adplug/adplug/sop.cpp | 47 +- plugins/adplug/adplug/sop.h | 6 +- plugins/adplug/adplug/strnlen.h | 60 + plugins/adplug/adplug/surroundopl.cpp | 90 +- plugins/adplug/adplug/surroundopl.h | 15 +- plugins/adplug/adplug/u6m.cpp | 218 +- plugins/adplug/adplug/u6m.h | 27 +- plugins/adplug/adplug/unlzh.c | 454 +++ plugins/adplug/adplug/unlzh.h | 19 + plugins/adplug/adplug/unlzss.c | 117 + plugins/adplug/adplug/unlzss.h | 42 + plugins/adplug/adplug/unlzw.c | 173 + plugins/adplug/adplug/{rad.h => unlzw.h} | 46 +- plugins/adplug/adplug/version.h | 2 +- plugins/adplug/adplug/vgm.cpp | 18 +- plugins/adplug/adplug/vgm.h | 9 +- plugins/adplug/adplug/wemuopl.h | 10 +- plugins/adplug/adplug/woodyopl.cpp | 2 +- plugins/adplug/adplug/xad.cpp | 8 +- plugins/adplug/adplug/xsm.cpp | 29 +- plugins/adplug/adplug/xsm.h | 3 + plugins/adplug/plugin.c | 9 +- 131 files changed, 18393 insertions(+), 8365 deletions(-) create mode 100644 plugins/adplug/adplug/a2m-v2.cpp create mode 100644 plugins/adplug/adplug/a2m-v2.h delete mode 100644 plugins/adplug/adplug/adlib.cpp delete mode 100644 plugins/adplug/adplug/adlib.h create mode 100644 plugins/adplug/adplug/coktel.cpp create mode 100644 plugins/adplug/adplug/coktel.h create mode 100644 plugins/adplug/adplug/composer.cpp create mode 100644 plugins/adplug/adplug/composer.h create mode 100644 plugins/adplug/adplug/depack.c create mode 100644 plugins/adplug/adplug/depack.h create mode 100644 plugins/adplug/adplug/kemuopl.cpp create mode 100644 plugins/adplug/adplug/load_helper.h create mode 100644 plugins/adplug/adplug/mtr.cpp create mode 100644 plugins/adplug/adplug/mtr.h create mode 100644 plugins/adplug/adplug/pch.h create mode 100644 plugins/adplug/adplug/pis.cpp create mode 100644 plugins/adplug/adplug/pis.h delete mode 100644 plugins/adplug/adplug/rad.cpp create mode 100644 plugins/adplug/adplug/rad2.cpp create mode 100644 plugins/adplug/adplug/rad2.h create mode 100644 plugins/adplug/adplug/sixdepack.cpp create mode 100644 plugins/adplug/adplug/sixdepack.h create mode 100644 plugins/adplug/adplug/strnlen.h create mode 100644 plugins/adplug/adplug/unlzh.c create mode 100644 plugins/adplug/adplug/unlzh.h create mode 100644 plugins/adplug/adplug/unlzss.c create mode 100644 plugins/adplug/adplug/unlzss.h create mode 100644 plugins/adplug/adplug/unlzw.c rename plugins/adplug/adplug/{rad.h => unlzw.h} (57%) diff --git a/osx/deadbeef.xcodeproj/project.pbxproj b/osx/deadbeef.xcodeproj/project.pbxproj index 459d7141e4..6eb96f857b 100644 --- a/osx/deadbeef.xcodeproj/project.pbxproj +++ b/osx/deadbeef.xcodeproj/project.pbxproj @@ -2107,146 +2107,6 @@ 2DBF3D4427086AA900023138 /* MainContentViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DBF3D4227086AA900023138 /* MainContentViewController.m */; }; 2DBF3DC4270A101000023138 /* medialibstate.c in Sources */ = {isa = PBXBuildFile; fileRef = 2DBF3DB1270A0D0200023138 /* medialibstate.c */; }; 2DBF3DC5270A101400023138 /* medialibstate.c in Sources */ = {isa = PBXBuildFile; fileRef = 2DBF3DB1270A0D0200023138 /* medialibstate.c */; }; - 2DC1CDD0240EEBA5000776DB /* rol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD44240EEB8F000776DB /* rol.cpp */; }; - 2DC1CDD1240EEBA5000776DB /* nukedopl.c in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD45240EEB8F000776DB /* nukedopl.c */; }; - 2DC1CDD2240EEBA5000776DB /* vgm.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD46240EEB8F000776DB /* vgm.h */; }; - 2DC1CDD3240EEBA5000776DB /* temuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD47240EEB8F000776DB /* temuopl.h */; }; - 2DC1CDD4240EEBA5000776DB /* sa2.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD48240EEB90000776DB /* sa2.h */; }; - 2DC1CDD5240EEBA5000776DB /* msc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD49240EEB90000776DB /* msc.cpp */; }; - 2DC1CDD6240EEBA5000776DB /* adlibemu.c in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD4A240EEB90000776DB /* adlibemu.c */; }; - 2DC1CDD7240EEBA5000776DB /* d00.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD4B240EEB90000776DB /* d00.h */; }; - 2DC1CDD8240EEBA5000776DB /* amd.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD4C240EEB90000776DB /* amd.h */; }; - 2DC1CDD9240EEBA5000776DB /* mid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD4D240EEB90000776DB /* mid.cpp */; }; - 2DC1CDDA240EEBA5000776DB /* got.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD4E240EEB91000776DB /* got.cpp */; }; - 2DC1CDDB240EEBA5000776DB /* fprovide.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD4F240EEB91000776DB /* fprovide.h */; }; - 2DC1CDDC240EEBA5000776DB /* diskopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD50240EEB91000776DB /* diskopl.h */; }; - 2DC1CDDD240EEBA5000776DB /* opl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD51240EEB91000776DB /* opl.h */; }; - 2DC1CDDE240EEBA5000776DB /* cff.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD52240EEB91000776DB /* cff.cpp */; }; - 2DC1CDDF240EEBA5000776DB /* sa2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD53240EEB91000776DB /* sa2.cpp */; }; - 2DC1CDE0240EEBA5000776DB /* adtrack.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD54240EEB91000776DB /* adtrack.h */; }; - 2DC1CDE1240EEBA5000776DB /* a2m.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD55240EEB92000776DB /* a2m.h */; }; - 2DC1CDE2240EEBA5000776DB /* nemuopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD56240EEB92000776DB /* nemuopl.cpp */; }; - 2DC1CDE3240EEBA5000776DB /* cmf.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD57240EEB92000776DB /* cmf.h */; }; - 2DC1CDE4240EEBA5000776DB /* players.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD58240EEB92000776DB /* players.h */; }; - 2DC1CDE5240EEBA5000776DB /* protrack.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD59240EEB92000776DB /* protrack.h */; }; - 2DC1CDE6240EEBA5000776DB /* u6m.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD5A240EEB92000776DB /* u6m.h */; }; - 2DC1CDE7240EEBA5000776DB /* cff.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD5B240EEB93000776DB /* cff.h */; }; - 2DC1CDE8240EEBA5000776DB /* xad.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD5C240EEB93000776DB /* xad.h */; }; - 2DC1CDE9240EEBA5000776DB /* adl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD5D240EEB93000776DB /* adl.h */; }; - 2DC1CDEA240EEBA5000776DB /* emuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD5E240EEB93000776DB /* emuopl.h */; }; - 2DC1CDEB240EEBA5000776DB /* hsc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD5F240EEB93000776DB /* hsc.cpp */; }; - 2DC1CDEC240EEBA5000776DB /* dro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD60240EEB94000776DB /* dro.cpp */; }; - 2DC1CDED240EEBA5000776DB /* raw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD61240EEB94000776DB /* raw.cpp */; }; - 2DC1CDEE240EEBA5000776DB /* hyp.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD62240EEB94000776DB /* hyp.h */; }; - 2DC1CDEF240EEBA5000776DB /* dfm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD63240EEB94000776DB /* dfm.cpp */; }; - 2DC1CDF0240EEBA5000776DB /* rad.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD64240EEB94000776DB /* rad.h */; }; - 2DC1CDF1240EEBA5000776DB /* realopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD65240EEB94000776DB /* realopl.h */; }; - 2DC1CDF2240EEBA5000776DB /* imf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD66240EEB95000776DB /* imf.cpp */; }; - 2DC1CDF3240EEBA5000776DB /* sop.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD67240EEB95000776DB /* sop.h */; }; - 2DC1CDF4240EEBA5000776DB /* fmopl.c in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD68240EEB95000776DB /* fmopl.c */; }; - 2DC1CDF5240EEBA5000776DB /* debug.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD69240EEB95000776DB /* debug.h */; }; - 2DC1CDF6240EEBA5000776DB /* sng.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD6A240EEB95000776DB /* sng.cpp */; }; - 2DC1CDF7240EEBA5000776DB /* bmf.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD6B240EEB95000776DB /* bmf.h */; }; - 2DC1CDF8240EEBA5000776DB /* mad.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD6C240EEB95000776DB /* mad.h */; }; - 2DC1CDF9240EEBA5000776DB /* woodyopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD6D240EEB96000776DB /* woodyopl.cpp */; }; - 2DC1CDFA240EEBA5000776DB /* mtk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD6E240EEB96000776DB /* mtk.cpp */; }; - 2DC1CDFB240EEBA5000776DB /* s3m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD6F240EEB96000776DB /* s3m.cpp */; }; - 2DC1CDFC240EEBA5000776DB /* analopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD70240EEB96000776DB /* analopl.cpp */; }; - 2DC1CDFD240EEBA5000776DB /* nemuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD71240EEB96000776DB /* nemuopl.h */; }; - 2DC1CDFE240EEBA5000776DB /* cmf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD72240EEB96000776DB /* cmf.cpp */; }; - 2DC1CDFF240EEBA5000776DB /* dfm.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD73240EEB97000776DB /* dfm.h */; }; - 2DC1CE00240EEBA5000776DB /* adlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD74240EEB97000776DB /* adlib.cpp */; }; - 2DC1CE01240EEBA5000776DB /* msc.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD75240EEB97000776DB /* msc.h */; }; - 2DC1CE02240EEBA5000776DB /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD76240EEB97000776DB /* version.h */; }; - 2DC1CE03240EEBA5000776DB /* mdi.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD77240EEB97000776DB /* mdi.h */; }; - 2DC1CE04240EEBA5000776DB /* player.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD78240EEB97000776DB /* player.h */; }; - 2DC1CE05240EEBA5000776DB /* rat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD79240EEB97000776DB /* rat.cpp */; }; - 2DC1CE06240EEBA5000776DB /* fmopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD7A240EEB98000776DB /* fmopl.h */; }; - 2DC1CE07240EEBA5000776DB /* lds.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD7B240EEB98000776DB /* lds.cpp */; }; - 2DC1CE08240EEBA5000776DB /* realopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD7C240EEB98000776DB /* realopl.cpp */; }; - 2DC1CE09240EEBA5000776DB /* dmo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD7D240EEB98000776DB /* dmo.cpp */; }; - 2DC1CE0A240EEBA5000776DB /* s3m.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD7E240EEB98000776DB /* s3m.h */; }; - 2DC1CE0B240EEBA5000776DB /* ksm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD7F240EEB98000776DB /* ksm.cpp */; }; - 2DC1CE0C240EEBA5000776DB /* xsm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD80240EEB99000776DB /* xsm.cpp */; }; - 2DC1CE0D240EEBA5000776DB /* wemuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD81240EEB99000776DB /* wemuopl.h */; }; - 2DC1CE0E240EEBA5000776DB /* database.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD82240EEB99000776DB /* database.h */; }; - 2DC1CE0F240EEBA5000776DB /* psi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD83240EEB99000776DB /* psi.cpp */; }; - 2DC1CE10240EEBA5000776DB /* fmc.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD84240EEB99000776DB /* fmc.h */; }; - 2DC1CE11240EEBA5000776DB /* mkj.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD85240EEB99000776DB /* mkj.h */; }; - 2DC1CE12240EEBA5000776DB /* ksm.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD86240EEB9A000776DB /* ksm.h */; }; - 2DC1CE13240EEBA5000776DB /* mididata.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD87240EEB9A000776DB /* mididata.h */; }; - 2DC1CE14240EEBA5000776DB /* jbm.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD88240EEB9A000776DB /* jbm.h */; }; - 2DC1CE15240EEBA5000776DB /* rix.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD89240EEB9A000776DB /* rix.h */; }; - 2DC1CE16240EEBA5000776DB /* mdi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD8A240EEB9A000776DB /* mdi.cpp */; }; - 2DC1CE17240EEBA5000776DB /* nukedopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD8B240EEB9A000776DB /* nukedopl.h */; }; - 2DC1CE18240EEBA5000776DB /* sng.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD8C240EEB9B000776DB /* sng.h */; }; - 2DC1CE19240EEBA5000776DB /* temuopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD8D240EEB9B000776DB /* temuopl.cpp */; }; - 2DC1CE1A240EEBA5000776DB /* fprovide.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD8E240EEB9B000776DB /* fprovide.cpp */; }; - 2DC1CE1B240EEBA5000776DB /* vgm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD8F240EEB9B000776DB /* vgm.cpp */; }; - 2DC1CE1C240EEBA5000776DB /* woodyopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD90240EEB9B000776DB /* woodyopl.h */; }; - 2DC1CE1D240EEBA5000776DB /* adl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD91240EEB9B000776DB /* adl.cpp */; }; - 2DC1CE1E240EEBA5000776DB /* surroundopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD92240EEB9B000776DB /* surroundopl.cpp */; }; - 2DC1CE1F240EEBA5000776DB /* imf.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD93240EEB9C000776DB /* imf.h */; }; - 2DC1CE20240EEBA5000776DB /* cmfmcsop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD94240EEB9C000776DB /* cmfmcsop.cpp */; }; - 2DC1CE21240EEBA5000776DB /* dtm.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD95240EEB9C000776DB /* dtm.h */; }; - 2DC1CE22240EEBA5000776DB /* surroundopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD96240EEB9C000776DB /* surroundopl.h */; }; - 2DC1CE23240EEBA5000776DB /* debug.c in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD97240EEB9C000776DB /* debug.c */; }; - 2DC1CE24240EEBA5000776DB /* mus.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD98240EEB9C000776DB /* mus.h */; }; - 2DC1CE25240EEBA5000776DB /* dmo.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CD99240EEB9D000776DB /* dmo.h */; }; - 2DC1CE26240EEBA5000776DB /* bam.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD9A240EEB9D000776DB /* bam.cpp */; }; - 2DC1CE27240EEBA5000776DB /* emuopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD9B240EEB9D000776DB /* emuopl.cpp */; }; - 2DC1CE28240EEBA5000776DB /* sop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD9C240EEB9D000776DB /* sop.cpp */; }; - 2DC1CE29240EEBA5000776DB /* diskopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD9D240EEB9D000776DB /* diskopl.cpp */; }; - 2DC1CE2A240EEBA5000776DB /* herad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD9E240EEB9D000776DB /* herad.cpp */; }; - 2DC1CE2B240EEBA5000776DB /* database.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CD9F240EEB9E000776DB /* database.cpp */; }; - 2DC1CE2C240EEBA5000776DB /* dtm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDA0240EEB9E000776DB /* dtm.cpp */; }; - 2DC1CE2D240EEBA5000776DB /* hsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDA1240EEB9E000776DB /* hsp.h */; }; - 2DC1CE2E240EEBA5000776DB /* bmf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDA2240EEB9E000776DB /* bmf.cpp */; }; - 2DC1CE2F240EEBA5000776DB /* fmc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDA3240EEB9E000776DB /* fmc.cpp */; }; - 2DC1CE30240EEBA5000776DB /* rix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDA4240EEB9E000776DB /* rix.cpp */; }; - 2DC1CE31240EEBA5000776DB /* protrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDA5240EEB9E000776DB /* protrack.cpp */; }; - 2DC1CE32240EEBA5000776DB /* xad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDA6240EEB9F000776DB /* xad.cpp */; }; - 2DC1CE33240EEBA5000776DB /* amd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDA7240EEB9F000776DB /* amd.cpp */; }; - 2DC1CE34240EEBA5000776DB /* player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDA8240EEB9F000776DB /* player.cpp */; }; - 2DC1CE35240EEBA5000776DB /* silentopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDA9240EEB9F000776DB /* silentopl.h */; }; - 2DC1CE36240EEBA5000776DB /* players.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDAA240EEB9F000776DB /* players.cpp */; }; - 2DC1CE37240EEBA5000776DB /* mtk.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDAB240EEB9F000776DB /* mtk.h */; }; - 2DC1CE38240EEBA5000776DB /* psi.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDAC240EEBA0000776DB /* psi.h */; }; - 2DC1CE39240EEBA5000776DB /* adtrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDAD240EEBA0000776DB /* adtrack.cpp */; }; - 2DC1CE3A240EEBA5000776DB /* jbm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDAE240EEBA0000776DB /* jbm.cpp */; }; - 2DC1CE3B240EEBA5000776DB /* u6m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDAF240EEBA0000776DB /* u6m.cpp */; }; - 2DC1CE3C240EEBA5000776DB /* herad.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDB0240EEBA0000776DB /* herad.h */; }; - 2DC1CE3D240EEBA5000776DB /* a2m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDB1240EEBA0000776DB /* a2m.cpp */; }; - 2DC1CE3E240EEBA5000776DB /* adlibemu.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDB2240EEBA1000776DB /* adlibemu.h */; }; - 2DC1CE3F240EEBA5000776DB /* rol.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDB3240EEBA1000776DB /* rol.h */; }; - 2DC1CE40240EEBA5000776DB /* dro2.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDB4240EEBA1000776DB /* dro2.h */; }; - 2DC1CE41240EEBA5000776DB /* mid.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDB5240EEBA1000776DB /* mid.h */; }; - 2DC1CE42240EEBA5000776DB /* mus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDB6240EEBA1000776DB /* mus.cpp */; }; - 2DC1CE43240EEBA5000776DB /* adlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDB7240EEBA1000776DB /* adlib.h */; }; - 2DC1CE44240EEBA5000776DB /* mad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDB8240EEBA1000776DB /* mad.cpp */; }; - 2DC1CE45240EEBA5000776DB /* hsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDB9240EEBA2000776DB /* hsp.cpp */; }; - 2DC1CE46240EEBA5000776DB /* raw.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDBA240EEBA2000776DB /* raw.h */; }; - 2DC1CE47240EEBA5000776DB /* d00.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDBB240EEBA2000776DB /* d00.cpp */; }; - 2DC1CE48240EEBA6000776DB /* hyp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDBC240EEBA2000776DB /* hyp.cpp */; }; - 2DC1CE49240EEBA6000776DB /* kemuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDBD240EEBA2000776DB /* kemuopl.h */; }; - 2DC1CE4A240EEBA6000776DB /* got.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDBE240EEBA2000776DB /* got.h */; }; - 2DC1CE4B240EEBA6000776DB /* flash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDBF240EEBA3000776DB /* flash.cpp */; }; - 2DC1CE4C240EEBA6000776DB /* xsm.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDC0240EEBA3000776DB /* xsm.h */; }; - 2DC1CE4D240EEBA6000776DB /* dro2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDC1240EEBA3000776DB /* dro2.cpp */; }; - 2DC1CE4E240EEBA6000776DB /* hsc.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDC2240EEBA3000776DB /* hsc.h */; }; - 2DC1CE4F240EEBA6000776DB /* lds.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDC3240EEBA3000776DB /* lds.h */; }; - 2DC1CE50240EEBA6000776DB /* hybrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDC4240EEBA3000776DB /* hybrid.cpp */; }; - 2DC1CE51240EEBA6000776DB /* mkj.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDC5240EEBA4000776DB /* mkj.cpp */; }; - 2DC1CE52240EEBA6000776DB /* dro.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDC6240EEBA4000776DB /* dro.h */; }; - 2DC1CE53240EEBA6000776DB /* bam.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDC7240EEBA4000776DB /* bam.h */; }; - 2DC1CE54240EEBA6000776DB /* adplug.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDC8240EEBA4000776DB /* adplug.h */; }; - 2DC1CE55240EEBA6000776DB /* adplug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDC9240EEBA4000776DB /* adplug.cpp */; }; - 2DC1CE56240EEBA6000776DB /* hybrid.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDCA240EEBA4000776DB /* hybrid.h */; }; - 2DC1CE57240EEBA6000776DB /* cmfmcsop.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDCB240EEBA5000776DB /* cmfmcsop.h */; }; - 2DC1CE58240EEBA6000776DB /* rad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DC1CDCC240EEBA5000776DB /* rad.cpp */; }; - 2DC1CE59240EEBA6000776DB /* flash.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDCD240EEBA5000776DB /* flash.h */; }; - 2DC1CE5A240EEBA6000776DB /* analopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDCE240EEBA5000776DB /* analopl.h */; }; - 2DC1CE5B240EEBA6000776DB /* rat.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC1CDCF240EEBA5000776DB /* rat.h */; }; 2DC4199E1B90540A007E3026 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DC4199D1B90540A007E3026 /* Carbon.framework */; }; 2DC5A2A72B05450400CBDA66 /* mlcellrendererpixbuf.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC5A2A12B05449800CBDA66 /* mlcellrendererpixbuf.h */; }; 2DC5A2A82B05450400CBDA66 /* mlcellrendererpixbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 2DC5A2A22B05449800CBDA66 /* mlcellrendererpixbuf.c */; }; @@ -3038,6 +2898,167 @@ 4DE28473205BE0B20023063E /* HelpViewer.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4DE28470205BE0B20023063E /* HelpViewer.xib */; }; 83BA8E501D542D0D00D345EE /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83BA8E4F1D542D0D00D345EE /* VideoToolbox.framework */; }; 83BA8E5F1D542D2700D345EE /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83BA8E5E1D542D2700D345EE /* CoreMedia.framework */; }; + E96D62CB2C97C15E009CB254 /* woodyopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62292C97C145009CB254 /* woodyopl.cpp */; }; + E96D62CC2C97C15E009CB254 /* cmfmcsop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D622A2C97C146009CB254 /* cmfmcsop.cpp */; }; + E96D62CD2C97C15E009CB254 /* mididata.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D622B2C97C146009CB254 /* mididata.h */; }; + E96D62CE2C97C15E009CB254 /* database.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D622C2C97C146009CB254 /* database.cpp */; }; + E96D62CF2C97C15E009CB254 /* bmf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D622D2C97C146009CB254 /* bmf.cpp */; }; + E96D62D02C97C15E009CB254 /* rad2.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D622E2C97C146009CB254 /* rad2.h */; }; + E96D62D12C97C15E009CB254 /* cmf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D622F2C97C146009CB254 /* cmf.cpp */; }; + E96D62D22C97C15E009CB254 /* rol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62302C97C146009CB254 /* rol.cpp */; }; + E96D62D32C97C15E009CB254 /* pch.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62312C97C147009CB254 /* pch.h */; }; + E96D62D42C97C15E009CB254 /* analopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62322C97C147009CB254 /* analopl.h */; }; + E96D62D52C97C15E009CB254 /* sop.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62332C97C147009CB254 /* sop.h */; }; + E96D62D62C97C15E009CB254 /* hyp.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62342C97C147009CB254 /* hyp.h */; }; + E96D62D72C97C15E009CB254 /* u6m.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62352C97C147009CB254 /* u6m.h */; }; + E96D62D82C97C15E009CB254 /* unlzw.c in Sources */ = {isa = PBXBuildFile; fileRef = E96D62362C97C147009CB254 /* unlzw.c */; }; + E96D62D92C97C15E009CB254 /* hybrid.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62372C97C147009CB254 /* hybrid.h */; }; + E96D62DA2C97C15E009CB254 /* xsm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62382C97C148009CB254 /* xsm.cpp */; }; + E96D62DB2C97C15E009CB254 /* cff.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62392C97C148009CB254 /* cff.h */; }; + E96D62DC2C97C15E009CB254 /* dtm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D623A2C97C148009CB254 /* dtm.cpp */; }; + E96D62DD2C97C15E009CB254 /* mtk.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D623B2C97C148009CB254 /* mtk.h */; }; + E96D62DE2C97C15E009CB254 /* rix.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D623C2C97C148009CB254 /* rix.h */; }; + E96D62DF2C97C15E009CB254 /* temuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D623D2C97C148009CB254 /* temuopl.h */; }; + E96D62E02C97C15E009CB254 /* jbm.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D623E2C97C148009CB254 /* jbm.h */; }; + E96D62E12C97C15E009CB254 /* mid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D623F2C97C149009CB254 /* mid.cpp */; }; + E96D62E22C97C15E009CB254 /* herad.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62402C97C149009CB254 /* herad.h */; }; + E96D62E32C97C15E009CB254 /* rat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62412C97C149009CB254 /* rat.cpp */; }; + E96D62E42C97C15E009CB254 /* coktel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62422C97C149009CB254 /* coktel.cpp */; }; + E96D62E52C97C15E009CB254 /* silentopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62432C97C149009CB254 /* silentopl.h */; }; + E96D62E62C97C15E009CB254 /* surroundopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62442C97C149009CB254 /* surroundopl.cpp */; }; + E96D62E72C97C15E009CB254 /* psi.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62452C97C14A009CB254 /* psi.h */; }; + E96D62E82C97C15E009CB254 /* vgm.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62462C97C14A009CB254 /* vgm.h */; }; + E96D62E92C97C15E009CB254 /* rol.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62472C97C14A009CB254 /* rol.h */; }; + E96D62EA2C97C15E009CB254 /* a2m.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62482C97C14A009CB254 /* a2m.h */; }; + E96D62EB2C97C15E009CB254 /* sixdepack.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62492C97C14A009CB254 /* sixdepack.h */; }; + E96D62EC2C97C15E009CB254 /* flash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D624A2C97C14A009CB254 /* flash.cpp */; }; + E96D62ED2C97C15E009CB254 /* mad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D624B2C97C14A009CB254 /* mad.cpp */; }; + E96D62EE2C97C15E009CB254 /* fmopl.c in Sources */ = {isa = PBXBuildFile; fileRef = E96D624C2C97C14B009CB254 /* fmopl.c */; }; + E96D62EF2C97C15E009CB254 /* mdi.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D624D2C97C14B009CB254 /* mdi.h */; }; + E96D62F02C97C15E009CB254 /* msc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D624E2C97C14B009CB254 /* msc.cpp */; }; + E96D62F12C97C15E009CB254 /* fmc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D624F2C97C14B009CB254 /* fmc.cpp */; }; + E96D62F22C97C15E009CB254 /* depack.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62502C97C14B009CB254 /* depack.h */; }; + E96D62F32C97C15E009CB254 /* adl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62512C97C14B009CB254 /* adl.h */; }; + E96D62F42C97C15E009CB254 /* d00.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62522C97C14C009CB254 /* d00.h */; }; + E96D62F52C97C15E009CB254 /* raw.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62532C97C14C009CB254 /* raw.h */; }; + E96D62F62C97C15E009CB254 /* unlzh.c in Sources */ = {isa = PBXBuildFile; fileRef = E96D62542C97C14C009CB254 /* unlzh.c */; }; + E96D62F72C97C15E009CB254 /* mid.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62552C97C14C009CB254 /* mid.h */; }; + E96D62F82C97C15E009CB254 /* dro2.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62562C97C14C009CB254 /* dro2.h */; }; + E96D62F92C97C15E009CB254 /* protrack.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62572C97C14C009CB254 /* protrack.h */; }; + E96D62FA2C97C15E009CB254 /* hybrid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62582C97C14C009CB254 /* hybrid.cpp */; }; + E96D62FB2C97C15E009CB254 /* database.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62592C97C14D009CB254 /* database.h */; }; + E96D62FC2C97C15E009CB254 /* dtm.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D625A2C97C14D009CB254 /* dtm.h */; }; + E96D62FD2C97C15E009CB254 /* nukedopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D625B2C97C14D009CB254 /* nukedopl.h */; }; + E96D62FE2C97C15E009CB254 /* jbm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D625C2C97C14D009CB254 /* jbm.cpp */; }; + E96D62FF2C97C15E009CB254 /* a2m-v2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D625D2C97C14D009CB254 /* a2m-v2.cpp */; }; + E96D63002C97C15E009CB254 /* sa2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D625E2C97C14D009CB254 /* sa2.cpp */; }; + E96D63012C97C15E009CB254 /* vgm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D625F2C97C14D009CB254 /* vgm.cpp */; }; + E96D63022C97C15E009CB254 /* protrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62602C97C14E009CB254 /* protrack.cpp */; }; + E96D63032C97C15E009CB254 /* mtk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62612C97C14E009CB254 /* mtk.cpp */; }; + E96D63042C97C15E009CB254 /* amd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62632C97C14E009CB254 /* amd.cpp */; }; + E96D63052C97C15E009CB254 /* mkj.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62642C97C14E009CB254 /* mkj.h */; }; + E96D63062C97C15E009CB254 /* cmfmcsop.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62652C97C14E009CB254 /* cmfmcsop.h */; }; + E96D63072C97C15E009CB254 /* hsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62662C97C14F009CB254 /* hsp.cpp */; }; + E96D63082C97C15E009CB254 /* dro2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62672C97C14F009CB254 /* dro2.cpp */; }; + E96D63092C97C15E009CB254 /* adlibemu.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62682C97C14F009CB254 /* adlibemu.h */; }; + E96D630A2C97C15E009CB254 /* hyp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62692C97C14F009CB254 /* hyp.cpp */; }; + E96D630B2C97C15E009CB254 /* dro.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D626A2C97C14F009CB254 /* dro.cpp */; }; + E96D630C2C97C15E009CB254 /* mkj.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D626B2C97C14F009CB254 /* mkj.cpp */; }; + E96D630D2C97C15E009CB254 /* nemuopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D626C2C97C14F009CB254 /* nemuopl.cpp */; }; + E96D630E2C97C15E009CB254 /* opl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D626D2C97C150009CB254 /* opl.h */; }; + E96D630F2C97C15E009CB254 /* players.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D626E2C97C150009CB254 /* players.h */; }; + E96D63102C97C15E009CB254 /* realopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D626F2C97C150009CB254 /* realopl.h */; }; + E96D63112C97C15E009CB254 /* surroundopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62702C97C150009CB254 /* surroundopl.h */; }; + E96D63122C97C15E009CB254 /* debug.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62712C97C150009CB254 /* debug.h */; }; + E96D63132C97C15E009CB254 /* depack.c in Sources */ = {isa = PBXBuildFile; fileRef = E96D62722C97C150009CB254 /* depack.c */; }; + E96D63142C97C15E009CB254 /* hsp.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62732C97C150009CB254 /* hsp.h */; }; + E96D63152C97C15E009CB254 /* strnlen.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62742C97C151009CB254 /* strnlen.h */; }; + E96D63162C97C15E009CB254 /* adtrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62752C97C151009CB254 /* adtrack.cpp */; }; + E96D63172C97C15E009CB254 /* lds.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62762C97C151009CB254 /* lds.cpp */; }; + E96D63182C97C15E009CB254 /* player.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62772C97C151009CB254 /* player.h */; }; + E96D63192C97C15E009CB254 /* s3m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62782C97C151009CB254 /* s3m.cpp */; }; + E96D631A2C97C15E009CB254 /* composer.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62792C97C151009CB254 /* composer.h */; }; + E96D631B2C97C15E009CB254 /* composer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D627A2C97C152009CB254 /* composer.cpp */; }; + E96D631C2C97C15E009CB254 /* ksm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D627B2C97C152009CB254 /* ksm.cpp */; }; + E96D631D2C97C15E009CB254 /* adtrack.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D627C2C97C152009CB254 /* adtrack.h */; }; + E96D631E2C97C15E009CB254 /* realopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D627D2C97C152009CB254 /* realopl.cpp */; }; + E96D631F2C97C15E009CB254 /* adplug.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D627E2C97C152009CB254 /* adplug.h */; }; + E96D63202C97C15E009CB254 /* u6m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D627F2C97C152009CB254 /* u6m.cpp */; }; + E96D63212C97C15E009CB254 /* mus.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62802C97C152009CB254 /* mus.h */; }; + E96D63222C97C15E009CB254 /* imf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62812C97C153009CB254 /* imf.cpp */; }; + E96D63232C97C15E009CB254 /* analopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62822C97C153009CB254 /* analopl.cpp */; }; + E96D63242C97C15E009CB254 /* a2m.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62832C97C153009CB254 /* a2m.cpp */; }; + E96D63252C97C15E009CB254 /* raw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62842C97C153009CB254 /* raw.cpp */; }; + E96D63262C97C15E009CB254 /* adlibemu.c in Sources */ = {isa = PBXBuildFile; fileRef = E96D62852C97C153009CB254 /* adlibemu.c */; }; + E96D63272C97C15E009CB254 /* sop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62862C97C153009CB254 /* sop.cpp */; }; + E96D63282C97C15E009CB254 /* xad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62872C97C153009CB254 /* xad.cpp */; }; + E96D63292C97C15E009CB254 /* imf.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62882C97C154009CB254 /* imf.h */; }; + E96D632A2C97C15E009CB254 /* sixdepack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62892C97C154009CB254 /* sixdepack.cpp */; }; + E96D632B2C97C15E009CB254 /* unlzw.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D628A2C97C154009CB254 /* unlzw.h */; }; + E96D632C2C97C15E009CB254 /* bam.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D628B2C97C154009CB254 /* bam.cpp */; }; + E96D632D2C97C15E009CB254 /* sng.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D628C2C97C154009CB254 /* sng.h */; }; + E96D632E2C97C15E009CB254 /* dmo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D628D2C97C154009CB254 /* dmo.cpp */; }; + E96D632F2C97C15E009CB254 /* dmo.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D628E2C97C155009CB254 /* dmo.h */; }; + E96D63302C97C15E009CB254 /* load_helper.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D628F2C97C155009CB254 /* load_helper.h */; }; + E96D63312C97C15E009CB254 /* amd.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62902C97C155009CB254 /* amd.h */; }; + E96D63322C97C15E009CB254 /* s3m.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62912C97C155009CB254 /* s3m.h */; }; + E96D63332C97C15E009CB254 /* fmc.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62922C97C155009CB254 /* fmc.h */; }; + E96D63342C97C15E009CB254 /* unlzh.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62932C97C155009CB254 /* unlzh.h */; }; + E96D63352C97C15E009CB254 /* fprovide.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62942C97C155009CB254 /* fprovide.h */; }; + E96D63362C97C15E009CB254 /* rix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62952C97C156009CB254 /* rix.cpp */; }; + E96D63372C97C15E009CB254 /* hsc.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62962C97C156009CB254 /* hsc.h */; }; + E96D63382C97C15E009CB254 /* emuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62972C97C156009CB254 /* emuopl.h */; }; + E96D63392C97C15E009CB254 /* players.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62982C97C156009CB254 /* players.cpp */; }; + E96D633A2C97C15E009CB254 /* debug.c in Sources */ = {isa = PBXBuildFile; fileRef = E96D62992C97C156009CB254 /* debug.c */; }; + E96D633B2C97C15E009CB254 /* player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D629A2C97C156009CB254 /* player.cpp */; }; + E96D633C2C97C15E009CB254 /* wemuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D629B2C97C157009CB254 /* wemuopl.h */; }; + E96D633D2C97C15E009CB254 /* fmopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D629C2C97C157009CB254 /* fmopl.h */; }; + E96D633E2C97C15E009CB254 /* mtr.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D629D2C97C157009CB254 /* mtr.h */; }; + E96D633F2C97C15E009CB254 /* nemuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D629E2C97C157009CB254 /* nemuopl.h */; }; + E96D63402C97C15E009CB254 /* mdi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D629F2C97C157009CB254 /* mdi.cpp */; }; + E96D63412C97C15E009CB254 /* xad.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62A02C97C157009CB254 /* xad.h */; }; + E96D63422C97C15E009CB254 /* herad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62A12C97C157009CB254 /* herad.cpp */; }; + E96D63432C97C15E009CB254 /* pis.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62A22C97C158009CB254 /* pis.cpp */; }; + E96D63442C97C15E009CB254 /* xsm.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62A32C97C158009CB254 /* xsm.h */; }; + E96D63452C97C15E009CB254 /* nukedopl.c in Sources */ = {isa = PBXBuildFile; fileRef = E96D62A42C97C158009CB254 /* nukedopl.c */; }; + E96D63462C97C15E009CB254 /* unlzss.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62A52C97C158009CB254 /* unlzss.h */; }; + E96D63472C97C15E009CB254 /* diskopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62A62C97C158009CB254 /* diskopl.cpp */; }; + E96D63482C97C15E009CB254 /* cmf.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62A72C97C158009CB254 /* cmf.h */; }; + E96D63492C97C15E009CB254 /* got.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62A82C97C158009CB254 /* got.h */; }; + E96D634A2C97C15E009CB254 /* bam.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62A92C97C159009CB254 /* bam.h */; }; + E96D634B2C97C15E009CB254 /* cff.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62AA2C97C159009CB254 /* cff.cpp */; }; + E96D634C2C97C15E009CB254 /* dro.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62AB2C97C159009CB254 /* dro.h */; }; + E96D634D2C97C15E009CB254 /* rad2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62AC2C97C159009CB254 /* rad2.cpp */; }; + E96D634E2C97C15E009CB254 /* ksm.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62AD2C97C159009CB254 /* ksm.h */; }; + E96D634F2C97C15E009CB254 /* unlzss.c in Sources */ = {isa = PBXBuildFile; fileRef = E96D62AE2C97C159009CB254 /* unlzss.c */; }; + E96D63502C97C15E009CB254 /* got.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62AF2C97C15A009CB254 /* got.cpp */; }; + E96D63512C97C15E009CB254 /* adplug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62B02C97C15A009CB254 /* adplug.cpp */; }; + E96D63522C97C15E009CB254 /* bmf.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62B12C97C15A009CB254 /* bmf.h */; }; + E96D63532C97C15E009CB254 /* pis.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62B22C97C15A009CB254 /* pis.h */; }; + E96D63542C97C15E009CB254 /* hsc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62B32C97C15A009CB254 /* hsc.cpp */; }; + E96D63552C97C15E009CB254 /* kemuopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62B42C97C15A009CB254 /* kemuopl.cpp */; }; + E96D63562C97C15E009CB254 /* temuopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62B52C97C15B009CB254 /* temuopl.cpp */; }; + E96D63572C97C15E009CB254 /* mad.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62B62C97C15B009CB254 /* mad.h */; }; + E96D63582C97C15E009CB254 /* emuopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62B72C97C15B009CB254 /* emuopl.cpp */; }; + E96D63592C97C15E009CB254 /* flash.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62B82C97C15B009CB254 /* flash.h */; }; + E96D635A2C97C15E009CB254 /* fprovide.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62B92C97C15B009CB254 /* fprovide.cpp */; }; + E96D635B2C97C15E009CB254 /* d00.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62BA2C97C15B009CB254 /* d00.cpp */; }; + E96D635C2C97C15E009CB254 /* mtr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62BB2C97C15B009CB254 /* mtr.cpp */; }; + E96D635D2C97C15E009CB254 /* lds.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62BC2C97C15C009CB254 /* lds.h */; }; + E96D635E2C97C15E009CB254 /* kemuopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62BD2C97C15C009CB254 /* kemuopl.h */; }; + E96D635F2C97C15E009CB254 /* msc.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62BE2C97C15C009CB254 /* msc.h */; }; + E96D63602C97C15E009CB254 /* dfm.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62BF2C97C15C009CB254 /* dfm.h */; }; + E96D63612C97C15E009CB254 /* a2m-v2.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62C02C97C15C009CB254 /* a2m-v2.h */; }; + E96D63622C97C15E009CB254 /* rat.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62C12C97C15C009CB254 /* rat.h */; }; + E96D63632C97C15E009CB254 /* diskopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62C22C97C15D009CB254 /* diskopl.h */; }; + E96D63642C97C15E009CB254 /* sa2.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62C32C97C15D009CB254 /* sa2.h */; }; + E96D63652C97C15E009CB254 /* woodyopl.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62C42C97C15D009CB254 /* woodyopl.h */; }; + E96D63662C97C15E009CB254 /* adl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62C52C97C15D009CB254 /* adl.cpp */; }; + E96D63672C97C15E009CB254 /* dfm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62C62C97C15D009CB254 /* dfm.cpp */; }; + E96D63682C97C15E009CB254 /* sng.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62C72C97C15D009CB254 /* sng.cpp */; }; + E96D63692C97C15E009CB254 /* psi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62C82C97C15D009CB254 /* psi.cpp */; }; + E96D636A2C97C15E009CB254 /* coktel.h in Headers */ = {isa = PBXBuildFile; fileRef = E96D62C92C97C15E009CB254 /* coktel.h */; }; + E96D636B2C97C15E009CB254 /* mus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E96D62CA2C97C15E009CB254 /* mus.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -6779,146 +6800,6 @@ 2DBF3D4227086AA900023138 /* MainContentViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MainContentViewController.m; sourceTree = ""; }; 2DBF3DB0270A0D0200023138 /* medialibstate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = medialibstate.h; sourceTree = ""; }; 2DBF3DB1270A0D0200023138 /* medialibstate.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = medialibstate.c; sourceTree = ""; }; - 2DC1CD44240EEB8F000776DB /* rol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rol.cpp; sourceTree = ""; }; - 2DC1CD45240EEB8F000776DB /* nukedopl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nukedopl.c; sourceTree = ""; }; - 2DC1CD46240EEB8F000776DB /* vgm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vgm.h; sourceTree = ""; }; - 2DC1CD47240EEB8F000776DB /* temuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = temuopl.h; sourceTree = ""; }; - 2DC1CD48240EEB90000776DB /* sa2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sa2.h; sourceTree = ""; }; - 2DC1CD49240EEB90000776DB /* msc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = msc.cpp; sourceTree = ""; }; - 2DC1CD4A240EEB90000776DB /* adlibemu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adlibemu.c; sourceTree = ""; }; - 2DC1CD4B240EEB90000776DB /* d00.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = d00.h; sourceTree = ""; }; - 2DC1CD4C240EEB90000776DB /* amd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = amd.h; sourceTree = ""; }; - 2DC1CD4D240EEB90000776DB /* mid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mid.cpp; sourceTree = ""; }; - 2DC1CD4E240EEB91000776DB /* got.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = got.cpp; sourceTree = ""; }; - 2DC1CD4F240EEB91000776DB /* fprovide.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fprovide.h; sourceTree = ""; }; - 2DC1CD50240EEB91000776DB /* diskopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = diskopl.h; sourceTree = ""; }; - 2DC1CD51240EEB91000776DB /* opl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opl.h; sourceTree = ""; }; - 2DC1CD52240EEB91000776DB /* cff.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cff.cpp; sourceTree = ""; }; - 2DC1CD53240EEB91000776DB /* sa2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sa2.cpp; sourceTree = ""; }; - 2DC1CD54240EEB91000776DB /* adtrack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adtrack.h; sourceTree = ""; }; - 2DC1CD55240EEB92000776DB /* a2m.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = a2m.h; sourceTree = ""; }; - 2DC1CD56240EEB92000776DB /* nemuopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nemuopl.cpp; sourceTree = ""; }; - 2DC1CD57240EEB92000776DB /* cmf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmf.h; sourceTree = ""; }; - 2DC1CD58240EEB92000776DB /* players.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = players.h; sourceTree = ""; }; - 2DC1CD59240EEB92000776DB /* protrack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = protrack.h; sourceTree = ""; }; - 2DC1CD5A240EEB92000776DB /* u6m.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = u6m.h; sourceTree = ""; }; - 2DC1CD5B240EEB93000776DB /* cff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cff.h; sourceTree = ""; }; - 2DC1CD5C240EEB93000776DB /* xad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xad.h; sourceTree = ""; }; - 2DC1CD5D240EEB93000776DB /* adl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adl.h; sourceTree = ""; }; - 2DC1CD5E240EEB93000776DB /* emuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emuopl.h; sourceTree = ""; }; - 2DC1CD5F240EEB93000776DB /* hsc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hsc.cpp; sourceTree = ""; }; - 2DC1CD60240EEB94000776DB /* dro.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dro.cpp; sourceTree = ""; }; - 2DC1CD61240EEB94000776DB /* raw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = raw.cpp; sourceTree = ""; }; - 2DC1CD62240EEB94000776DB /* hyp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hyp.h; sourceTree = ""; }; - 2DC1CD63240EEB94000776DB /* dfm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dfm.cpp; sourceTree = ""; }; - 2DC1CD64240EEB94000776DB /* rad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rad.h; sourceTree = ""; }; - 2DC1CD65240EEB94000776DB /* realopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = realopl.h; sourceTree = ""; }; - 2DC1CD66240EEB95000776DB /* imf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = imf.cpp; sourceTree = ""; }; - 2DC1CD67240EEB95000776DB /* sop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sop.h; sourceTree = ""; }; - 2DC1CD68240EEB95000776DB /* fmopl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fmopl.c; sourceTree = ""; }; - 2DC1CD69240EEB95000776DB /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = ""; }; - 2DC1CD6A240EEB95000776DB /* sng.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sng.cpp; sourceTree = ""; }; - 2DC1CD6B240EEB95000776DB /* bmf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bmf.h; sourceTree = ""; }; - 2DC1CD6C240EEB95000776DB /* mad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mad.h; sourceTree = ""; }; - 2DC1CD6D240EEB96000776DB /* woodyopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = woodyopl.cpp; sourceTree = ""; }; - 2DC1CD6E240EEB96000776DB /* mtk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mtk.cpp; sourceTree = ""; }; - 2DC1CD6F240EEB96000776DB /* s3m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = s3m.cpp; sourceTree = ""; }; - 2DC1CD70240EEB96000776DB /* analopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = analopl.cpp; sourceTree = ""; }; - 2DC1CD71240EEB96000776DB /* nemuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nemuopl.h; sourceTree = ""; }; - 2DC1CD72240EEB96000776DB /* cmf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cmf.cpp; sourceTree = ""; }; - 2DC1CD73240EEB97000776DB /* dfm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dfm.h; sourceTree = ""; }; - 2DC1CD74240EEB97000776DB /* adlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = adlib.cpp; sourceTree = ""; }; - 2DC1CD75240EEB97000776DB /* msc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = msc.h; sourceTree = ""; }; - 2DC1CD76240EEB97000776DB /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = ""; }; - 2DC1CD77240EEB97000776DB /* mdi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mdi.h; sourceTree = ""; }; - 2DC1CD78240EEB97000776DB /* player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = player.h; sourceTree = ""; }; - 2DC1CD79240EEB97000776DB /* rat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rat.cpp; sourceTree = ""; }; - 2DC1CD7A240EEB98000776DB /* fmopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmopl.h; sourceTree = ""; }; - 2DC1CD7B240EEB98000776DB /* lds.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lds.cpp; sourceTree = ""; }; - 2DC1CD7C240EEB98000776DB /* realopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = realopl.cpp; sourceTree = ""; }; - 2DC1CD7D240EEB98000776DB /* dmo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dmo.cpp; sourceTree = ""; }; - 2DC1CD7E240EEB98000776DB /* s3m.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = s3m.h; sourceTree = ""; }; - 2DC1CD7F240EEB98000776DB /* ksm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ksm.cpp; sourceTree = ""; }; - 2DC1CD80240EEB99000776DB /* xsm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xsm.cpp; sourceTree = ""; }; - 2DC1CD81240EEB99000776DB /* wemuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wemuopl.h; sourceTree = ""; }; - 2DC1CD82240EEB99000776DB /* database.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = database.h; sourceTree = ""; }; - 2DC1CD83240EEB99000776DB /* psi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = psi.cpp; sourceTree = ""; }; - 2DC1CD84240EEB99000776DB /* fmc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmc.h; sourceTree = ""; }; - 2DC1CD85240EEB99000776DB /* mkj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mkj.h; sourceTree = ""; }; - 2DC1CD86240EEB9A000776DB /* ksm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ksm.h; sourceTree = ""; }; - 2DC1CD87240EEB9A000776DB /* mididata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mididata.h; sourceTree = ""; }; - 2DC1CD88240EEB9A000776DB /* jbm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jbm.h; sourceTree = ""; }; - 2DC1CD89240EEB9A000776DB /* rix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rix.h; sourceTree = ""; }; - 2DC1CD8A240EEB9A000776DB /* mdi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mdi.cpp; sourceTree = ""; }; - 2DC1CD8B240EEB9A000776DB /* nukedopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nukedopl.h; sourceTree = ""; }; - 2DC1CD8C240EEB9B000776DB /* sng.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sng.h; sourceTree = ""; }; - 2DC1CD8D240EEB9B000776DB /* temuopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = temuopl.cpp; sourceTree = ""; }; - 2DC1CD8E240EEB9B000776DB /* fprovide.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fprovide.cpp; sourceTree = ""; }; - 2DC1CD8F240EEB9B000776DB /* vgm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vgm.cpp; sourceTree = ""; }; - 2DC1CD90240EEB9B000776DB /* woodyopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = woodyopl.h; sourceTree = ""; }; - 2DC1CD91240EEB9B000776DB /* adl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = adl.cpp; sourceTree = ""; }; - 2DC1CD92240EEB9B000776DB /* surroundopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = surroundopl.cpp; sourceTree = ""; }; - 2DC1CD93240EEB9C000776DB /* imf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = imf.h; sourceTree = ""; }; - 2DC1CD94240EEB9C000776DB /* cmfmcsop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cmfmcsop.cpp; sourceTree = ""; }; - 2DC1CD95240EEB9C000776DB /* dtm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dtm.h; sourceTree = ""; }; - 2DC1CD96240EEB9C000776DB /* surroundopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = surroundopl.h; sourceTree = ""; }; - 2DC1CD97240EEB9C000776DB /* debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = debug.c; sourceTree = ""; }; - 2DC1CD98240EEB9C000776DB /* mus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mus.h; sourceTree = ""; }; - 2DC1CD99240EEB9D000776DB /* dmo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dmo.h; sourceTree = ""; }; - 2DC1CD9A240EEB9D000776DB /* bam.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bam.cpp; sourceTree = ""; }; - 2DC1CD9B240EEB9D000776DB /* emuopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = emuopl.cpp; sourceTree = ""; }; - 2DC1CD9C240EEB9D000776DB /* sop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sop.cpp; sourceTree = ""; }; - 2DC1CD9D240EEB9D000776DB /* diskopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = diskopl.cpp; sourceTree = ""; }; - 2DC1CD9E240EEB9D000776DB /* herad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = herad.cpp; sourceTree = ""; }; - 2DC1CD9F240EEB9E000776DB /* database.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = database.cpp; sourceTree = ""; }; - 2DC1CDA0240EEB9E000776DB /* dtm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dtm.cpp; sourceTree = ""; }; - 2DC1CDA1240EEB9E000776DB /* hsp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hsp.h; sourceTree = ""; }; - 2DC1CDA2240EEB9E000776DB /* bmf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bmf.cpp; sourceTree = ""; }; - 2DC1CDA3240EEB9E000776DB /* fmc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fmc.cpp; sourceTree = ""; }; - 2DC1CDA4240EEB9E000776DB /* rix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rix.cpp; sourceTree = ""; }; - 2DC1CDA5240EEB9E000776DB /* protrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = protrack.cpp; sourceTree = ""; }; - 2DC1CDA6240EEB9F000776DB /* xad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xad.cpp; sourceTree = ""; }; - 2DC1CDA7240EEB9F000776DB /* amd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = amd.cpp; sourceTree = ""; }; - 2DC1CDA8240EEB9F000776DB /* player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = player.cpp; sourceTree = ""; }; - 2DC1CDA9240EEB9F000776DB /* silentopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = silentopl.h; sourceTree = ""; }; - 2DC1CDAA240EEB9F000776DB /* players.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = players.cpp; sourceTree = ""; }; - 2DC1CDAB240EEB9F000776DB /* mtk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mtk.h; sourceTree = ""; }; - 2DC1CDAC240EEBA0000776DB /* psi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = psi.h; sourceTree = ""; }; - 2DC1CDAD240EEBA0000776DB /* adtrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = adtrack.cpp; sourceTree = ""; }; - 2DC1CDAE240EEBA0000776DB /* jbm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jbm.cpp; sourceTree = ""; }; - 2DC1CDAF240EEBA0000776DB /* u6m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = u6m.cpp; sourceTree = ""; }; - 2DC1CDB0240EEBA0000776DB /* herad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = herad.h; sourceTree = ""; }; - 2DC1CDB1240EEBA0000776DB /* a2m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = a2m.cpp; sourceTree = ""; }; - 2DC1CDB2240EEBA1000776DB /* adlibemu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adlibemu.h; sourceTree = ""; }; - 2DC1CDB3240EEBA1000776DB /* rol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rol.h; sourceTree = ""; }; - 2DC1CDB4240EEBA1000776DB /* dro2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dro2.h; sourceTree = ""; }; - 2DC1CDB5240EEBA1000776DB /* mid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mid.h; sourceTree = ""; }; - 2DC1CDB6240EEBA1000776DB /* mus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mus.cpp; sourceTree = ""; }; - 2DC1CDB7240EEBA1000776DB /* adlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adlib.h; sourceTree = ""; }; - 2DC1CDB8240EEBA1000776DB /* mad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mad.cpp; sourceTree = ""; }; - 2DC1CDB9240EEBA2000776DB /* hsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hsp.cpp; sourceTree = ""; }; - 2DC1CDBA240EEBA2000776DB /* raw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = raw.h; sourceTree = ""; }; - 2DC1CDBB240EEBA2000776DB /* d00.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = d00.cpp; sourceTree = ""; }; - 2DC1CDBC240EEBA2000776DB /* hyp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hyp.cpp; sourceTree = ""; }; - 2DC1CDBD240EEBA2000776DB /* kemuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kemuopl.h; sourceTree = ""; }; - 2DC1CDBE240EEBA2000776DB /* got.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = got.h; sourceTree = ""; }; - 2DC1CDBF240EEBA3000776DB /* flash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = flash.cpp; sourceTree = ""; }; - 2DC1CDC0240EEBA3000776DB /* xsm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xsm.h; sourceTree = ""; }; - 2DC1CDC1240EEBA3000776DB /* dro2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dro2.cpp; sourceTree = ""; }; - 2DC1CDC2240EEBA3000776DB /* hsc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hsc.h; sourceTree = ""; }; - 2DC1CDC3240EEBA3000776DB /* lds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lds.h; sourceTree = ""; }; - 2DC1CDC4240EEBA3000776DB /* hybrid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hybrid.cpp; sourceTree = ""; }; - 2DC1CDC5240EEBA4000776DB /* mkj.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mkj.cpp; sourceTree = ""; }; - 2DC1CDC6240EEBA4000776DB /* dro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dro.h; sourceTree = ""; }; - 2DC1CDC7240EEBA4000776DB /* bam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bam.h; sourceTree = ""; }; - 2DC1CDC8240EEBA4000776DB /* adplug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adplug.h; sourceTree = ""; }; - 2DC1CDC9240EEBA4000776DB /* adplug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = adplug.cpp; sourceTree = ""; }; - 2DC1CDCA240EEBA4000776DB /* hybrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hybrid.h; sourceTree = ""; }; - 2DC1CDCB240EEBA5000776DB /* cmfmcsop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmfmcsop.h; sourceTree = ""; }; - 2DC1CDCC240EEBA5000776DB /* rad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rad.cpp; sourceTree = ""; }; - 2DC1CDCD240EEBA5000776DB /* flash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = flash.h; sourceTree = ""; }; - 2DC1CDCE240EEBA5000776DB /* analopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = analopl.h; sourceTree = ""; }; - 2DC1CDCF240EEBA5000776DB /* rat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rat.h; sourceTree = ""; }; 2DC4199D1B90540A007E3026 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; 2DC5A2A12B05449800CBDA66 /* mlcellrendererpixbuf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mlcellrendererpixbuf.h; sourceTree = ""; }; 2DC5A2A22B05449800CBDA66 /* mlcellrendererpixbuf.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mlcellrendererpixbuf.c; sourceTree = ""; }; @@ -7618,6 +7499,168 @@ 4DE28470205BE0B20023063E /* HelpViewer.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HelpViewer.xib; sourceTree = ""; }; 83BA8E4F1D542D0D00D345EE /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; }; 83BA8E5E1D542D2700D345EE /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + E96D62292C97C145009CB254 /* woodyopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = woodyopl.cpp; sourceTree = ""; }; + E96D622A2C97C146009CB254 /* cmfmcsop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cmfmcsop.cpp; sourceTree = ""; }; + E96D622B2C97C146009CB254 /* mididata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mididata.h; sourceTree = ""; }; + E96D622C2C97C146009CB254 /* database.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = database.cpp; sourceTree = ""; }; + E96D622D2C97C146009CB254 /* bmf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bmf.cpp; sourceTree = ""; }; + E96D622E2C97C146009CB254 /* rad2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rad2.h; sourceTree = ""; }; + E96D622F2C97C146009CB254 /* cmf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cmf.cpp; sourceTree = ""; }; + E96D62302C97C146009CB254 /* rol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rol.cpp; sourceTree = ""; }; + E96D62312C97C147009CB254 /* pch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pch.h; sourceTree = ""; }; + E96D62322C97C147009CB254 /* analopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = analopl.h; sourceTree = ""; }; + E96D62332C97C147009CB254 /* sop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sop.h; sourceTree = ""; }; + E96D62342C97C147009CB254 /* hyp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hyp.h; sourceTree = ""; }; + E96D62352C97C147009CB254 /* u6m.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = u6m.h; sourceTree = ""; }; + E96D62362C97C147009CB254 /* unlzw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unlzw.c; sourceTree = ""; }; + E96D62372C97C147009CB254 /* hybrid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hybrid.h; sourceTree = ""; }; + E96D62382C97C148009CB254 /* xsm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xsm.cpp; sourceTree = ""; }; + E96D62392C97C148009CB254 /* cff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cff.h; sourceTree = ""; }; + E96D623A2C97C148009CB254 /* dtm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dtm.cpp; sourceTree = ""; }; + E96D623B2C97C148009CB254 /* mtk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mtk.h; sourceTree = ""; }; + E96D623C2C97C148009CB254 /* rix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rix.h; sourceTree = ""; }; + E96D623D2C97C148009CB254 /* temuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = temuopl.h; sourceTree = ""; }; + E96D623E2C97C148009CB254 /* jbm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jbm.h; sourceTree = ""; }; + E96D623F2C97C149009CB254 /* mid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mid.cpp; sourceTree = ""; }; + E96D62402C97C149009CB254 /* herad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = herad.h; sourceTree = ""; }; + E96D62412C97C149009CB254 /* rat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rat.cpp; sourceTree = ""; }; + E96D62422C97C149009CB254 /* coktel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = coktel.cpp; sourceTree = ""; }; + E96D62432C97C149009CB254 /* silentopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = silentopl.h; sourceTree = ""; }; + E96D62442C97C149009CB254 /* surroundopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = surroundopl.cpp; sourceTree = ""; }; + E96D62452C97C14A009CB254 /* psi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = psi.h; sourceTree = ""; }; + E96D62462C97C14A009CB254 /* vgm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vgm.h; sourceTree = ""; }; + E96D62472C97C14A009CB254 /* rol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rol.h; sourceTree = ""; }; + E96D62482C97C14A009CB254 /* a2m.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = a2m.h; sourceTree = ""; }; + E96D62492C97C14A009CB254 /* sixdepack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sixdepack.h; sourceTree = ""; }; + E96D624A2C97C14A009CB254 /* flash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = flash.cpp; sourceTree = ""; }; + E96D624B2C97C14A009CB254 /* mad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mad.cpp; sourceTree = ""; }; + E96D624C2C97C14B009CB254 /* fmopl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fmopl.c; sourceTree = ""; }; + E96D624D2C97C14B009CB254 /* mdi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mdi.h; sourceTree = ""; }; + E96D624E2C97C14B009CB254 /* msc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = msc.cpp; sourceTree = ""; }; + E96D624F2C97C14B009CB254 /* fmc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fmc.cpp; sourceTree = ""; }; + E96D62502C97C14B009CB254 /* depack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = depack.h; sourceTree = ""; }; + E96D62512C97C14B009CB254 /* adl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adl.h; sourceTree = ""; }; + E96D62522C97C14C009CB254 /* d00.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = d00.h; sourceTree = ""; }; + E96D62532C97C14C009CB254 /* raw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = raw.h; sourceTree = ""; }; + E96D62542C97C14C009CB254 /* unlzh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unlzh.c; sourceTree = ""; }; + E96D62552C97C14C009CB254 /* mid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mid.h; sourceTree = ""; }; + E96D62562C97C14C009CB254 /* dro2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dro2.h; sourceTree = ""; }; + E96D62572C97C14C009CB254 /* protrack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = protrack.h; sourceTree = ""; }; + E96D62582C97C14C009CB254 /* hybrid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hybrid.cpp; sourceTree = ""; }; + E96D62592C97C14D009CB254 /* database.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = database.h; sourceTree = ""; }; + E96D625A2C97C14D009CB254 /* dtm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dtm.h; sourceTree = ""; }; + E96D625B2C97C14D009CB254 /* nukedopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nukedopl.h; sourceTree = ""; }; + E96D625C2C97C14D009CB254 /* jbm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jbm.cpp; sourceTree = ""; }; + E96D625D2C97C14D009CB254 /* a2m-v2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "a2m-v2.cpp"; sourceTree = ""; }; + E96D625E2C97C14D009CB254 /* sa2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sa2.cpp; sourceTree = ""; }; + E96D625F2C97C14D009CB254 /* vgm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vgm.cpp; sourceTree = ""; }; + E96D62602C97C14E009CB254 /* protrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = protrack.cpp; sourceTree = ""; }; + E96D62612C97C14E009CB254 /* mtk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mtk.cpp; sourceTree = ""; }; + E96D62632C97C14E009CB254 /* amd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = amd.cpp; sourceTree = ""; }; + E96D62642C97C14E009CB254 /* mkj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mkj.h; sourceTree = ""; }; + E96D62652C97C14E009CB254 /* cmfmcsop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmfmcsop.h; sourceTree = ""; }; + E96D62662C97C14F009CB254 /* hsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hsp.cpp; sourceTree = ""; }; + E96D62672C97C14F009CB254 /* dro2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dro2.cpp; sourceTree = ""; }; + E96D62682C97C14F009CB254 /* adlibemu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adlibemu.h; sourceTree = ""; }; + E96D62692C97C14F009CB254 /* hyp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hyp.cpp; sourceTree = ""; }; + E96D626A2C97C14F009CB254 /* dro.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dro.cpp; sourceTree = ""; }; + E96D626B2C97C14F009CB254 /* mkj.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mkj.cpp; sourceTree = ""; }; + E96D626C2C97C14F009CB254 /* nemuopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nemuopl.cpp; sourceTree = ""; }; + E96D626D2C97C150009CB254 /* opl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opl.h; sourceTree = ""; }; + E96D626E2C97C150009CB254 /* players.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = players.h; sourceTree = ""; }; + E96D626F2C97C150009CB254 /* realopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = realopl.h; sourceTree = ""; }; + E96D62702C97C150009CB254 /* surroundopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = surroundopl.h; sourceTree = ""; }; + E96D62712C97C150009CB254 /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = ""; }; + E96D62722C97C150009CB254 /* depack.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = depack.c; sourceTree = ""; }; + E96D62732C97C150009CB254 /* hsp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hsp.h; sourceTree = ""; }; + E96D62742C97C151009CB254 /* strnlen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = strnlen.h; sourceTree = ""; }; + E96D62752C97C151009CB254 /* adtrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = adtrack.cpp; sourceTree = ""; }; + E96D62762C97C151009CB254 /* lds.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lds.cpp; sourceTree = ""; }; + E96D62772C97C151009CB254 /* player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = player.h; sourceTree = ""; }; + E96D62782C97C151009CB254 /* s3m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = s3m.cpp; sourceTree = ""; }; + E96D62792C97C151009CB254 /* composer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = composer.h; sourceTree = ""; }; + E96D627A2C97C152009CB254 /* composer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = composer.cpp; sourceTree = ""; }; + E96D627B2C97C152009CB254 /* ksm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ksm.cpp; sourceTree = ""; }; + E96D627C2C97C152009CB254 /* adtrack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adtrack.h; sourceTree = ""; }; + E96D627D2C97C152009CB254 /* realopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = realopl.cpp; sourceTree = ""; }; + E96D627E2C97C152009CB254 /* adplug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = adplug.h; sourceTree = ""; }; + E96D627F2C97C152009CB254 /* u6m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = u6m.cpp; sourceTree = ""; }; + E96D62802C97C152009CB254 /* mus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mus.h; sourceTree = ""; }; + E96D62812C97C153009CB254 /* imf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = imf.cpp; sourceTree = ""; }; + E96D62822C97C153009CB254 /* analopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = analopl.cpp; sourceTree = ""; }; + E96D62832C97C153009CB254 /* a2m.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = a2m.cpp; sourceTree = ""; }; + E96D62842C97C153009CB254 /* raw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = raw.cpp; sourceTree = ""; }; + E96D62852C97C153009CB254 /* adlibemu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adlibemu.c; sourceTree = ""; }; + E96D62862C97C153009CB254 /* sop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sop.cpp; sourceTree = ""; }; + E96D62872C97C153009CB254 /* xad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xad.cpp; sourceTree = ""; }; + E96D62882C97C154009CB254 /* imf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = imf.h; sourceTree = ""; }; + E96D62892C97C154009CB254 /* sixdepack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sixdepack.cpp; sourceTree = ""; }; + E96D628A2C97C154009CB254 /* unlzw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unlzw.h; sourceTree = ""; }; + E96D628B2C97C154009CB254 /* bam.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bam.cpp; sourceTree = ""; }; + E96D628C2C97C154009CB254 /* sng.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sng.h; sourceTree = ""; }; + E96D628D2C97C154009CB254 /* dmo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dmo.cpp; sourceTree = ""; }; + E96D628E2C97C155009CB254 /* dmo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dmo.h; sourceTree = ""; }; + E96D628F2C97C155009CB254 /* load_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = load_helper.h; sourceTree = ""; }; + E96D62902C97C155009CB254 /* amd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = amd.h; sourceTree = ""; }; + E96D62912C97C155009CB254 /* s3m.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = s3m.h; sourceTree = ""; }; + E96D62922C97C155009CB254 /* fmc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmc.h; sourceTree = ""; }; + E96D62932C97C155009CB254 /* unlzh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unlzh.h; sourceTree = ""; }; + E96D62942C97C155009CB254 /* fprovide.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fprovide.h; sourceTree = ""; }; + E96D62952C97C156009CB254 /* rix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rix.cpp; sourceTree = ""; }; + E96D62962C97C156009CB254 /* hsc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hsc.h; sourceTree = ""; }; + E96D62972C97C156009CB254 /* emuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emuopl.h; sourceTree = ""; }; + E96D62982C97C156009CB254 /* players.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = players.cpp; sourceTree = ""; }; + E96D62992C97C156009CB254 /* debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = debug.c; sourceTree = ""; }; + E96D629A2C97C156009CB254 /* player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = player.cpp; sourceTree = ""; }; + E96D629B2C97C157009CB254 /* wemuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wemuopl.h; sourceTree = ""; }; + E96D629C2C97C157009CB254 /* fmopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fmopl.h; sourceTree = ""; }; + E96D629D2C97C157009CB254 /* mtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mtr.h; sourceTree = ""; }; + E96D629E2C97C157009CB254 /* nemuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nemuopl.h; sourceTree = ""; }; + E96D629F2C97C157009CB254 /* mdi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mdi.cpp; sourceTree = ""; }; + E96D62A02C97C157009CB254 /* xad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xad.h; sourceTree = ""; }; + E96D62A12C97C157009CB254 /* herad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = herad.cpp; sourceTree = ""; }; + E96D62A22C97C158009CB254 /* pis.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pis.cpp; sourceTree = ""; }; + E96D62A32C97C158009CB254 /* xsm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xsm.h; sourceTree = ""; }; + E96D62A42C97C158009CB254 /* nukedopl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nukedopl.c; sourceTree = ""; }; + E96D62A52C97C158009CB254 /* unlzss.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unlzss.h; sourceTree = ""; }; + E96D62A62C97C158009CB254 /* diskopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = diskopl.cpp; sourceTree = ""; }; + E96D62A72C97C158009CB254 /* cmf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmf.h; sourceTree = ""; }; + E96D62A82C97C158009CB254 /* got.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = got.h; sourceTree = ""; }; + E96D62A92C97C159009CB254 /* bam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bam.h; sourceTree = ""; }; + E96D62AA2C97C159009CB254 /* cff.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cff.cpp; sourceTree = ""; }; + E96D62AB2C97C159009CB254 /* dro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dro.h; sourceTree = ""; }; + E96D62AC2C97C159009CB254 /* rad2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rad2.cpp; sourceTree = ""; }; + E96D62AD2C97C159009CB254 /* ksm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ksm.h; sourceTree = ""; }; + E96D62AE2C97C159009CB254 /* unlzss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unlzss.c; sourceTree = ""; }; + E96D62AF2C97C15A009CB254 /* got.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = got.cpp; sourceTree = ""; }; + E96D62B02C97C15A009CB254 /* adplug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = adplug.cpp; sourceTree = ""; }; + E96D62B12C97C15A009CB254 /* bmf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bmf.h; sourceTree = ""; }; + E96D62B22C97C15A009CB254 /* pis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pis.h; sourceTree = ""; }; + E96D62B32C97C15A009CB254 /* hsc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hsc.cpp; sourceTree = ""; }; + E96D62B42C97C15A009CB254 /* kemuopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = kemuopl.cpp; sourceTree = ""; }; + E96D62B52C97C15B009CB254 /* temuopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = temuopl.cpp; sourceTree = ""; }; + E96D62B62C97C15B009CB254 /* mad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mad.h; sourceTree = ""; }; + E96D62B72C97C15B009CB254 /* emuopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = emuopl.cpp; sourceTree = ""; }; + E96D62B82C97C15B009CB254 /* flash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = flash.h; sourceTree = ""; }; + E96D62B92C97C15B009CB254 /* fprovide.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fprovide.cpp; sourceTree = ""; }; + E96D62BA2C97C15B009CB254 /* d00.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = d00.cpp; sourceTree = ""; }; + E96D62BB2C97C15B009CB254 /* mtr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mtr.cpp; sourceTree = ""; }; + E96D62BC2C97C15C009CB254 /* lds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lds.h; sourceTree = ""; }; + E96D62BD2C97C15C009CB254 /* kemuopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kemuopl.h; sourceTree = ""; }; + E96D62BE2C97C15C009CB254 /* msc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = msc.h; sourceTree = ""; }; + E96D62BF2C97C15C009CB254 /* dfm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dfm.h; sourceTree = ""; }; + E96D62C02C97C15C009CB254 /* a2m-v2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "a2m-v2.h"; sourceTree = ""; }; + E96D62C12C97C15C009CB254 /* rat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rat.h; sourceTree = ""; }; + E96D62C22C97C15D009CB254 /* diskopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = diskopl.h; sourceTree = ""; }; + E96D62C32C97C15D009CB254 /* sa2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sa2.h; sourceTree = ""; }; + E96D62C42C97C15D009CB254 /* woodyopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = woodyopl.h; sourceTree = ""; }; + E96D62C52C97C15D009CB254 /* adl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = adl.cpp; sourceTree = ""; }; + E96D62C62C97C15D009CB254 /* dfm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dfm.cpp; sourceTree = ""; }; + E96D62C72C97C15D009CB254 /* sng.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sng.cpp; sourceTree = ""; }; + E96D62C82C97C15D009CB254 /* psi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = psi.cpp; sourceTree = ""; }; + E96D62C92C97C15E009CB254 /* coktel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coktel.h; sourceTree = ""; }; + E96D62CA2C97C15E009CB254 /* mus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mus.cpp; sourceTree = ""; }; + E96D636C2C97C49F009CB254 /* version.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -10102,146 +10145,168 @@ 2D652FDE1CE79ED400163808 /* adplug */ = { isa = PBXGroup; children = ( - 2DC1CDB1240EEBA0000776DB /* a2m.cpp */, - 2DC1CD55240EEB92000776DB /* a2m.h */, - 2DC1CD91240EEB9B000776DB /* adl.cpp */, - 2DC1CD5D240EEB93000776DB /* adl.h */, - 2DC1CD74240EEB97000776DB /* adlib.cpp */, - 2DC1CDB7240EEBA1000776DB /* adlib.h */, - 2DC1CD4A240EEB90000776DB /* adlibemu.c */, - 2DC1CDB2240EEBA1000776DB /* adlibemu.h */, - 2DC1CDC9240EEBA4000776DB /* adplug.cpp */, - 2DC1CDC8240EEBA4000776DB /* adplug.h */, - 2DC1CDAD240EEBA0000776DB /* adtrack.cpp */, - 2DC1CD54240EEB91000776DB /* adtrack.h */, - 2DC1CDA7240EEB9F000776DB /* amd.cpp */, - 2DC1CD4C240EEB90000776DB /* amd.h */, - 2DC1CD70240EEB96000776DB /* analopl.cpp */, - 2DC1CDCE240EEBA5000776DB /* analopl.h */, - 2DC1CD9A240EEB9D000776DB /* bam.cpp */, - 2DC1CDC7240EEBA4000776DB /* bam.h */, - 2DC1CDA2240EEB9E000776DB /* bmf.cpp */, - 2DC1CD6B240EEB95000776DB /* bmf.h */, - 2DC1CD52240EEB91000776DB /* cff.cpp */, - 2DC1CD5B240EEB93000776DB /* cff.h */, - 2DC1CD72240EEB96000776DB /* cmf.cpp */, - 2DC1CD57240EEB92000776DB /* cmf.h */, - 2DC1CD94240EEB9C000776DB /* cmfmcsop.cpp */, - 2DC1CDCB240EEBA5000776DB /* cmfmcsop.h */, - 2DC1CDBB240EEBA2000776DB /* d00.cpp */, - 2DC1CD4B240EEB90000776DB /* d00.h */, - 2DC1CD9F240EEB9E000776DB /* database.cpp */, - 2DC1CD82240EEB99000776DB /* database.h */, - 2DC1CD97240EEB9C000776DB /* debug.c */, - 2DC1CD69240EEB95000776DB /* debug.h */, - 2DC1CD63240EEB94000776DB /* dfm.cpp */, - 2DC1CD73240EEB97000776DB /* dfm.h */, - 2DC1CD9D240EEB9D000776DB /* diskopl.cpp */, - 2DC1CD50240EEB91000776DB /* diskopl.h */, - 2DC1CD7D240EEB98000776DB /* dmo.cpp */, - 2DC1CD99240EEB9D000776DB /* dmo.h */, - 2DC1CD60240EEB94000776DB /* dro.cpp */, - 2DC1CDC6240EEBA4000776DB /* dro.h */, - 2DC1CDC1240EEBA3000776DB /* dro2.cpp */, - 2DC1CDB4240EEBA1000776DB /* dro2.h */, - 2DC1CDA0240EEB9E000776DB /* dtm.cpp */, - 2DC1CD95240EEB9C000776DB /* dtm.h */, - 2DC1CD9B240EEB9D000776DB /* emuopl.cpp */, - 2DC1CD5E240EEB93000776DB /* emuopl.h */, - 2DC1CDBF240EEBA3000776DB /* flash.cpp */, - 2DC1CDCD240EEBA5000776DB /* flash.h */, - 2DC1CDA3240EEB9E000776DB /* fmc.cpp */, - 2DC1CD84240EEB99000776DB /* fmc.h */, - 2DC1CD68240EEB95000776DB /* fmopl.c */, - 2DC1CD7A240EEB98000776DB /* fmopl.h */, - 2DC1CD8E240EEB9B000776DB /* fprovide.cpp */, - 2DC1CD4F240EEB91000776DB /* fprovide.h */, - 2DC1CD4E240EEB91000776DB /* got.cpp */, - 2DC1CDBE240EEBA2000776DB /* got.h */, - 2DC1CD9E240EEB9D000776DB /* herad.cpp */, - 2DC1CDB0240EEBA0000776DB /* herad.h */, - 2DC1CD5F240EEB93000776DB /* hsc.cpp */, - 2DC1CDC2240EEBA3000776DB /* hsc.h */, - 2DC1CDB9240EEBA2000776DB /* hsp.cpp */, - 2DC1CDA1240EEB9E000776DB /* hsp.h */, - 2DC1CDC4240EEBA3000776DB /* hybrid.cpp */, - 2DC1CDCA240EEBA4000776DB /* hybrid.h */, - 2DC1CDBC240EEBA2000776DB /* hyp.cpp */, - 2DC1CD62240EEB94000776DB /* hyp.h */, - 2DC1CD66240EEB95000776DB /* imf.cpp */, - 2DC1CD93240EEB9C000776DB /* imf.h */, - 2DC1CDAE240EEBA0000776DB /* jbm.cpp */, - 2DC1CD88240EEB9A000776DB /* jbm.h */, - 2DC1CDBD240EEBA2000776DB /* kemuopl.h */, - 2DC1CD7F240EEB98000776DB /* ksm.cpp */, - 2DC1CD86240EEB9A000776DB /* ksm.h */, - 2DC1CD7B240EEB98000776DB /* lds.cpp */, - 2DC1CDC3240EEBA3000776DB /* lds.h */, - 2DC1CDB8240EEBA1000776DB /* mad.cpp */, - 2DC1CD6C240EEB95000776DB /* mad.h */, - 2DC1CD8A240EEB9A000776DB /* mdi.cpp */, - 2DC1CD77240EEB97000776DB /* mdi.h */, - 2DC1CD4D240EEB90000776DB /* mid.cpp */, - 2DC1CDB5240EEBA1000776DB /* mid.h */, - 2DC1CD87240EEB9A000776DB /* mididata.h */, - 2DC1CDC5240EEBA4000776DB /* mkj.cpp */, - 2DC1CD85240EEB99000776DB /* mkj.h */, - 2DC1CD49240EEB90000776DB /* msc.cpp */, - 2DC1CD75240EEB97000776DB /* msc.h */, - 2DC1CD6E240EEB96000776DB /* mtk.cpp */, - 2DC1CDAB240EEB9F000776DB /* mtk.h */, - 2DC1CDB6240EEBA1000776DB /* mus.cpp */, - 2DC1CD98240EEB9C000776DB /* mus.h */, - 2DC1CD56240EEB92000776DB /* nemuopl.cpp */, - 2DC1CD71240EEB96000776DB /* nemuopl.h */, - 2DC1CD45240EEB8F000776DB /* nukedopl.c */, - 2DC1CD8B240EEB9A000776DB /* nukedopl.h */, - 2DC1CD51240EEB91000776DB /* opl.h */, - 2DC1CDA8240EEB9F000776DB /* player.cpp */, - 2DC1CD78240EEB97000776DB /* player.h */, - 2DC1CDAA240EEB9F000776DB /* players.cpp */, - 2DC1CD58240EEB92000776DB /* players.h */, - 2DC1CDA5240EEB9E000776DB /* protrack.cpp */, - 2DC1CD59240EEB92000776DB /* protrack.h */, - 2DC1CD83240EEB99000776DB /* psi.cpp */, - 2DC1CDAC240EEBA0000776DB /* psi.h */, - 2DC1CDCC240EEBA5000776DB /* rad.cpp */, - 2DC1CD64240EEB94000776DB /* rad.h */, - 2DC1CD79240EEB97000776DB /* rat.cpp */, - 2DC1CDCF240EEBA5000776DB /* rat.h */, - 2DC1CD61240EEB94000776DB /* raw.cpp */, - 2DC1CDBA240EEBA2000776DB /* raw.h */, - 2DC1CD7C240EEB98000776DB /* realopl.cpp */, - 2DC1CD65240EEB94000776DB /* realopl.h */, - 2DC1CDA4240EEB9E000776DB /* rix.cpp */, - 2DC1CD89240EEB9A000776DB /* rix.h */, - 2DC1CD44240EEB8F000776DB /* rol.cpp */, - 2DC1CDB3240EEBA1000776DB /* rol.h */, - 2DC1CD6F240EEB96000776DB /* s3m.cpp */, - 2DC1CD7E240EEB98000776DB /* s3m.h */, - 2DC1CD53240EEB91000776DB /* sa2.cpp */, - 2DC1CD48240EEB90000776DB /* sa2.h */, - 2DC1CDA9240EEB9F000776DB /* silentopl.h */, - 2DC1CD6A240EEB95000776DB /* sng.cpp */, - 2DC1CD8C240EEB9B000776DB /* sng.h */, - 2DC1CD9C240EEB9D000776DB /* sop.cpp */, - 2DC1CD67240EEB95000776DB /* sop.h */, - 2DC1CD92240EEB9B000776DB /* surroundopl.cpp */, - 2DC1CD96240EEB9C000776DB /* surroundopl.h */, - 2DC1CD8D240EEB9B000776DB /* temuopl.cpp */, - 2DC1CD47240EEB8F000776DB /* temuopl.h */, - 2DC1CDAF240EEBA0000776DB /* u6m.cpp */, - 2DC1CD5A240EEB92000776DB /* u6m.h */, - 2DC1CD76240EEB97000776DB /* version.h */, - 2DC1CD8F240EEB9B000776DB /* vgm.cpp */, - 2DC1CD46240EEB8F000776DB /* vgm.h */, - 2DC1CD81240EEB99000776DB /* wemuopl.h */, - 2DC1CD6D240EEB96000776DB /* woodyopl.cpp */, - 2DC1CD90240EEB9B000776DB /* woodyopl.h */, - 2DC1CDA6240EEB9F000776DB /* xad.cpp */, - 2DC1CD5C240EEB93000776DB /* xad.h */, - 2DC1CD80240EEB99000776DB /* xsm.cpp */, - 2DC1CDC0240EEBA3000776DB /* xsm.h */, + E96D625D2C97C14D009CB254 /* a2m-v2.cpp */, + E96D62C02C97C15C009CB254 /* a2m-v2.h */, + E96D62832C97C153009CB254 /* a2m.cpp */, + E96D62482C97C14A009CB254 /* a2m.h */, + E96D62C52C97C15D009CB254 /* adl.cpp */, + E96D62512C97C14B009CB254 /* adl.h */, + E96D62852C97C153009CB254 /* adlibemu.c */, + E96D62682C97C14F009CB254 /* adlibemu.h */, + E96D62B02C97C15A009CB254 /* adplug.cpp */, + E96D627E2C97C152009CB254 /* adplug.h */, + E96D62752C97C151009CB254 /* adtrack.cpp */, + E96D627C2C97C152009CB254 /* adtrack.h */, + E96D62632C97C14E009CB254 /* amd.cpp */, + E96D62902C97C155009CB254 /* amd.h */, + E96D62822C97C153009CB254 /* analopl.cpp */, + E96D62322C97C147009CB254 /* analopl.h */, + E96D628B2C97C154009CB254 /* bam.cpp */, + E96D62A92C97C159009CB254 /* bam.h */, + E96D622D2C97C146009CB254 /* bmf.cpp */, + E96D62B12C97C15A009CB254 /* bmf.h */, + E96D62AA2C97C159009CB254 /* cff.cpp */, + E96D62392C97C148009CB254 /* cff.h */, + E96D622F2C97C146009CB254 /* cmf.cpp */, + E96D62A72C97C158009CB254 /* cmf.h */, + E96D622A2C97C146009CB254 /* cmfmcsop.cpp */, + E96D62652C97C14E009CB254 /* cmfmcsop.h */, + E96D62422C97C149009CB254 /* coktel.cpp */, + E96D62C92C97C15E009CB254 /* coktel.h */, + E96D627A2C97C152009CB254 /* composer.cpp */, + E96D62792C97C151009CB254 /* composer.h */, + E96D62BA2C97C15B009CB254 /* d00.cpp */, + E96D62522C97C14C009CB254 /* d00.h */, + E96D622C2C97C146009CB254 /* database.cpp */, + E96D62592C97C14D009CB254 /* database.h */, + E96D62992C97C156009CB254 /* debug.c */, + E96D62712C97C150009CB254 /* debug.h */, + E96D62722C97C150009CB254 /* depack.c */, + E96D62502C97C14B009CB254 /* depack.h */, + E96D62C62C97C15D009CB254 /* dfm.cpp */, + E96D62BF2C97C15C009CB254 /* dfm.h */, + E96D62A62C97C158009CB254 /* diskopl.cpp */, + E96D62C22C97C15D009CB254 /* diskopl.h */, + E96D628D2C97C154009CB254 /* dmo.cpp */, + E96D628E2C97C155009CB254 /* dmo.h */, + E96D626A2C97C14F009CB254 /* dro.cpp */, + E96D62AB2C97C159009CB254 /* dro.h */, + E96D62672C97C14F009CB254 /* dro2.cpp */, + E96D62562C97C14C009CB254 /* dro2.h */, + E96D623A2C97C148009CB254 /* dtm.cpp */, + E96D625A2C97C14D009CB254 /* dtm.h */, + E96D62B72C97C15B009CB254 /* emuopl.cpp */, + E96D62972C97C156009CB254 /* emuopl.h */, + E96D624A2C97C14A009CB254 /* flash.cpp */, + E96D62B82C97C15B009CB254 /* flash.h */, + E96D624F2C97C14B009CB254 /* fmc.cpp */, + E96D62922C97C155009CB254 /* fmc.h */, + E96D624C2C97C14B009CB254 /* fmopl.c */, + E96D629C2C97C157009CB254 /* fmopl.h */, + E96D62B92C97C15B009CB254 /* fprovide.cpp */, + E96D62942C97C155009CB254 /* fprovide.h */, + E96D62AF2C97C15A009CB254 /* got.cpp */, + E96D62A82C97C158009CB254 /* got.h */, + E96D62A12C97C157009CB254 /* herad.cpp */, + E96D62402C97C149009CB254 /* herad.h */, + E96D62B32C97C15A009CB254 /* hsc.cpp */, + E96D62962C97C156009CB254 /* hsc.h */, + E96D62662C97C14F009CB254 /* hsp.cpp */, + E96D62732C97C150009CB254 /* hsp.h */, + E96D62582C97C14C009CB254 /* hybrid.cpp */, + E96D62372C97C147009CB254 /* hybrid.h */, + E96D62692C97C14F009CB254 /* hyp.cpp */, + E96D62342C97C147009CB254 /* hyp.h */, + E96D62812C97C153009CB254 /* imf.cpp */, + E96D62882C97C154009CB254 /* imf.h */, + E96D625C2C97C14D009CB254 /* jbm.cpp */, + E96D623E2C97C148009CB254 /* jbm.h */, + E96D62B42C97C15A009CB254 /* kemuopl.cpp */, + E96D62BD2C97C15C009CB254 /* kemuopl.h */, + E96D627B2C97C152009CB254 /* ksm.cpp */, + E96D62AD2C97C159009CB254 /* ksm.h */, + E96D62762C97C151009CB254 /* lds.cpp */, + E96D62BC2C97C15C009CB254 /* lds.h */, + E96D628F2C97C155009CB254 /* load_helper.h */, + E96D624B2C97C14A009CB254 /* mad.cpp */, + E96D62B62C97C15B009CB254 /* mad.h */, + E96D629F2C97C157009CB254 /* mdi.cpp */, + E96D624D2C97C14B009CB254 /* mdi.h */, + E96D623F2C97C149009CB254 /* mid.cpp */, + E96D62552C97C14C009CB254 /* mid.h */, + E96D622B2C97C146009CB254 /* mididata.h */, + E96D626B2C97C14F009CB254 /* mkj.cpp */, + E96D62642C97C14E009CB254 /* mkj.h */, + E96D624E2C97C14B009CB254 /* msc.cpp */, + E96D62BE2C97C15C009CB254 /* msc.h */, + E96D62612C97C14E009CB254 /* mtk.cpp */, + E96D623B2C97C148009CB254 /* mtk.h */, + E96D62BB2C97C15B009CB254 /* mtr.cpp */, + E96D629D2C97C157009CB254 /* mtr.h */, + E96D62CA2C97C15E009CB254 /* mus.cpp */, + E96D62802C97C152009CB254 /* mus.h */, + E96D626C2C97C14F009CB254 /* nemuopl.cpp */, + E96D629E2C97C157009CB254 /* nemuopl.h */, + E96D62A42C97C158009CB254 /* nukedopl.c */, + E96D625B2C97C14D009CB254 /* nukedopl.h */, + E96D626D2C97C150009CB254 /* opl.h */, + E96D62312C97C147009CB254 /* pch.h */, + E96D62A22C97C158009CB254 /* pis.cpp */, + E96D62B22C97C15A009CB254 /* pis.h */, + E96D629A2C97C156009CB254 /* player.cpp */, + E96D62772C97C151009CB254 /* player.h */, + E96D62982C97C156009CB254 /* players.cpp */, + E96D626E2C97C150009CB254 /* players.h */, + E96D62602C97C14E009CB254 /* protrack.cpp */, + E96D62572C97C14C009CB254 /* protrack.h */, + E96D62C82C97C15D009CB254 /* psi.cpp */, + E96D62452C97C14A009CB254 /* psi.h */, + E96D62AC2C97C159009CB254 /* rad2.cpp */, + E96D622E2C97C146009CB254 /* rad2.h */, + E96D62412C97C149009CB254 /* rat.cpp */, + E96D62C12C97C15C009CB254 /* rat.h */, + E96D62842C97C153009CB254 /* raw.cpp */, + E96D62532C97C14C009CB254 /* raw.h */, + E96D627D2C97C152009CB254 /* realopl.cpp */, + E96D626F2C97C150009CB254 /* realopl.h */, + E96D62952C97C156009CB254 /* rix.cpp */, + E96D623C2C97C148009CB254 /* rix.h */, + E96D62302C97C146009CB254 /* rol.cpp */, + E96D62472C97C14A009CB254 /* rol.h */, + E96D62782C97C151009CB254 /* s3m.cpp */, + E96D62912C97C155009CB254 /* s3m.h */, + E96D625E2C97C14D009CB254 /* sa2.cpp */, + E96D62C32C97C15D009CB254 /* sa2.h */, + E96D62432C97C149009CB254 /* silentopl.h */, + E96D62892C97C154009CB254 /* sixdepack.cpp */, + E96D62492C97C14A009CB254 /* sixdepack.h */, + E96D62C72C97C15D009CB254 /* sng.cpp */, + E96D628C2C97C154009CB254 /* sng.h */, + E96D62862C97C153009CB254 /* sop.cpp */, + E96D62332C97C147009CB254 /* sop.h */, + E96D62742C97C151009CB254 /* strnlen.h */, + E96D62442C97C149009CB254 /* surroundopl.cpp */, + E96D62702C97C150009CB254 /* surroundopl.h */, + E96D62B52C97C15B009CB254 /* temuopl.cpp */, + E96D623D2C97C148009CB254 /* temuopl.h */, + E96D627F2C97C152009CB254 /* u6m.cpp */, + E96D62352C97C147009CB254 /* u6m.h */, + E96D62542C97C14C009CB254 /* unlzh.c */, + E96D62932C97C155009CB254 /* unlzh.h */, + E96D62AE2C97C159009CB254 /* unlzss.c */, + E96D62A52C97C158009CB254 /* unlzss.h */, + E96D62362C97C147009CB254 /* unlzw.c */, + E96D628A2C97C154009CB254 /* unlzw.h */, + E96D625F2C97C14D009CB254 /* vgm.cpp */, + E96D62462C97C14A009CB254 /* vgm.h */, + E96D629B2C97C157009CB254 /* wemuopl.h */, + E96D62292C97C145009CB254 /* woodyopl.cpp */, + E96D62C42C97C15D009CB254 /* woodyopl.h */, + E96D62872C97C153009CB254 /* xad.cpp */, + E96D62A02C97C157009CB254 /* xad.h */, + E96D62382C97C148009CB254 /* xsm.cpp */, + E96D62A32C97C158009CB254 /* xsm.h */, + E96D636C2C97C49F009CB254 /* version.h */, ); path = adplug; sourceTree = ""; @@ -13937,83 +14002,94 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 2DC1CE3F240EEBA5000776DB /* rol.h in Headers */, - 2DC1CE14240EEBA5000776DB /* jbm.h in Headers */, - 2DC1CE12240EEBA5000776DB /* ksm.h in Headers */, - 2DC1CDE3240EEBA5000776DB /* cmf.h in Headers */, + E96D636A2C97C15E009CB254 /* coktel.h in Headers */, + E96D63572C97C15E009CB254 /* mad.h in Headers */, + E96D63462C97C15E009CB254 /* unlzss.h in Headers */, + E96D63122C97C15E009CB254 /* debug.h in Headers */, + E96D62E92C97C15E009CB254 /* rol.h in Headers */, + E96D62F82C97C15E009CB254 /* dro2.h in Headers */, + E96D62EA2C97C15E009CB254 /* a2m.h in Headers */, + E96D63622C97C15E009CB254 /* rat.h in Headers */, + E96D63062C97C15E009CB254 /* cmfmcsop.h in Headers */, + E96D62DE2C97C15E009CB254 /* rix.h in Headers */, + E96D62D72C97C15E009CB254 /* u6m.h in Headers */, + E96D62E02C97C15E009CB254 /* jbm.h in Headers */, + E96D63642C97C15E009CB254 /* sa2.h in Headers */, + E96D63212C97C15E009CB254 /* mus.h in Headers */, + E96D63292C97C15E009CB254 /* imf.h in Headers */, + E96D62F92C97C15E009CB254 /* protrack.h in Headers */, + E96D63412C97C15E009CB254 /* xad.h in Headers */, + E96D62E22C97C15E009CB254 /* herad.h in Headers */, + E96D62F32C97C15E009CB254 /* adl.h in Headers */, + E96D62FB2C97C15E009CB254 /* database.h in Headers */, + E96D62D52C97C15E009CB254 /* sop.h in Headers */, + E96D632F2C97C15E009CB254 /* dmo.h in Headers */, 2D6530DC1CE79ED400163808 /* binio.h in Headers */, - 2DC1CE46240EEBA5000776DB /* raw.h in Headers */, - 2DC1CDE1240EEBA5000776DB /* a2m.h in Headers */, - 2DC1CDFD240EEBA5000776DB /* nemuopl.h in Headers */, - 2DC1CE18240EEBA5000776DB /* sng.h in Headers */, - 2DC1CDE6240EEBA5000776DB /* u6m.h in Headers */, - 2DC1CE40240EEBA5000776DB /* dro2.h in Headers */, - 2DC1CE03240EEBA5000776DB /* mdi.h in Headers */, - 2DC1CDF8240EEBA5000776DB /* mad.h in Headers */, - 2DC1CE49240EEBA6000776DB /* kemuopl.h in Headers */, - 2DC1CE5A240EEBA6000776DB /* analopl.h in Headers */, - 2DC1CE1F240EEBA5000776DB /* imf.h in Headers */, - 2DC1CDEA240EEBA5000776DB /* emuopl.h in Headers */, + E96D62DD2C97C15E009CB254 /* mtk.h in Headers */, + E96D62DF2C97C15E009CB254 /* temuopl.h in Headers */, + E96D63302C97C15E009CB254 /* load_helper.h in Headers */, + E96D62F42C97C15E009CB254 /* d00.h in Headers */, + E96D62F22C97C15E009CB254 /* depack.h in Headers */, + E96D62E52C97C15E009CB254 /* silentopl.h in Headers */, + E96D63372C97C15E009CB254 /* hsc.h in Headers */, + E96D632B2C97C15E009CB254 /* unlzw.h in Headers */, + E96D635E2C97C15E009CB254 /* kemuopl.h in Headers */, + E96D634A2C97C15E009CB254 /* bam.h in Headers */, + E96D632D2C97C15E009CB254 /* sng.h in Headers */, + E96D62E82C97C15E009CB254 /* vgm.h in Headers */, + E96D62F72C97C15E009CB254 /* mid.h in Headers */, + E96D62CD2C97C15E009CB254 /* mididata.h in Headers */, + E96D633C2C97C15E009CB254 /* wemuopl.h in Headers */, + E96D63142C97C15E009CB254 /* hsp.h in Headers */, + E96D63102C97C15E009CB254 /* realopl.h in Headers */, + E96D63532C97C15E009CB254 /* pis.h in Headers */, + E96D63492C97C15E009CB254 /* got.h in Headers */, + E96D631F2C97C15E009CB254 /* adplug.h in Headers */, + E96D633F2C97C15E009CB254 /* nemuopl.h in Headers */, + E96D631A2C97C15E009CB254 /* composer.h in Headers */, + E96D635F2C97C15E009CB254 /* msc.h in Headers */, + E96D62D32C97C15E009CB254 /* pch.h in Headers */, 2D6530DA1CE79ED400163808 /* binfile.h in Headers */, - 2DC1CE41240EEBA5000776DB /* mid.h in Headers */, - 2DC1CDD2240EEBA5000776DB /* vgm.h in Headers */, - 2DC1CDD8240EEBA5000776DB /* amd.h in Headers */, - 2DC1CDE0240EEBA5000776DB /* adtrack.h in Headers */, - 2DC1CDF5240EEBA5000776DB /* debug.h in Headers */, - 2DC1CE4C240EEBA6000776DB /* xsm.h in Headers */, - 2DC1CE15240EEBA5000776DB /* rix.h in Headers */, - 2DC1CE17240EEBA5000776DB /* nukedopl.h in Headers */, + E96D62FC2C97C15E009CB254 /* dtm.h in Headers */, + E96D62F52C97C15E009CB254 /* raw.h in Headers */, + E96D634C2C97C15E009CB254 /* dro.h in Headers */, + E96D63152C97C15E009CB254 /* strnlen.h in Headers */, + E96D63312C97C15E009CB254 /* amd.h in Headers */, + E96D62EF2C97C15E009CB254 /* mdi.h in Headers */, + E96D62DB2C97C15E009CB254 /* cff.h in Headers */, + E96D633E2C97C15E009CB254 /* mtr.h in Headers */, + E96D62D02C97C15E009CB254 /* rad2.h in Headers */, 2D6530E01CE79ED400163808 /* binwrap.h in Headers */, - 2DC1CE24240EEBA5000776DB /* mus.h in Headers */, + E96D63092C97C15E009CB254 /* adlibemu.h in Headers */, + E96D62D92C97C15E009CB254 /* hybrid.h in Headers */, + E96D635D2C97C15E009CB254 /* lds.h in Headers */, + E96D63342C97C15E009CB254 /* unlzh.h in Headers */, + E96D62D42C97C15E009CB254 /* analopl.h in Headers */, + E96D63602C97C15E009CB254 /* dfm.h in Headers */, + E96D62D62C97C15E009CB254 /* hyp.h in Headers */, + E96D63612C97C15E009CB254 /* a2m-v2.h in Headers */, + E96D63352C97C15E009CB254 /* fprovide.h in Headers */, + E96D630E2C97C15E009CB254 /* opl.h in Headers */, + E96D63482C97C15E009CB254 /* cmf.h in Headers */, + E96D63382C97C15E009CB254 /* emuopl.h in Headers */, + E96D62EB2C97C15E009CB254 /* sixdepack.h in Headers */, + E96D63322C97C15E009CB254 /* s3m.h in Headers */, + E96D634E2C97C15E009CB254 /* ksm.h in Headers */, + E96D63652C97C15E009CB254 /* woodyopl.h in Headers */, + E96D63112C97C15E009CB254 /* surroundopl.h in Headers */, + E96D631D2C97C15E009CB254 /* adtrack.h in Headers */, 2D6530DE1CE79ED400163808 /* binstr.h in Headers */, - 2DC1CE06240EEBA5000776DB /* fmopl.h in Headers */, - 2DC1CE13240EEBA5000776DB /* mididata.h in Headers */, - 2DC1CE43240EEBA5000776DB /* adlib.h in Headers */, - 2DC1CE56240EEBA6000776DB /* hybrid.h in Headers */, - 2DC1CDF1240EEBA5000776DB /* realopl.h in Headers */, - 2DC1CDDD240EEBA5000776DB /* opl.h in Headers */, - 2DC1CE4F240EEBA6000776DB /* lds.h in Headers */, - 2DC1CE0A240EEBA5000776DB /* s3m.h in Headers */, - 2DC1CE4A240EEBA6000776DB /* got.h in Headers */, - 2DC1CE38240EEBA5000776DB /* psi.h in Headers */, - 2DC1CDD4240EEBA5000776DB /* sa2.h in Headers */, - 2DC1CE04240EEBA5000776DB /* player.h in Headers */, - 2DC1CE57240EEBA6000776DB /* cmfmcsop.h in Headers */, - 2DC1CDDB240EEBA5000776DB /* fprovide.h in Headers */, - 2DC1CE59240EEBA6000776DB /* flash.h in Headers */, - 2DC1CDD3240EEBA5000776DB /* temuopl.h in Headers */, - 2DC1CE37240EEBA5000776DB /* mtk.h in Headers */, - 2DC1CE35240EEBA5000776DB /* silentopl.h in Headers */, - 2DC1CE22240EEBA5000776DB /* surroundopl.h in Headers */, - 2DC1CE3C240EEBA5000776DB /* herad.h in Headers */, - 2DC1CDDC240EEBA5000776DB /* diskopl.h in Headers */, - 2DC1CE0E240EEBA5000776DB /* database.h in Headers */, - 2DC1CDEE240EEBA5000776DB /* hyp.h in Headers */, - 2DC1CE3E240EEBA5000776DB /* adlibemu.h in Headers */, - 2DC1CDD7240EEBA5000776DB /* d00.h in Headers */, - 2DC1CE2D240EEBA5000776DB /* hsp.h in Headers */, - 2DC1CE5B240EEBA6000776DB /* rat.h in Headers */, - 2DC1CDF0240EEBA5000776DB /* rad.h in Headers */, - 2DC1CDE8240EEBA5000776DB /* xad.h in Headers */, - 2DC1CDE4240EEBA5000776DB /* players.h in Headers */, - 2DC1CE11240EEBA5000776DB /* mkj.h in Headers */, - 2DC1CDFF240EEBA5000776DB /* dfm.h in Headers */, - 2DC1CDE9240EEBA5000776DB /* adl.h in Headers */, - 2DC1CE21240EEBA5000776DB /* dtm.h in Headers */, - 2DC1CE4E240EEBA6000776DB /* hsc.h in Headers */, - 2DC1CE54240EEBA6000776DB /* adplug.h in Headers */, - 2DC1CE53240EEBA6000776DB /* bam.h in Headers */, - 2DC1CDF7240EEBA5000776DB /* bmf.h in Headers */, - 2DC1CE10240EEBA5000776DB /* fmc.h in Headers */, - 2DC1CE0D240EEBA5000776DB /* wemuopl.h in Headers */, - 2DC1CE02240EEBA5000776DB /* version.h in Headers */, - 2DC1CDE5240EEBA5000776DB /* protrack.h in Headers */, - 2DC1CE25240EEBA5000776DB /* dmo.h in Headers */, - 2DC1CDE7240EEBA5000776DB /* cff.h in Headers */, - 2DC1CE52240EEBA6000776DB /* dro.h in Headers */, - 2DC1CE1C240EEBA5000776DB /* woodyopl.h in Headers */, - 2DC1CE01240EEBA5000776DB /* msc.h in Headers */, - 2DC1CDF3240EEBA5000776DB /* sop.h in Headers */, + E96D63182C97C15E009CB254 /* player.h in Headers */, + E96D63632C97C15E009CB254 /* diskopl.h in Headers */, + E96D63332C97C15E009CB254 /* fmc.h in Headers */, + E96D633D2C97C15E009CB254 /* fmopl.h in Headers */, + E96D63052C97C15E009CB254 /* mkj.h in Headers */, + E96D62FD2C97C15E009CB254 /* nukedopl.h in Headers */, + E96D62E72C97C15E009CB254 /* psi.h in Headers */, + E96D63522C97C15E009CB254 /* bmf.h in Headers */, + E96D63592C97C15E009CB254 /* flash.h in Headers */, + E96D630F2C97C15E009CB254 /* players.h in Headers */, + E96D63442C97C15E009CB254 /* xsm.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -17587,79 +17663,89 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2DC1CE26240EEBA5000776DB /* bam.cpp in Sources */, - 2DC1CE23240EEBA5000776DB /* debug.c in Sources */, - 2DC1CE28240EEBA5000776DB /* sop.cpp in Sources */, - 2DC1CDF2240EEBA5000776DB /* imf.cpp in Sources */, - 2DC1CE36240EEBA5000776DB /* players.cpp in Sources */, - 2DC1CE20240EEBA5000776DB /* cmfmcsop.cpp in Sources */, - 2DC1CE3B240EEBA5000776DB /* u6m.cpp in Sources */, - 2DC1CDED240EEBA5000776DB /* raw.cpp in Sources */, - 2DC1CE2E240EEBA5000776DB /* bmf.cpp in Sources */, - 2DC1CDE2240EEBA5000776DB /* nemuopl.cpp in Sources */, - 2DC1CE0B240EEBA5000776DB /* ksm.cpp in Sources */, - 2DC1CE4D240EEBA6000776DB /* dro2.cpp in Sources */, - 2DC1CE39240EEBA5000776DB /* adtrack.cpp in Sources */, - 2DC1CE47240EEBA5000776DB /* d00.cpp in Sources */, - 2DC1CE42240EEBA5000776DB /* mus.cpp in Sources */, - 2DC1CE51240EEBA6000776DB /* mkj.cpp in Sources */, - 2DC1CE07240EEBA5000776DB /* lds.cpp in Sources */, - 2DC1CE33240EEBA5000776DB /* amd.cpp in Sources */, - 2DC1CE2F240EEBA5000776DB /* fmc.cpp in Sources */, - 2DC1CDD6240EEBA5000776DB /* adlibemu.c in Sources */, - 2DC1CE16240EEBA5000776DB /* mdi.cpp in Sources */, + E96D63192C97C15E009CB254 /* s3m.cpp in Sources */, + E96D631E2C97C15E009CB254 /* realopl.cpp in Sources */, + E96D62EE2C97C15E009CB254 /* fmopl.c in Sources */, + E96D62D82C97C15E009CB254 /* unlzw.c in Sources */, + E96D63432C97C15E009CB254 /* pis.cpp in Sources */, + E96D634B2C97C15E009CB254 /* cff.cpp in Sources */, + E96D63692C97C15E009CB254 /* psi.cpp in Sources */, + E96D634D2C97C15E009CB254 /* rad2.cpp in Sources */, + E96D63202C97C15E009CB254 /* u6m.cpp in Sources */, + E96D633B2C97C15E009CB254 /* player.cpp in Sources */, + E96D631C2C97C15E009CB254 /* ksm.cpp in Sources */, + E96D63452C97C15E009CB254 /* nukedopl.c in Sources */, + E96D63582C97C15E009CB254 /* emuopl.cpp in Sources */, + E96D635B2C97C15E009CB254 /* d00.cpp in Sources */, 2D6530D81CE79ED400163808 /* adplug-db.cpp in Sources */, - 2DC1CDD9240EEBA5000776DB /* mid.cpp in Sources */, - 2DC1CE1D240EEBA5000776DB /* adl.cpp in Sources */, - 2DC1CE34240EEBA5000776DB /* player.cpp in Sources */, - 2DC1CDD0240EEBA5000776DB /* rol.cpp in Sources */, - 2DC1CE3A240EEBA5000776DB /* jbm.cpp in Sources */, + E96D63362C97C15E009CB254 /* rix.cpp in Sources */, + E96D63172C97C15E009CB254 /* lds.cpp in Sources */, + E96D632C2C97C15E009CB254 /* bam.cpp in Sources */, + E96D631B2C97C15E009CB254 /* composer.cpp in Sources */, + E96D63552C97C15E009CB254 /* kemuopl.cpp in Sources */, + E96D63082C97C15E009CB254 /* dro2.cpp in Sources */, + E96D62E12C97C15E009CB254 /* mid.cpp in Sources */, + E96D635C2C97C15E009CB254 /* mtr.cpp in Sources */, + E96D63032C97C15E009CB254 /* mtk.cpp in Sources */, + E96D630C2C97C15E009CB254 /* mkj.cpp in Sources */, + E96D63682C97C15E009CB254 /* sng.cpp in Sources */, + E96D636B2C97C15E009CB254 /* mus.cpp in Sources */, + E96D63072C97C15E009CB254 /* hsp.cpp in Sources */, + E96D63562C97C15E009CB254 /* temuopl.cpp in Sources */, + E96D62E42C97C15E009CB254 /* coktel.cpp in Sources */, + E96D62FA2C97C15E009CB254 /* hybrid.cpp in Sources */, + E96D63422C97C15E009CB254 /* herad.cpp in Sources */, + E96D63022C97C15E009CB254 /* protrack.cpp in Sources */, + E96D62E62C97C15E009CB254 /* surroundopl.cpp in Sources */, + E96D634F2C97C15E009CB254 /* unlzss.c in Sources */, + E96D63392C97C15E009CB254 /* players.cpp in Sources */, + E96D63242C97C15E009CB254 /* a2m.cpp in Sources */, + E96D62F12C97C15E009CB254 /* fmc.cpp in Sources */, + E96D63502C97C15E009CB254 /* got.cpp in Sources */, + E96D62FF2C97C15E009CB254 /* a2m-v2.cpp in Sources */, + E96D62F02C97C15E009CB254 /* msc.cpp in Sources */, + E96D62CB2C97C15E009CB254 /* woodyopl.cpp in Sources */, 2D6530DD1CE79ED400163808 /* binstr.cpp in Sources */, - 2DC1CDEB240EEBA5000776DB /* hsc.cpp in Sources */, - 2DC1CE31240EEBA5000776DB /* protrack.cpp in Sources */, - 2DC1CDF9240EEBA5000776DB /* woodyopl.cpp in Sources */, - 2DC1CE30240EEBA5000776DB /* rix.cpp in Sources */, - 2DC1CE00240EEBA5000776DB /* adlib.cpp in Sources */, - 2DC1CE29240EEBA5000776DB /* diskopl.cpp in Sources */, - 2DC1CE4B240EEBA6000776DB /* flash.cpp in Sources */, + E96D63272C97C15E009CB254 /* sop.cpp in Sources */, + E96D62CF2C97C15E009CB254 /* bmf.cpp in Sources */, + E96D62D12C97C15E009CB254 /* cmf.cpp in Sources */, 2D6530DB1CE79ED400163808 /* binio.cpp in Sources */, - 2DC1CDFC240EEBA5000776DB /* analopl.cpp in Sources */, - 2DC1CE32240EEBA5000776DB /* xad.cpp in Sources */, - 2DC1CDEC240EEBA5000776DB /* dro.cpp in Sources */, - 2DC1CE05240EEBA5000776DB /* rat.cpp in Sources */, - 2DC1CDFA240EEBA5000776DB /* mtk.cpp in Sources */, - 2DC1CE3D240EEBA5000776DB /* a2m.cpp in Sources */, - 2DC1CDEF240EEBA5000776DB /* dfm.cpp in Sources */, - 2DC1CDDF240EEBA5000776DB /* sa2.cpp in Sources */, - 2DC1CE08240EEBA5000776DB /* realopl.cpp in Sources */, - 2DC1CE48240EEBA6000776DB /* hyp.cpp in Sources */, - 2DC1CE27240EEBA5000776DB /* emuopl.cpp in Sources */, - 2DC1CE44240EEBA5000776DB /* mad.cpp in Sources */, - 2DC1CE2C240EEBA5000776DB /* dtm.cpp in Sources */, - 2DC1CE19240EEBA5000776DB /* temuopl.cpp in Sources */, - 2DC1CE2B240EEBA5000776DB /* database.cpp in Sources */, - 2DC1CDDE240EEBA5000776DB /* cff.cpp in Sources */, - 2DC1CDDA240EEBA5000776DB /* got.cpp in Sources */, - 2DC1CDF4240EEBA5000776DB /* fmopl.c in Sources */, - 2DC1CE1A240EEBA5000776DB /* fprovide.cpp in Sources */, - 2DC1CE1B240EEBA5000776DB /* vgm.cpp in Sources */, - 2DC1CE0C240EEBA5000776DB /* xsm.cpp in Sources */, + E96D632A2C97C15E009CB254 /* sixdepack.cpp in Sources */, + E96D632E2C97C15E009CB254 /* dmo.cpp in Sources */, + E96D63512C97C15E009CB254 /* adplug.cpp in Sources */, + E96D63672C97C15E009CB254 /* dfm.cpp in Sources */, + E96D63162C97C15E009CB254 /* adtrack.cpp in Sources */, 2D6530D91CE79ED400163808 /* binfile.cpp in Sources */, - 2DC1CE09240EEBA5000776DB /* dmo.cpp in Sources */, - 2DC1CE0F240EEBA5000776DB /* psi.cpp in Sources */, - 2DC1CDD1240EEBA5000776DB /* nukedopl.c in Sources */, - 2DC1CDFE240EEBA5000776DB /* cmf.cpp in Sources */, - 2DC1CDD5240EEBA5000776DB /* msc.cpp in Sources */, + E96D63002C97C15E009CB254 /* sa2.cpp in Sources */, + E96D630A2C97C15E009CB254 /* hyp.cpp in Sources */, + E96D62CC2C97C15E009CB254 /* cmfmcsop.cpp in Sources */, + E96D63472C97C15E009CB254 /* diskopl.cpp in Sources */, + E96D63282C97C15E009CB254 /* xad.cpp in Sources */, + E96D63012C97C15E009CB254 /* vgm.cpp in Sources */, + E96D63232C97C15E009CB254 /* analopl.cpp in Sources */, + E96D63402C97C15E009CB254 /* mdi.cpp in Sources */, + E96D63132C97C15E009CB254 /* depack.c in Sources */, + E96D62E32C97C15E009CB254 /* rat.cpp in Sources */, + E96D63542C97C15E009CB254 /* hsc.cpp in Sources */, + E96D62F62C97C15E009CB254 /* unlzh.c in Sources */, + E96D630D2C97C15E009CB254 /* nemuopl.cpp in Sources */, + E96D63222C97C15E009CB254 /* imf.cpp in Sources */, + E96D62DC2C97C15E009CB254 /* dtm.cpp in Sources */, + E96D63042C97C15E009CB254 /* amd.cpp in Sources */, + E96D63662C97C15E009CB254 /* adl.cpp in Sources */, + E96D630B2C97C15E009CB254 /* dro.cpp in Sources */, + E96D635A2C97C15E009CB254 /* fprovide.cpp in Sources */, 2D6530DF1CE79ED400163808 /* binwrap.cpp in Sources */, - 2DC1CE55240EEBA6000776DB /* adplug.cpp in Sources */, - 2DC1CE1E240EEBA5000776DB /* surroundopl.cpp in Sources */, - 2DC1CDF6240EEBA5000776DB /* sng.cpp in Sources */, - 2DC1CE50240EEBA6000776DB /* hybrid.cpp in Sources */, - 2DC1CE58240EEBA6000776DB /* rad.cpp in Sources */, + E96D62ED2C97C15E009CB254 /* mad.cpp in Sources */, + E96D633A2C97C15E009CB254 /* debug.c in Sources */, + E96D62D22C97C15E009CB254 /* rol.cpp in Sources */, + E96D62EC2C97C15E009CB254 /* flash.cpp in Sources */, 2D6530E11CE79ED400163808 /* plugin.c in Sources */, - 2DC1CE45240EEBA5000776DB /* hsp.cpp in Sources */, - 2DC1CE2A240EEBA5000776DB /* herad.cpp in Sources */, - 2DC1CDFB240EEBA5000776DB /* s3m.cpp in Sources */, + E96D62FE2C97C15E009CB254 /* jbm.cpp in Sources */, + E96D63252C97C15E009CB254 /* raw.cpp in Sources */, + E96D62DA2C97C15E009CB254 /* xsm.cpp in Sources */, + E96D63262C97C15E009CB254 /* adlibemu.c in Sources */, + E96D62CE2C97C15E009CB254 /* database.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/plugins/adplug/adplug-db.cpp b/plugins/adplug/adplug-db.cpp index c210d957f9..0af2510254 100644 --- a/plugins/adplug/adplug-db.cpp +++ b/plugins/adplug/adplug-db.cpp @@ -1,6 +1,7 @@ /* DeaDBeeF ADPLUG plugin Copyright (C) 2009-2014 Oleksiy Yakovenko + Copyright (C) 2024 Thomas Jepp This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -27,8 +28,12 @@ #include #include #include "adplug.h" +#include "opl.h" #include "emuopl.h" #include "kemuopl.h" +#include "nemuopl.h" +#include "temuopl.h" +#include "wemuopl.h" #include "surroundopl.h" #include "silentopl.h" @@ -66,35 +71,67 @@ adplug_open (uint32_t hints) { return _info; } +Copl* +adplug_create_opl(int samplerate, bool is16bit, bool stereo) { + COPLprops aprops, bprops; + int synth = deadbeef->conf_get_int("adplug.synth", 0); + + Copl* opl; + + switch (synth) { + case 0: // NukedOPL3 + default: + opl = new CNemuopl(samplerate); + trace("adplug: created CNemuopl instance\n"); + break; + + case 1: // DOSBox OPL3 + opl = new CWemuopl(samplerate, is16bit, stereo); + trace("adplug: created CWemuopl instance\n"); + break; + + case 2: // Tatsuyuki Satoh's OPL2 emulator + opl = new CTemuopl(samplerate, is16bit, stereo); + trace("adplug: created CTemuopl instance\n"); + break; + + case 3: // Ken Silverman's OPL emulator + aprops.opl = new CKemuopl(samplerate, is16bit, false); + aprops.use16bit = is16bit; + aprops.stereo = false; + bprops.opl = new CKemuopl(samplerate, is16bit, false); + bprops.use16bit = is16bit; + bprops.stereo = false; + opl = new CSurroundopl(&aprops, &bprops, is16bit); + trace("adplug: created CKemuopl instance\n"); + break; + + case 4: // Simon Peter's OPL emulator + aprops.opl = new CEmuopl(samplerate, is16bit, false); + aprops.use16bit = is16bit; + aprops.stereo = false; + bprops.opl = new CEmuopl(samplerate, is16bit, false); + bprops.use16bit = is16bit; + bprops.stereo = false; + opl = new CSurroundopl(&aprops, &bprops, is16bit); + trace("adplug: created Cemuopl instance\n"); + break; + + } + + return opl; +} + int adplug_init (DB_fileinfo_t *_info, DB_playItem_t *it) { // prepare to decode the track // return -1 on failure adplug_info_t *info = (adplug_info_t *)_info; - int samplerate = deadbeef->conf_get_int ("synth.samplerate", 44100); + int samplerate = deadbeef->conf_get_int ("adplug.samplerate", 49716); int bps = 16; // NOTE: there's no need to support 8bit input, because adplug simply downgrades 16bit signal to 8bits int channels = 2; - if (deadbeef->conf_get_int ("adplug.surround", 1)) { - if (deadbeef->conf_get_int ("adplug.use_ken", 0)) { - Copl *a = new CKemuopl(samplerate, bps == 16, false); - Copl *b = new CKemuopl(samplerate, bps == 16, false); - info->opl = new CSurroundopl(a, b, bps == 16); - } - else { - Copl *a = new CEmuopl(samplerate, bps == 16, false); - Copl *b = new CEmuopl(samplerate, bps == 16, false); - info->opl = new CSurroundopl(a, b, bps == 16); - } - } - else { - if (deadbeef->conf_get_int ("adplug.use_ken", 0)) { - info->opl = new CKemuopl (samplerate, bps == 16, channels == 2); - } - else { - info->opl = new CEmuopl (samplerate, bps == 16, channels == 2); - } - } + info->opl = adplug_create_opl(samplerate, bps == 16, channels == 2); deadbeef->pl_lock (); const char *uri = strdupa (deadbeef->pl_find_meta (it, ":URI")); deadbeef->pl_unlock (); @@ -119,7 +156,7 @@ adplug_init (DB_fileinfo_t *_info, DB_playItem_t *it) { _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); _info->readpos = 0; - trace ("adplug_init ok (songlength=%d, duration=%f, totalsamples=%d)\n", info->decoder->songlength (info->subsong), deadbeef->pl_get_item_duration (it), info->totalsamples); + trace ("adplug_init ok (samplerate=%d, bps=%d, channels=%d, songlength=%d, duration=%f, totalsamples=%d)\n", samplerate, bps, channels, info->decoder->songlength (info->subsong), deadbeef->pl_get_item_duration (it), info->totalsamples); return 0; } diff --git a/plugins/adplug/adplug/a2m-v2.cpp b/plugins/adplug/adplug/a2m-v2.cpp new file mode 100644 index 0000000000..7c678d883b --- /dev/null +++ b/plugins/adplug/adplug/a2m-v2.cpp @@ -0,0 +1,4034 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2008 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * a2m-v2.cpp - Adlib Tracker II Player by Dmitry Smagin + * Originally by Stanislav Baranec + * + * NOTES: + * This player loads a2m and a2t modules versions 1 - 14. + * The code is adapted directly from FreePascal sources of the Adlib Tracker II + * + * REFERENCES: + * https://github.com/ijsf/at2 + * http://www.adlibtracker.net/ + * + */ + +#include "a2m-v2.h" +#include "debug.h" +#include +#include + +static const uint8_t _panning[3] = { 0x30, 0x10, 0x20 }; +static const uint8_t def_vibtrem_table[256] = { + 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, + 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, + 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, + 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, + 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, + 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, + 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, + 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, + 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, + 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, + 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, + 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, + 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, + 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, + 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, + 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24 +}; + +/*** public methods *************************************/ + +CPlayer *Ca2mv2Player::factory(Copl *newopl) +{ + return new Ca2mv2Player(newopl); +} + +Ca2mv2Player::Ca2mv2Player(Copl *newopl) : CPlayer(newopl) +{ + songinfo = new tSONGINFO(); + instrinfo = new tINSTR_INFO(); + eventsinfo = new tEVENTS_INFO(); + ch = new tCHDATA(); +} + +Ca2mv2Player::~Ca2mv2Player() +{ + arpvib_tables_free(); + patterns_free(); + instruments_free(); + + delete songinfo; + delete instrinfo; + delete eventsinfo; + delete ch; +} + +bool Ca2mv2Player::update() +{ + newtimer(); + + return !songend; +} + +void Ca2mv2Player::rewind(int subsong) +{ + chip = 0; + opl->init(); + opl->setchip(0); + + init_player(); + + songend = false; + current_order = 0; + last_order = 0xff; + current_pattern = songinfo->pattern_order[current_order]; + current_line = 0; + pattern_break = false; + pattern_delay = false; + tickXF = 0; + ticks = 0; + next_line = 0; + irq_mode = true; + play_status = isPlaying; + + ticklooper = 0; + macro_ticklooper = 0; + speed = songinfo->speed; + macro_speedup = songinfo->macro_speedup; + update_timer(songinfo->tempo); +} + +float Ca2mv2Player::getrefresh() +{ + return (float)tempo * _macro_speedup(); +} + +std::string Ca2mv2Player::gettype() { + char tmpstr[42]; + + snprintf(tmpstr, sizeof (tmpstr), "Adlib Tracker 2 (%sversion %d)", (type == 1 ? "tiny module " : ""), ffver); + return std::string(tmpstr); +}; + +bool Ca2mv2Player::load(const std::string &filename, const CFileProvider &fp) +{ + binistream *f = fp.open(filename); + if (!f) + return false; + + if (!fp.extension(filename, ".a2m") && !fp.extension(filename, ".a2t")) { + fp.close(f); + return false; + } + + // Read the whole file + unsigned long size = fp.filesize(f); + char *tune = (char *)calloc(1, size); + f->readString(tune, size); + fp.close(f); + + bool result = a2_import(tune, size); + + free(tune); + + if (result) + rewind(0); + + return result; +} + +// Helpers for instruments ======================================================================== + +void Ca2mv2Player::instruments_free() +{ + if (instrinfo->instruments) { + for (unsigned int i = 0; i < instrinfo->count; i++) { + if (instrinfo->instruments[i].fmreg) { + free(instrinfo->instruments[i].fmreg); + instrinfo->instruments[i].fmreg = NULL; + } + } + + free(instrinfo->instruments); + instrinfo->instruments = NULL; + instrinfo->count = 0; + instrinfo->size = 0; + } +} + +void Ca2mv2Player::instruments_allocate(size_t number) +{ + if (editor_mode) { + number = 255; // Allocate max possible + } + + instruments_free(); + + size_t size = number * sizeof(tINSTR_DATA_EXT); + instrinfo->instruments = (tINSTR_DATA_EXT *)calloc(1, size); + assert(instrinfo->instruments); + instrinfo->count = number; + instrinfo->size = size; +} + +tINSTR_DATA_EXT *Ca2mv2Player::get_instr(uint8_t ins) +{ + if (ins == 0 || ins > instrinfo->count ) { + return NULL; + } + + return &instrinfo->instruments[ins - 1]; +} + +inline int8_t Ca2mv2Player::get_instr_fine_tune(uint8_t ins) +{ + tINSTR_DATA_EXT *instrument = get_instr(ins); + + return instrument ? instrument->instr_data.fine_tune : 0; +} + +inline tINSTR_DATA *Ca2mv2Player::get_instr_data_by_ch(int chan) +{ + tINSTR_DATA_EXT *instrument = get_instr(ch->voice_table[chan]); + + return instrument ? &instrument->instr_data : NULL; +} + +inline tINSTR_DATA *Ca2mv2Player::get_instr_data(uint8_t ins) +{ + tINSTR_DATA_EXT *instrument = get_instr(ins); + + return instrument ? &instrument->instr_data : NULL; +} + +// Helpers for macro tables ======================================================================= + +void Ca2mv2Player::fmreg_table_allocate(size_t n, tFMREG_TABLE rt[]) +{ + n = editor_mode ? 255 : n; + + // Note: for editor_mode allocate max entries possible + for (unsigned int i = 0; i < n; i++) { + if (editor_mode || rt[i].length) { + tINSTR_DATA_EXT *instrument = get_instr(i + 1); + assert(instrument); + if (!instrument) + continue; + + instrument->fmreg = (tFMREG_TABLE *)calloc(1, sizeof(tFMREG_TABLE)); + assert(instrument->fmreg); + *instrument->fmreg = rt[i]; // copy struct + } + } +} + +void Ca2mv2Player::disabled_fmregs_import(size_t n, bool dis_fmregs[255][28]) +{ + n = editor_mode ? 255 : n; + + // shrink bool[255][28] to uint32_t[255], use bits as enable/disable flag + for (unsigned int i = 0; i < n; i++) { + uint32_t result = 0; // all enabled by default + for (unsigned int bit = 0; bit < 28; bit++) { + result |= (dis_fmregs[i][bit] & 1) << bit; + } + + tINSTR_DATA_EXT *instrument = get_instr(i + 1); + assert(instrument); + if (!instrument) + continue; + + instrument->dis_fmreg_cols = result; + } +} + +void Ca2mv2Player::arpvib_tables_free() +{ + if (!vibrato_table || !arpeggio_table) + return; + + for (unsigned int i = 0; i < arpvib_count; i++) { + free(vibrato_table[i]); + free(arpeggio_table[i]); + vibrato_table[i] = 0; + arpeggio_table[i] = 0; + } + + delete[] vibrato_table; + delete[] arpeggio_table; +} + +void Ca2mv2Player::arpvib_tables_allocate(size_t n, tARPVIB_TABLE mt[]) +{ + arpvib_tables_free(); + + // Note: for editor_mode allocate max entries possible + n = editor_mode ? 255 : n; + + vibrato_table = new tVIBRATO_TABLE *[n](); + arpeggio_table = new tARPEGGIO_TABLE *[n](); + arpvib_count = n; + + for (unsigned int i = 0; i < n; i++) { + if (editor_mode || mt[i].vibrato.length) { + vibrato_table[i] = (tVIBRATO_TABLE *)calloc(1, sizeof(tVIBRATO_TABLE)); + *vibrato_table[i] = mt[i].vibrato; // copy struct + } + if (editor_mode || mt[i].arpeggio.length) { + arpeggio_table[i] = (tARPEGGIO_TABLE *)calloc(1, sizeof(tARPEGGIO_TABLE)); + *arpeggio_table[i] = mt[i].arpeggio; // copy struct + } + } +} + +tARPEGGIO_TABLE *Ca2mv2Player::get_arpeggio_table(uint8_t arp_table) +{ + return arp_table && arpeggio_table && arpeggio_table[arp_table - 1] ? arpeggio_table[arp_table - 1] : NULL; +} + +tVIBRATO_TABLE *Ca2mv2Player::get_vibrato_table(uint8_t vib_table) +{ + return vib_table && vibrato_table && vibrato_table[vib_table - 1] ? vibrato_table[vib_table - 1] : NULL; +} + +tFMREG_TABLE *Ca2mv2Player::get_fmreg_table(uint8_t fmreg_ins) +{ + tINSTR_DATA_EXT *instrument = get_instr(fmreg_ins); + + return instrument && instrument->fmreg ? instrument->fmreg : NULL; +} + +// Helpers for patterns =========================================================================== + +// event = pattern * (channels * rows) + ch * rows + row +tADTRACK2_EVENT *Ca2mv2Player::get_event_p(int pattern, int channel, int row) +{ + static tADTRACK2_EVENT null_event = { 0 }; + + return ( + pattern < eventsinfo->patterns + ? &eventsinfo->events[ + pattern * eventsinfo->channels * eventsinfo->rows + + channel * eventsinfo->rows + row] + : &null_event + ); +} + +void Ca2mv2Player::patterns_free() +{ + if (eventsinfo->events && eventsinfo->size) { + free(eventsinfo->events); + eventsinfo->events = NULL; + eventsinfo->size = 0; + } +} + +void Ca2mv2Player::patterns_allocate(int patterns, int channels, int rows) +{ + if (editor_mode) { // allocate max possible + patterns = 128; + channels = 20; + rows = 256; + } + + patterns_free(); + + size_t size = patterns * channels * rows * sizeof(tADTRACK2_EVENT); + + eventsinfo->events = (tADTRACK2_EVENT *)calloc(1, size); + assert(eventsinfo->events); + + eventsinfo->patterns = patterns; + eventsinfo->channels = channels; + eventsinfo->rows = rows; + eventsinfo->size = size; +} + +// End of patterns helpers ======================================================================== + +inline bool note_in_range(uint8_t note) +{ + return ((note & 0x7f) > 0) && ((note & 0x7f) < 12 * 8 + 1); +} + +inline uint16_t Ca2mv2Player::regoffs_n(int chan) +{ + static const uint16_t _ch_n[2][20] = { + { // mm + 0x003,0x000,0x004,0x001,0x005,0x002,0x006,0x007,0x008,0x103, + 0x100,0x104,0x101,0x105,0x102,0x106,0x107,0x108,BYTE_NULL,BYTE_NULL + }, { // pm + 0x003,0x000,0x004,0x001,0x005,0x002,0x106,0x107,0x108,0x103, + 0x100,0x104,0x101,0x105,0x102,0x006,0x007,0x008,0x008,0x007 + } + }; + + return _ch_n[!!percussion_mode][chan]; +} + +inline uint16_t Ca2mv2Player::regoffs_m(int chan) +{ + static const uint16_t _ch_m[2][20] = { + { // mm + 0x008,0x000,0x009,0x001,0x00a,0x002,0x010,0x011,0x012,0x108, + 0x100,0x109,0x101,0x10a,0x102,0x110,0x111,0x112,BYTE_NULL,BYTE_NULL + }, { // pm + 0x008,0x000,0x009,0x001,0x00a,0x002,0x110,0x111,0x112,0x108, + 0x100,0x109,0x101,0x10a,0x102,0x010,0x014,0x012,0x015,0x011 + } + }; + + return _ch_m[!!percussion_mode][chan]; +} + +inline uint16_t Ca2mv2Player::regoffs_c(int chan) +{ + static const uint16_t _ch_c[2][20] = { + { + 0x00b,0x003,0x00c,0x004,0x00d,0x005,0x013,0x014,0x015,0x10b, + 0x103,0x10c,0x104,0x10d,0x105,0x113,0x114,0x115,BYTE_NULL,BYTE_NULL + }, { + 0x00b,0x003,0x00c,0x004,0x00d,0x005,0x113,0x114,0x115,0x10b, + 0x103,0x10c,0x104,0x10d,0x105,0x013,BYTE_NULL,BYTE_NULL,BYTE_NULL,BYTE_NULL + } + }; + + return _ch_c[!!percussion_mode][chan]; +} + +#define FreqStart 0x156 +#define FreqEnd 0x2ae +#define FreqRange (FreqEnd - FreqStart) + +/* PLAYER */ +void Ca2mv2Player::opl2out(uint16_t reg, uint16_t data) +{ + if (chip != 0) { + chip = 0; + opl->setchip(chip); + } + opl->write(reg, data); +} + +void Ca2mv2Player::opl3out(uint16_t reg, uint8_t data) +{ + int _chip = reg < 0x100 ? 0 : 1; + if (chip != _chip) { + chip = _chip; + opl->setchip(chip); + } + opl->write(reg & 0xff, data); +} + +void Ca2mv2Player::opl3exp(uint16_t data) +{ + if (chip != 1) { + chip = 1; + opl->setchip(chip); + } + opl->write(data & 0xff, (data >> 8) & 0xff); +} + +static uint16_t nFreq(uint8_t note) +{ + static uint16_t Fnum[13] = {0x156,0x16b,0x181,0x198,0x1b0,0x1ca,0x1e5, + 0x202,0x220,0x241,0x263,0x287,0x2ae}; + + if (note >= 12 * 8) + return (7 << 10) | FreqEnd; + + return (note / 12 << 10) | Fnum[note % 12]; +} + +static uint16_t calc_freq_shift_up(uint16_t freq, uint16_t shift) +{ + uint16_t oc = (freq >> 10) & 7; + int16_t fr = (freq & 0x3ff) + shift; + + if (fr > FreqEnd) { + if (oc == 7) { + fr = FreqEnd; + } else { + oc++; + fr -= FreqRange; + } + } + + return (uint16_t)((oc << 10) | fr); +} + +static uint16_t calc_freq_shift_down(uint16_t freq, uint16_t shift) +{ + uint16_t oc = (freq >> 10) & 7; + int16_t fr = (freq & 0x3ff) - shift; + + if (fr < FreqStart) { + if (oc == 0) { + fr = FreqStart; + } else { + oc--; + fr += FreqRange; + } + } + + return (uint16_t)((oc << 10) | fr); +} + +/* == calc_vibtrem_shift() in AT2 */ +static uint16_t calc_vibrato_shift(uint8_t depth, uint8_t position) +{ + uint8_t vibr[32] = { + 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, + 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24 + }; + + /* ATTENTION: wtf this calculation should be ? */ + return (vibr[position & 0x1f] * depth) >> 6; +} + +void Ca2mv2Player::change_freq(int chan, uint16_t freq) +{ + if (is_4op_chan(chan) && is_4op_chan_hi(chan)) { + ch->freq_table[chan + 1] = ch->freq_table[chan]; + chan++; + } + + ch->freq_table[chan] &= ~0x1fff; + ch->freq_table[chan] |= (freq & 0x1fff); + + uint16_t n = regoffs_n(chan); + + opl3out(0xa0 + n, ch->freq_table[chan] & 0xFF); + opl3out(0xb0 + n, (ch->freq_table[chan] >> 8) & 0xFF); + + if (is_4op_chan(chan) && is_4op_chan_lo(chan)) { + ch->freq_table[chan - 1] = ch->freq_table[chan]; + } +} + +bool Ca2mv2Player::is_chan_adsr_data_empty(int chan) +{ + tFM_INST_DATA *fmpar = &ch->fmpar_table[chan]; + + return ( + !fmpar->data[4] && + !fmpar->data[5] && + !fmpar->data[6] && + !fmpar->data[7] + ); +} + +bool Ca2mv2Player::is_ins_adsr_data_empty(int ins) +{ + tINSTR_DATA *i = get_instr_data(ins); + + return ( + !i->fm.data[4] && + !i->fm.data[5] && + !i->fm.data[6] && + !i->fm.data[7] + ); +} + +static bool is_data_empty(char *data, unsigned int size) +{ + while (size--) { + if (*(char *)data++) + return false; + } + + return true; +} + +static inline uint16_t max(uint16_t value, uint16_t maximum) +{ + return (value > maximum ? maximum : value); +} + +void Ca2mv2Player::change_frequency(int chan, uint16_t freq) +{ + ch->macro_table[chan].vib_paused = true; + change_freq(chan, freq); + + if (is_4op_chan(chan)) { + int i = is_4op_chan_hi(chan) ? 1 : -1; + + ch->macro_table[chan + i].vib_count = 1; + ch->macro_table[chan + i].vib_pos = 0; + ch->macro_table[chan + i].vib_freq = freq; + ch->macro_table[chan + i].vib_paused = false; + } + + ch->macro_table[chan].vib_count = 1; + ch->macro_table[chan].vib_pos = 0; + ch->macro_table[chan].vib_freq = freq; + ch->macro_table[chan].vib_paused = false; +} + +inline uint16_t Ca2mv2Player::_macro_speedup() +{ + return macro_speedup ? macro_speedup : 1; +} + +void Ca2mv2Player::set_clock_rate(uint8_t clock_rate) +{ +} + +void Ca2mv2Player::update_timer(int Hz) +{ + if (Hz == 0) { + set_clock_rate(0); + return; + } else { + tempo = Hz; + } + + if (tempo == 18 && timer_fix) { + IRQ_freq = ((float)tempo + 0.2) * 20.0; + } else { + IRQ_freq = 250; + } + + while (IRQ_freq % (tempo * _macro_speedup()) != 0) { + IRQ_freq++; + } + + if (IRQ_freq > MAX_IRQ_FREQ) + IRQ_freq = MAX_IRQ_FREQ; + + while ((IRQ_freq + IRQ_freq_shift + playback_speed_shift > MAX_IRQ_FREQ) && (playback_speed_shift > 0)) + playback_speed_shift--; + + while ((IRQ_freq + IRQ_freq_shift + playback_speed_shift > MAX_IRQ_FREQ) && (IRQ_freq_shift > 0)) + IRQ_freq_shift--; + + set_clock_rate(1193180 / max(IRQ_freq + IRQ_freq_shift + playback_speed_shift, MAX_IRQ_FREQ)); +} + +void Ca2mv2Player::update_playback_speed(int speed_shift) +{ + if (!speed_shift) + return; + + if ((speed_shift > 0) && (IRQ_freq + playback_speed_shift + speed_shift > MAX_IRQ_FREQ)) { + while (IRQ_freq + IRQ_freq_shift + playback_speed_shift + speed_shift > MAX_IRQ_FREQ) + speed_shift--; + } else if ((speed_shift < 0) && (IRQ_freq + IRQ_freq_shift + playback_speed_shift + speed_shift < MIN_IRQ_FREQ)) { + while (IRQ_freq + IRQ_freq_shift + playback_speed_shift + speed_shift < MIN_IRQ_FREQ) + speed_shift++; + } + + playback_speed_shift += speed_shift; + update_timer(tempo); +} + +void Ca2mv2Player::key_on(int chan) +{ + int i = is_4op_chan(chan) && is_4op_chan_hi(chan) ? 1 : 0; + + opl3out(0xb0 + regoffs_n(chan + i), 0); +} + +void Ca2mv2Player::key_off(int chan) +{ + ch->freq_table[chan] &= ~0x2000; + change_frequency(chan, ch->freq_table[chan]); + ch->event_table[chan].note |= keyoff_flag; +} + +void Ca2mv2Player::release_sustaining_sound(int chan) +{ + uint16_t m = regoffs_m(chan); + uint16_t c = regoffs_c(chan); + + opl3out(0x40 + m, 63); + opl3out(0x40 + c, 63); + + // clear adsrw_mod and adsrw_car + for (int i = 4; i <= 9; i++) { + ch->fmpar_table[chan].data[i] = 0; + } + + key_on(chan); + opl3out(0x60 + m, BYTE_NULL); + opl3out(0x60 + c, BYTE_NULL); + opl3out(0x80 + m, BYTE_NULL); + opl3out(0x80 + c, BYTE_NULL); + + key_off(chan); + ch->event_table[chan].instr_def = 0; + ch->reset_chan[chan] = true; +} + +// inverted volume here +static uint8_t scale_volume(uint8_t volume, uint8_t scale_factor) +{ + return 63 - ((63 - volume) * (63 - scale_factor) / 63); +} + +// former _4op_data_flag() +t4OP_DATA Ca2mv2Player::get_4op_data(uint8_t chan) +{ + t4OP_DATA d = { false, 0, 0, 0, 0, 0 }; + + if (!is_4op_chan(chan)) + return d; + + d.mode = true; + + if (is_4op_chan_hi(chan)) { + d.ch1 = chan; + d.ch2 = chan + 1; + } else { + d.ch1 = chan - 1; + d.ch2 = chan; + } + + d.ins1 = ch->event_table[d.ch1].instr_def; + if (d.ins1 == 0) d.ins1 = ch->voice_table[d.ch1]; + + d.ins2 = ch->event_table[d.ch2].instr_def; + if (d.ins2 == 0) d.ins2 = ch->voice_table[d.ch2]; + + if (d.ins1 && d.ins2) { + d.conn = (get_instr_data(d.ins1)->fm.connect << 1) | get_instr_data(d.ins2)->fm.connect; + } + + return d; +} + +bool Ca2mv2Player::_4op_vol_valid_chan(int chan) +{ + t4OP_DATA d = get_4op_data(chan); + + return d.mode && ch->vol4op_lock[chan] && d.ins1 && d.ins2; +} + +// TODO here: fade_out_volume +// inverted volume here +void Ca2mv2Player::set_ins_volume(uint8_t modulator, uint8_t carrier, uint8_t chan) +{ + if (chan >= 20) { + AdPlug_LogWrite("set_ins_volume: channel out of bounds\n"); + return; + } + + tINSTR_DATA *instr = get_instr_data_by_ch(chan); + + // ** OPL3 emulation workaround ** + // force muted instrument volume with missing channel ADSR data + // when there is additionally no FM-reg macro defined for this instrument + tFMREG_TABLE *fmreg = get_fmreg_table(ch->voice_table[chan]); + uint8_t fmreg_length = fmreg ? fmreg->length : 0; + + if (is_chan_adsr_data_empty(chan) && !fmreg_length) { + modulator = 63; + carrier = 63; + } + + uint16_t m = regoffs_m(chan); + uint16_t c = regoffs_c(chan); + + // Note: fmpar[].volM/volC has pure unscaled volume, + // modulator_vol/carrier_vol have scaled but without overall_volume + if (modulator != BYTE_NULL) { + uint8_t regm; + bool is_perc_chan = instr->fm.connect || + (percussion_mode && chan >= 16); // in [17..20] + + ch->fmpar_table[chan].volM = modulator; + + if (is_perc_chan) { // in [17..20] + if (volume_scaling) + modulator = scale_volume(instr->fm.volM, modulator); + + modulator = scale_volume(modulator, 63 - global_volume); + regm = scale_volume(modulator, 63 - overall_volume) + (ch->fmpar_table[chan].kslM << 6); + } else { + regm = modulator + (ch->fmpar_table[chan].kslM << 6); + } + + opl3out(0x40 + m, regm); + ch->modulator_vol[chan] = 63 - modulator; + } + + if (carrier != BYTE_NULL) { + uint8_t regc; + + ch->fmpar_table[chan].volC = carrier; + + if (volume_scaling) + carrier = scale_volume(instr->fm.volC, carrier); + + carrier = scale_volume(carrier, 63 - global_volume); + regc = scale_volume(carrier, 63 - overall_volume) + (ch->fmpar_table[chan].kslC << 6); + + opl3out(0x40 + c, regc); + ch->carrier_vol[chan] = 63 - carrier; + } +} + +void Ca2mv2Player::set_volume(uint8_t modulator, uint8_t carrier, uint8_t chan) +{ + tINSTR_DATA *instr = get_instr_data_by_ch(chan); + + // ** OPL3 emulation workaround ** + // force muted instrument volume with missing channel ADSR data + // when there is additionally no FM-reg macro defined for this instrument + tFMREG_TABLE *fmreg = get_fmreg_table(ch->voice_table[chan]); + uint8_t fmreg_length = fmreg ? fmreg->length : 0; + + if (is_chan_adsr_data_empty(chan) && !fmreg_length) { + modulator = 63; + carrier = 63; + } + + uint16_t m = regoffs_m(chan); + uint16_t c = regoffs_c(chan); + + if (modulator != BYTE_NULL) { + uint8_t regm; + ch->fmpar_table[chan].volM = modulator; + + modulator = scale_volume(instr->fm.volM, modulator); + modulator = scale_volume(modulator, /*scale_volume(*/63 - global_volume/*, 63 - fade_out_volume)*/); + + regm = scale_volume(modulator, 63 - overall_volume) + (ch->fmpar_table[chan].kslM << 6); + + opl3out(0x40 + m, regm); + ch->modulator_vol[chan] = 63 - modulator; + } + + if (carrier != BYTE_NULL) { + uint8_t regc; + ch->fmpar_table[chan].volC = carrier; + + carrier = scale_volume(instr->fm.volC, carrier); + carrier = scale_volume(carrier, /*scale_volume(*/63 - global_volume/*, 63 - fade_out_volume)*/); + + regc = scale_volume(carrier, 63 - overall_volume) + (ch->fmpar_table[chan].kslC << 6); + + opl3out(0x40 + c, regc); + ch->carrier_vol[chan] = 63 - carrier; + } +} + +void Ca2mv2Player::set_ins_volume_4op(uint8_t volume, uint8_t chan) +{ + t4OP_DATA d = get_4op_data(chan); + + if (!_4op_vol_valid_chan(chan)) + return; + + uint8_t volM1 = BYTE_NULL; + uint8_t volC1 = BYTE_NULL; + uint8_t volM2 = BYTE_NULL; + uint8_t volC2 = BYTE_NULL; + + volC1 = volume == BYTE_NULL ? ch->fmpar_table[d.ch1].volC : volume; + + switch (d.conn) { + case 0: // FM/FM ins1=FM, ins2=FM + break; + case 1: // FM/AM ins1=FM, ins2=AM + volM2 = volume == BYTE_NULL ? ch->fmpar_table[d.ch2].volM : volume; + break; + case 2: // AM/FM ins1=AM, ins2=FM + volC2 = volume == BYTE_NULL ? ch->fmpar_table[d.ch2].volC : volume; + break; + case 3:// AM/AM ins1=AM, ins2=AM + volM1 = volume == BYTE_NULL ? ch->fmpar_table[d.ch1].volM : volume; + volM2 = volume == BYTE_NULL ? ch->fmpar_table[d.ch2].volM : volume; + break; + } + + set_volume(volM1, volC1, d.ch1); + set_volume(volM2, volC2, d.ch2); +} + +void Ca2mv2Player::reset_ins_volume(int chan) +{ + tINSTR_DATA *instr = get_instr_data_by_ch(chan); + if (!instr) return; + + uint8_t vol_mod = instr->fm.volM; + uint8_t vol_car = instr->fm.volC; + uint8_t conn = instr->fm.connect; + + if (volume_scaling) { + vol_mod = (!conn ? vol_mod : 0); + vol_car = 0; + } + + set_ins_volume(vol_mod, vol_car, chan); +} + +void Ca2mv2Player::set_global_volume() +{ + for (int chan = 0; chan < songinfo->nm_tracks; chan++) { + if (_4op_vol_valid_chan(chan)) { + set_ins_volume_4op(BYTE_NULL, chan); + } else if (ch->carrier_vol[chan] || ch->modulator_vol[chan]) { + tINSTR_DATA *instr = get_instr_data_by_ch(chan); + + set_ins_volume(instr->fm.connect ? ch->fmpar_table[chan].volM : BYTE_NULL, ch->fmpar_table[chan].volC, chan); + } + } +} + +void Ca2mv2Player::set_overall_volume(unsigned char level) +{ + overall_volume = max(level, 63); + set_global_volume(); +} + +void Ca2mv2Player::init_macro_table(int chan, uint8_t note, uint8_t ins, uint16_t freq) +{ + tINSTR_DATA_EXT *instrument = get_instr(ins); + + uint8_t arp_table = instrument ? instrument->arpeggio : 0; + ch->macro_table[chan].fmreg_pos = 0; + ch->macro_table[chan].fmreg_duration = 0; + ch->macro_table[chan].fmreg_ins = ins; // todo: check against instruments->fmreg.length + ch->macro_table[chan].arpg_count = 1; + ch->macro_table[chan].arpg_pos = 0; + ch->macro_table[chan].arpg_table = arp_table; + ch->macro_table[chan].arpg_note = note; + + uint8_t vib_table = instrument ? instrument->vibrato : 0; + tVIBRATO_TABLE *vib = get_vibrato_table(vib_table); + uint8_t vib_delay = vib ? vib->delay : 0; + + ch->macro_table[chan].vib_count = 1; + ch->macro_table[chan].vib_paused = false; + ch->macro_table[chan].vib_pos = 0; + ch->macro_table[chan].vib_table = vib_table; + ch->macro_table[chan].vib_freq = freq; + ch->macro_table[chan].vib_delay = vib_delay; + + ch->zero_fq_table[chan] = 0; +} + +void Ca2mv2Player::set_ins_data(uint8_t ins, int chan) +{ + static tINSTR_DATA zeroins = { 0 }; + + if (ins == 0) return; + + tINSTR_DATA *i = get_instr_data(ins); + i = i ? i : &zeroins; + + if (is_data_empty((char *)i, sizeof(tINSTR_DATA))) { + release_sustaining_sound(chan); + } + + if ((ins != ch->event_table[chan].instr_def) || ch->reset_chan[chan]) { + ch->panning_table[chan] = !ch->pan_lock[chan] + ? i->panning + : songinfo->lock_flags[chan] & 3; + if (ch->panning_table[chan] >= sizeof (_panning)) + ch->panning_table[chan] = 0; /* various code paths can lead to this value going out of the 0-2 range */ + + uint16_t m = regoffs_m(chan); + uint16_t c = regoffs_c(chan); + uint16_t n = regoffs_n(chan); + + opl3out(0x20 + m, i->fm.data[0]); + opl3out(0x20 + c, i->fm.data[1]); + opl3out(0x40 + m, (i->fm.data[2] & 0xc0) + 63); + opl3out(0x40 + c, (i->fm.data[3] & 0xc0) + 63); + opl3out(0x60 + m, i->fm.data[4]); + opl3out(0x60 + c, i->fm.data[5]); + opl3out(0x80 + m, i->fm.data[6]); + opl3out(0x80 + c, i->fm.data[7]); + opl3out(0xe0 + m, i->fm.data[8]); + opl3out(0xe0 + c, i->fm.data[9]); + opl3out(0xc0 + n, i->fm.data[10] | _panning[ch->panning_table[chan]]); + + for (int r = 0; r < 11; r++) { + ch->fmpar_table[chan].data[r] = i->fm.data[r]; + } + + // Stop instr macro if resetting voice + if (!ch->reset_chan[chan]) + ch->keyoff_loop[chan] = false; + + if (ch->reset_chan[chan]) { + ch->voice_table[chan] = ins; + reset_ins_volume(chan); + ch->reset_chan[chan] = false; + } + + uint8_t note = ch->event_table[chan].note & 0x7f; + note = note_in_range(note) ? note : 0; + + init_macro_table(chan, note, ins, ch->freq_table[chan]); + } + + ch->voice_table[chan] = ins; + uint8_t old_ins = ch->event_table[chan].instr_def; + ch->event_table[chan].instr_def = ins; + + if (!ch->volume_lock[chan] || (ins != old_ins)) + reset_ins_volume(chan); +} + +void Ca2mv2Player::update_modulator_adsrw(int chan) +{ + tFM_INST_DATA *fmpar = &ch->fmpar_table[chan]; + uint16_t m = regoffs_m(chan); + + opl3out(0x60 + m, fmpar->data[4]); + opl3out(0x80 + m, fmpar->data[6]); + opl3out(0xe0 + m, fmpar->data[8]); +} + +void Ca2mv2Player::update_carrier_adsrw(int chan) +{ + tFM_INST_DATA *fmpar = &ch->fmpar_table[chan]; + uint16_t c = regoffs_c(chan); + + opl3out(0x60 + c, fmpar->data[5]); + opl3out(0x80 + c, fmpar->data[7]); + opl3out(0xe0 + c, fmpar->data[9]); +} + +void Ca2mv2Player::update_fmpar(int chan) +{ + tFM_INST_DATA *fmpar = &ch->fmpar_table[chan]; + + opl3out(0x20 + regoffs_m(chan), fmpar->data[0]); + opl3out(0x20 + regoffs_c(chan), fmpar->data[1]); + opl3out(0xc0 + regoffs_n(chan), fmpar->data[10] | _panning[ch->panning_table[chan]]); + + set_ins_volume(fmpar->volM, fmpar->volC, chan); +} + +inline bool Ca2mv2Player::is_4op_chan(int chan) // 0..19 +{ + static char mask[20] = { + (1<<0), (1<<0), (1<<1), (1<<1), (1<<2), (1<<2), 0, 0, 0, + (1<<3), (1<<3), (1<<4), (1<<4), (1<<5), (1<<5), 0, 0, 0, 0, 0 + }; +/* + 4-op track extension flags byte, channels 1-18 + 0 - tracks 1,2 + 1 - tracks 3,4 + 2 - tracks 5,6 + 3 - tracks 10,11 + 4 - tracks 12,13 + 5 - tracks 14,15 + 6 - %unused% + 7 - %unused% +*/ + return (chan > 14 ? false : !!(songinfo->flag_4op & mask[chan])); +} + +inline bool Ca2mv2Player::is_4op_chan_hi(int chan) +{ + static bool _4op_hi[20] = { + true, false, true, false, true, false, false, false, false, // 0, 2, 4 + true, false, true, false, true, false, false, false, false, false, false // 9, 10, 13 + }; + + return _4op_hi[chan]; +} + +inline bool Ca2mv2Player::is_4op_chan_lo(int chan) +{ + static bool _4op_lo[20] = { + false, true, false, true, false, true, false, false, false, // 1, 3, 5 + false, true, false, true, false, true, false, false, false, false, false // 10, 12, 14 + }; + + return _4op_lo[chan]; +} + +void Ca2mv2Player::output_note(uint8_t note, uint8_t ins, int chan, bool restart_macro, bool restart_adsr) +{ + uint16_t freq; + + if ((note == 0) && (ch->ftune_table[chan] == 0)) return; + + if ((note & 0x80) || !note_in_range(note)) { + freq = ch->freq_table[chan]; + } else { + freq = nFreq(note - 1) + get_instr_fine_tune(ins); + + if (restart_adsr) { + key_on(chan); + } else { + AdPlug_LogWrite("restart_adsr == false in output_note()\n"); + } + + ch->freq_table[chan] |= 0x2000; + } + + if (ch->ftune_table[chan] == -127) + ch->ftune_table[chan] = 0; + + freq = freq + ch->ftune_table[chan]; + change_frequency(chan, freq); + + if (note) { + ch->event_table[chan].note = note; + + if (is_4op_chan(chan) && is_4op_chan_lo(chan)) { + ch->event_table[chan - 1].note = note; + } + + // Do we need that? + /*if (is_4op_chan(chan) && is_4op_chan_hi(chan)) { + ch->event_table[chan + 1].note = note; + }*/ + + if (restart_macro) { + // Check if no ZFF - force no restart + bool force_no_restart = ( + ((ch->event_table[chan].eff[0].def == ef_Extended) && + (ch->event_table[chan].eff[0].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_NoRestart)) || + ((ch->event_table[chan].eff[1].def == ef_Extended) && + (ch->event_table[chan].eff[1].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_NoRestart)) + ); + if (!force_no_restart) { + init_macro_table(chan, note, ins, freq); + } else { + ch->macro_table[chan].arpg_note = note; + } + } + } +} + +bool Ca2mv2Player::no_loop(uint8_t current_chan, uint8_t current_line) +{ + for (int chan = 0; chan < current_chan; chan++) { + if ((ch->loop_table[chan][current_line] != 0) && + (ch->loop_table[chan][current_line] != BYTE_NULL)) + return false; + } + + return true; +} + +static int get_effect_group(uint8_t def) +{ + switch (def) { + case ef_ArpggVSlide: + case ef_ArpggVSlideFine: return EFGR_ARPVOLSLIDE; + case ef_FSlideUpVSlide: + case ef_FSlUpVSlF: + case ef_FSlideDownVSlide: + case ef_FSlDownVSlF: + case ef_FSlUpFineVSlide: + case ef_FSlUpFineVSlF: + case ef_FSlDownFineVSlide: + case ef_FSlDownFineVSlF: return EFGR_FSLIDEVOLSLIDE; + case ef_TonePortamento: return EFGR_TONEPORTAMENTO; + case ef_Vibrato: + case ef_ExtraFineVibrato: return EFGR_VIBRATO; + case ef_Tremolo: + case ef_ExtraFineTremolo: return EFGR_TREMOLO; + case ef_VibratoVolSlide: + case ef_VibratoVSlideFine: return EFGR_VIBRATOVOLSLIDE; + case ef_TPortamVolSlide: + case ef_TPortamVSlideFine: return EFGR_PORTAVOLSLIDE; + case ef_RetrigNote: + case ef_MultiRetrigNote: return EFGR_RETRIGNOTE; + default: return -1; + } + + return -1; +} + +// In case of x00 set value of the previous compatible effect command +void Ca2mv2Player::update_effect_table(int slot, int chan, int eff_group, uint8_t def, uint8_t val) +{ + uint8_t lval = ch->last_effect[slot][chan].val; + + ch->effect_table[slot][chan].def = def; + + if (val) { + ch->effect_table[slot][chan].val = val; + } else if (get_effect_group(ch->last_effect[slot][chan].def) == eff_group && lval) { + ch->effect_table[slot][chan].val = lval; + } else { + // x00 without any previous compatible command, should never happen + AdPlug_LogWrite("x00 without any previous compatible command\n"); + ch->effect_table[slot][chan].def = 0; + ch->effect_table[slot][chan].val = 0; + } +} + +void Ca2mv2Player::process_effects(tADTRACK2_EVENT *event, int slot, int chan) +{ + tINSTR_DATA *instr = get_instr_data_by_ch(chan); + uint8_t def = event->eff[slot].def; + uint8_t val = event->eff[slot].val; + + // Note: this might be dropped because effect_table stores effects + // that might be continued with x00 + ch->effect_table[slot][chan].def = def; + ch->effect_table[slot][chan].val = val; + + if ((def != ef_Vibrato) && + (def != ef_ExtraFineVibrato) && + (def != ef_VibratoVolSlide) && + (def != ef_VibratoVSlideFine)) + memset(&ch->vibr_table[slot][chan], 0, sizeof(ch->vibr_table[slot][chan])); + + if ((def != ef_RetrigNote) && + (def != ef_MultiRetrigNote)) + memset(&ch->retrig_table[slot][chan], 0, sizeof(ch->retrig_table[slot][chan])); + + if ((def != ef_Tremolo) && + (def != ef_ExtraFineTremolo)) + memset(&ch->trem_table[slot][chan], 0, sizeof(ch->trem_table[slot][chan])); + + if (!(((def == ef_Arpeggio) && (val != 0)) || (def == ef_ExtraFineArpeggio)) && + (ch->arpgg_table[slot][chan].note != 0) && (ch->arpgg_table[slot][chan].state != 1)) { + ch->arpgg_table[slot][chan].state = 1; + change_frequency(chan, nFreq(ch->arpgg_table[slot][chan].note - 1) + + get_instr_fine_tune(ch->event_table[chan].instr_def)); + } + + if ((def == ef_GlobalFSlideUp) || (def == ef_GlobalFSlideDown)) { + if ((event->eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == ef_ex_ExtendedCmd * 16 + ef_ex_cmd_ForceBpmSld)) { + + AdPlug_LogWrite("ef_GlobalFSlideUp or ef_GlobalFSlideDown with ef_ex_cmd_ForceBpmSld\n"); + + if (def == ef_GlobalFSlideUp) { + update_playback_speed(val); + } else { + update_playback_speed(-val); + } + } else { + uint8_t eff; + + switch (def) { + case ef_GlobalFSlideUp: + eff = ef_FSlideUp; + + // >xx + ZFE + if ((event->eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_FTrm_XFGFS)) { + eff = ef_GlobalFreqSlideUpXF; + } + + // >xx + ZFD + if ((event->eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_FVib_FGFS)) { + eff = ef_FSlideUpFine; + } + + ch->effect_table[slot][chan].def = eff; + ch->effect_table[slot][chan].val = val; + break; + case ef_GlobalFSlideDown: + eff = ef_FSlideDown; + + // eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_FTrm_XFGFS)) { + eff = ef_GlobalFreqSlideDnXF; + } + + // eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_FVib_FGFS)) { + eff = ef_FSlideDownFine; + } + + ch->effect_table[slot][chan].def = eff; + ch->effect_table[slot][chan].val = val; + break; + } + + // shouldn't it be int c = 0 ?? + for (int c = chan; c < songinfo->nm_tracks; c++) { + ch->fslide_table[slot][c] = val; + ch->glfsld_table[slot][c].def = ch->effect_table[slot][chan].def; + ch->glfsld_table[slot][c].val = ch->effect_table[slot][chan].val; + } + } + } + + if (ch->tremor_table[slot][chan].pos && (def != ef_Tremor)) { + ch->tremor_table[slot][chan].pos = 0; + set_ins_volume(ch->tremor_table[slot][chan].volM, ch->tremor_table[slot][chan].volC, chan); + } + + switch (def) { + case ef_Arpeggio: + if (!val) + break; + + case ef_ExtraFineArpeggio: + case ef_ArpggVSlide: + case ef_ArpggVSlideFine: + switch (def) { + case ef_Arpeggio: + ch->effect_table[slot][chan].def = ef_Arpeggio; + ch->effect_table[slot][chan].val = val; + break; + case ef_ExtraFineArpeggio: + ch->effect_table[slot][chan].def = ef_ExtraFineArpeggio; + ch->effect_table[slot][chan].val = val; + break; + case ef_ArpggVSlide: + case ef_ArpggVSlideFine: + update_effect_table(slot, chan, EFGR_ARPVOLSLIDE, def, val); + break; + } + + if (note_in_range(event->note)) { + ch->arpgg_table[slot][chan].state = 0; + ch->arpgg_table[slot][chan].note = event->note & 0x7f; + if ((def == ef_Arpeggio) || (def == ef_ExtraFineArpeggio)) { + ch->arpgg_table[slot][chan].add1 = val >> 4; + ch->arpgg_table[slot][chan].add2 = val & 0x0f; + } + } else { + if (!event->note && note_in_range(ch->event_table[chan].note)) { + + // This never occurs most probably + /*if ((def != ef_Arpeggio) && + (def != ef_ExtraFineArpeggio) && + (def != ef_ArpggVSlide) && + (def != ef_ArpggVSlideFine)) + ch->arpgg_table[slot][chan].state = 0;*/ + + ch->arpgg_table[slot][chan].note = ch->event_table[chan].note & 0x7f; + if ((def == ef_Arpeggio) || (def == ef_ExtraFineArpeggio)) { + ch->arpgg_table[slot][chan].add1 = val / 16; + ch->arpgg_table[slot][chan].add2 = val % 16; + } + } else { + ch->effect_table[slot][chan].def = 0; + ch->effect_table[slot][chan].val = 0; + } + } + break; + + case ef_FSlideUp: + case ef_FSlideDown: + case ef_FSlideUpFine: + case ef_FSlideDownFine: + ch->effect_table[slot][chan].def = def; + ch->effect_table[slot][chan].val = val; + ch->fslide_table[slot][chan] = val; + break; + + case ef_FSlideUpVSlide: + case ef_FSlUpVSlF: + case ef_FSlideDownVSlide: + case ef_FSlDownVSlF: + case ef_FSlUpFineVSlide: + case ef_FSlUpFineVSlF: + case ef_FSlDownFineVSlide: + case ef_FSlDownFineVSlF: + update_effect_table(slot, chan, EFGR_FSLIDEVOLSLIDE, def, val); + break; + + case ef_TonePortamento: + update_effect_table(slot, chan, EFGR_TONEPORTAMENTO, def, val); + + if (note_in_range(event->note)) { + ch->porta_table[slot][chan].speed = val; + ch->porta_table[slot][chan].freq = nFreq(event->note - 1) + + get_instr_fine_tune(ch->event_table[chan].instr_def); + } else { + ch->porta_table[slot][chan].speed = ch->effect_table[slot][chan].val; + } + break; + + case ef_TPortamVolSlide: + case ef_TPortamVSlideFine: + update_effect_table(slot, chan, EFGR_PORTAVOLSLIDE, def, val); + + break; + + case ef_Vibrato: + case ef_ExtraFineVibrato: + update_effect_table(slot, chan, EFGR_VIBRATO, def, val); + + if ((event->eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_FVib_FGFS)) { + ch->vibr_table[slot][chan].fine = true; + } + + ch->vibr_table[slot][chan].speed = val / 16; + ch->vibr_table[slot][chan].depth = val % 16; + break; + + case ef_Tremolo: + case ef_ExtraFineTremolo: + update_effect_table(slot, chan, EFGR_TREMOLO, def, val); + + if ((event->eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_FTrm_XFGFS)) { + ch->trem_table[slot][chan].fine = true; + } + + ch->trem_table[slot][chan].speed = val / 16; + ch->trem_table[slot][chan].depth = val % 16; + break; + + case ef_VibratoVolSlide: + case ef_VibratoVSlideFine: + update_effect_table(slot, chan, EFGR_VIBRATOVOLSLIDE, def, val); + + if ((event->eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_FVib_FGFS)) + ch->vibr_table[slot][chan].fine = true; + break; + + case ef_SetCarrierVol: + set_ins_volume(BYTE_NULL, 63 - val, chan); + break; + + case ef_SetModulatorVol: + set_ins_volume(63 - val, BYTE_NULL, chan); + break; + + case ef_SetInsVolume: + if (_4op_vol_valid_chan(chan)) { + set_ins_volume_4op(63 - val, chan); + } else if (percussion_mode && ((chan >= 16) && (chan <= 19))) { // in [17..20] + set_ins_volume(63 - val, BYTE_NULL, chan); + } else if (instr->fm.connect == 0) { + set_ins_volume(BYTE_NULL, 63 - val, chan); + } else { + set_ins_volume(63 - val, 63 - val, chan); + } + break; + + case ef_ForceInsVolume: + if (percussion_mode && ((chan >= 16) && (chan <= 19))) { // in [17..20] + set_ins_volume(63 - val, BYTE_NULL, chan); + } else if (instr->fm.connect == 0) { + set_ins_volume(scale_volume(instr->fm.volM, 63 - val), 63 - val, chan); + } else { + set_ins_volume(63 - val, 63 - val, chan); + } + break; + + case ef_PositionJump: + if (no_loop(chan, current_line)) { + pattern_break = true; + next_line = pattern_break_flag + chan; + } + break; + + case ef_PatternBreak: + if (no_loop(chan, current_line)) { + pattern_break = true; + // seek_pattern_break = true; // TODO + next_line = max(val, songinfo->patt_len - 1); + } + break; + + case ef_SetSpeed: + speed = val; + break; + + case ef_SetTempo: + update_timer(val); + break; + + case ef_SetWaveform: + if (val / 16 <= 7) { // in [0..7] + ch->fmpar_table[chan].wformC = val / 16; + update_carrier_adsrw(chan); + } + + if (val % 16 <= 7) { // in [0..7] + ch->fmpar_table[chan].wformM = val % 16; + update_modulator_adsrw(chan); + } + break; + + case ef_VolSlide: + ch->effect_table[slot][chan].def = def; + ch->effect_table[slot][chan].val = val; + break; + + case ef_VolSlideFine: + ch->effect_table[slot][chan].def = def; + ch->effect_table[slot][chan].val = val; + break; + + case ef_RetrigNote: + case ef_MultiRetrigNote: + if (val) { + if (get_effect_group(ch->last_effect[slot][chan].def) != EFGR_RETRIGNOTE) { + ch->retrig_table[slot][chan] = 1; + } + + ch->effect_table[slot][chan].def = def; + ch->effect_table[slot][chan].val = val; + } + break; + + case ef_SetGlobalVolume: + global_volume = val; + set_global_volume(); + break; + + case ef_Tremor: + if (val) { + if (ch->last_effect[slot][chan].def != ef_Tremor) { + ch->tremor_table[slot][chan].pos = 0; + ch->tremor_table[slot][chan].volM = ch->fmpar_table[chan].volM; + ch->tremor_table[slot][chan].volC = ch->fmpar_table[chan].volC; + } + + ch->effect_table[slot][chan].def = def; + ch->effect_table[slot][chan].val = val; + } + break; + + case ef_Extended: + switch (val / 16) { + case ef_ex_SetTremDepth: + switch (val % 16) { + case 0: + opl3out(0xbd, misc_register & 0x7f); + current_tremolo_depth = 0; + break; + + case 1: + opl3out(0xbd, misc_register | 0x80); + current_tremolo_depth = 1; + break; + } + break; + + case ef_ex_SetVibDepth: + switch (val % 16) { + case 0: + opl3out(0xbd, misc_register & 0xbf); + current_vibrato_depth = 0; + break; + + case 1: + opl3out(0xbd, misc_register | 0x40); + current_vibrato_depth = 1; + break; + } + break; + + case ef_ex_SetAttckRateM: + ch->fmpar_table[chan].attckM = val % 16; + update_modulator_adsrw(chan); + break; + + case ef_ex_SetDecayRateM: + ch->fmpar_table[chan].decM = val % 16; + update_modulator_adsrw(chan); + break; + + case ef_ex_SetSustnLevelM: + ch->fmpar_table[chan].sustnM = val % 16; + update_modulator_adsrw(chan); + break; + + case ef_ex_SetRelRateM: + ch->fmpar_table[chan].relM = val % 16; + update_modulator_adsrw(chan); + break; + + case ef_ex_SetAttckRateC: + ch->fmpar_table[chan].attckC = val % 16; + update_carrier_adsrw(chan); + break; + + case ef_ex_SetDecayRateC: + ch->fmpar_table[chan].decC = val % 16; + update_carrier_adsrw(chan); + break; + + case ef_ex_SetSustnLevelC: + ch->fmpar_table[chan].sustnC = val % 16; + update_carrier_adsrw(chan); + break; + + case ef_ex_SetRelRateC: + ch->fmpar_table[chan].relC = val % 16; + update_carrier_adsrw(chan); + break; + + case ef_ex_SetFeedback: + ch->fmpar_table[chan].feedb = val % 16; + update_fmpar(chan); + break; + + case ef_ex_SetPanningPos: + ch->panning_table[chan] = val % 16; + update_fmpar(chan); + break; + + case ef_ex_PatternLoop: + case ef_ex_PatternLoopRec: + if (val % 16 == 0) { + ch->loopbck_table[chan] = current_line; + } else { + if (ch->loopbck_table[chan] != BYTE_NULL) { + if (ch->loop_table[chan][current_line] == BYTE_NULL) + ch->loop_table[chan][current_line] = val % 16; + + if (ch->loop_table[chan][current_line] != 0) { + pattern_break = true; + next_line = pattern_loop_flag + chan; + } else { + if (val / 16 == ef_ex_PatternLoopRec) + ch->loop_table[chan][current_line] = BYTE_NULL; + } + } + } + break; + case ef_ex_ExtendedCmd: + switch (val & 0x0f) { + case ef_ex_cmd_MKOffLoopDi: ch->keyoff_loop[chan] = false; break; + case ef_ex_cmd_MKOffLoopEn: ch->keyoff_loop[chan] = true; break; + case ef_ex_cmd_TPortaFKdis: ch->portaFK_table[chan] = false; break; + case ef_ex_cmd_TPortaFKenb: ch->portaFK_table[chan] = true; break; + case ef_ex_cmd_RestartEnv: + key_on(chan); + change_freq(chan, ch->freq_table[chan]); + break; + case ef_ex_cmd_4opVlockOff: + if (is_4op_chan(chan)) { + ch->vol4op_lock[chan] = false; + int i = is_4op_chan_hi(chan) ? 1 : -1; + + ch->vol4op_lock[chan + i] = false; + } + break; + case ef_ex_cmd_4opVlockOn: + if (is_4op_chan(chan)) { + ch->vol4op_lock[chan] = true; + int i = is_4op_chan_hi(chan) ? 1 : -1; + + ch->vol4op_lock[chan + i] = true; + } + break; + } + break; + case ef_ex_ExtendedCmd2: + switch (val % 16) { + case ef_ex_cmd2_RSS: release_sustaining_sound(chan); break; + case ef_ex_cmd2_ResetVol: reset_ins_volume(chan); break; + case ef_ex_cmd2_LockVol: ch->volume_lock [chan] = true; break; + case ef_ex_cmd2_UnlockVol: ch->volume_lock [chan] = false; break; + case ef_ex_cmd2_LockVP: ch->peak_lock [chan] = true; break; + case ef_ex_cmd2_UnlockVP: ch->peak_lock [chan] = false; break; + case ef_ex_cmd2_VSlide_def: ch->volslide_type[chan] = 0; break; + case ef_ex_cmd2_LockPan: ch->pan_lock [chan] = true; break; + case ef_ex_cmd2_UnlockPan: ch->pan_lock [chan] = false; break; + case ef_ex_cmd2_VibrOff: change_frequency(chan, ch->freq_table[chan]); break; + case ef_ex_cmd2_TremOff: + if (is_4op_chan(chan)) { + set_ins_volume_4op(BYTE_NULL, chan); + } else { + set_ins_volume(ch->fmpar_table[chan].volM, ch->fmpar_table[chan].volC, chan); + } + break; + case ef_ex_cmd2_VSlide_car: + if ((event->eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == ef_ex_ExtendedCmd2 * 16 + + ef_ex_cmd2_VSlide_mod)) { + ch->volslide_type[chan] = 3; + } else { + ch->volslide_type[chan] = 1; + } + break; + + case ef_ex_cmd2_VSlide_mod: + if ((event->eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == ef_ex_ExtendedCmd2 * 16 + + ef_ex_cmd2_VSlide_car)) { + ch->volslide_type[chan] = 3; + } else { + ch->volslide_type[chan] = 2; + } + break; + } + break; + } + break; + + case ef_Extended2: + switch (val / 16) { + case ef_ex2_PatDelayFrame: + pattern_delay = true; + tickD = val % 16; + break; + + case ef_ex2_PatDelayRow: + pattern_delay = true; + tickD = speed * (val % 16); + break; + + case ef_ex2_NoteDelay: + ch->effect_table[slot][chan].def = ef_Extended2; + ch->effect_table[slot][chan].val = val; + ch->notedel_table[chan] = val % 16; + break; + + case ef_ex2_NoteCut: + ch->effect_table[slot][chan].def = ef_Extended2; + ch->effect_table[slot][chan].val = val; + ch->notecut_table[chan] = val % 16; + break; + + case ef_ex2_FineTuneUp: + ch->ftune_table[chan] += val % 16; + break; + + case ef_ex2_FineTuneDown: + ch->ftune_table[chan] -= val % 16; + break; + + case ef_ex2_GlVolSlideUp: + case ef_ex2_GlVolSlideDn: + case ef_ex2_GlVolSlideUpF: + case ef_ex2_GlVolSlideDnF: + case ef_ex2_GlVolSldUpXF: + case ef_ex2_GlVolSldDnXF: + case ef_ex2_VolSlideUpXF: + case ef_ex2_VolSlideDnXF: + case ef_ex2_FreqSlideUpXF: + case ef_ex2_FreqSlideDnXF: + ch->effect_table[slot][chan].def = ef_Extended2; + ch->effect_table[slot][chan].val = val; + break; + } + break; + + case ef_Extended3: + switch (val / 16) { + case ef_ex3_SetConnection: + ch->fmpar_table[chan].connect = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetMultipM: + ch->fmpar_table[chan].multipM = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetKslM: + ch->fmpar_table[chan].kslM = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetTremoloM: + ch->fmpar_table[chan].tremM = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetVibratoM: + ch->fmpar_table[chan].vibrM = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetKsrM: + ch->fmpar_table[chan].ksrM = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetSustainM: + ch->fmpar_table[chan].sustM = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetMultipC: + ch->fmpar_table[chan].multipC = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetKslC: + ch->fmpar_table[chan].kslC = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetTremoloC: + ch->fmpar_table[chan].tremC = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetVibratoC: + ch->fmpar_table[chan].vibrC = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetKsrC: + ch->fmpar_table[chan].ksrC = val % 16; + update_fmpar(chan); + break; + + case ef_ex3_SetSustainC: + ch->fmpar_table[chan].sustC = val % 16; + update_fmpar(chan); + break; + } + break; + } +} + +static bool no_swap_and_restart(tADTRACK2_EVENT *event) +{ + // [!xx/@xx] swap arp/swap vib + [zff] no force restart + return + !(((event->eff[1].def == ef_SwapArpeggio) || + (event->eff[1].def == ef_SwapVibrato)) && + (event->eff[0].def == ef_Extended) && + (event->eff[0].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_NoRestart)) && + !(((event->eff[0].def == ef_SwapArpeggio) || + (event->eff[0].def == ef_SwapVibrato)) && + (event->eff[1].def == ef_Extended) && + (event->eff[1].val == ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_NoRestart)); +} + +static bool is_eff_porta(tADTRACK2_EVENT *event) +{ + int eff0 = event->eff[0].def; + bool is_p0 = (eff0 == ef_TonePortamento) || + (eff0 == ef_TPortamVolSlide) || + (eff0 == ef_TPortamVSlideFine); + int eff1 = event->eff[1].def; + bool is_p1 = (eff1 == ef_TonePortamento) || + (eff1 == ef_TPortamVolSlide) || + (eff1 == ef_TPortamVSlideFine); + return is_p0 || is_p1; +} + +static bool is_eff_notedelay(tADTRACK2_EVENT *event) +{ + return ( + (event->eff[0].def == ef_Extended2 && (event->eff[0].val / 16 == ef_ex2_NoteDelay)) || + (event->eff[1].def == ef_Extended2 && (event->eff[1].val / 16 == ef_ex2_NoteDelay)) + ); +} + +void Ca2mv2Player::new_process_note(tADTRACK2_EVENT *event, int chan) +{ + bool tporta_flag = is_eff_porta(event); + bool notedelay_flag = is_eff_notedelay(event); + + if (event->note == 0) + return; + + // This might delay even note-off + // Or put this after key_off? + if (notedelay_flag) { + ch->event_table[chan].note = event->note; + return; + } + + if (event->note & keyoff_flag) { + key_off(chan); + return; + } + + if (!tporta_flag) { + output_note(event->note, ch->voice_table[chan], chan, true, no_swap_and_restart(event)); + return; + } + + // if previous note was off'ed or restart_adsr enabled for channel + // and we are doing portamento to a new note + if (ch->event_table[chan].note & keyoff_flag || ch->portaFK_table[chan]) { + output_note(ch->event_table[chan].note & ~keyoff_flag, ch->voice_table[chan], chan, false, true); + } else { + ch->event_table[chan].note = event->note; + } +} + +void Ca2mv2Player::play_line() +{ + tADTRACK2_EVENT _event, *event = &_event; + + if (!(pattern_break && ((next_line & 0xf0) == pattern_loop_flag)) && current_order != last_order) { + memset(ch->loopbck_table, BYTE_NULL, sizeof(ch->loopbck_table)); + memset(ch->loop_table, BYTE_NULL, sizeof(ch->loop_table)); + last_order = current_order; + } + + for (int chan = 0; chan < songinfo->nm_tracks; chan++) { + // save effect_table into last_effect + for (int slot = 0; slot < 2; slot++) { + if (ch->effect_table[slot][chan].def | ch->effect_table[slot][chan].val) { + ch->last_effect[slot][chan].def = ch->effect_table[slot][chan].def; + ch->last_effect[slot][chan].val = ch->effect_table[slot][chan].val; + } + if (ch->glfsld_table[slot][chan].def | ch->glfsld_table[slot][chan].val) { + ch->effect_table[slot][chan].def = ch->glfsld_table[slot][chan].def; + ch->effect_table[slot][chan].val = ch->glfsld_table[slot][chan].val; + } else { + ch->effect_table[slot][chan].def = 0; + ch->effect_table[slot][chan].val = 0; + } + } + + ch->ftune_table[chan] = 0; + + // Do a full copy of the event, because we modify event->note + *event = *get_event_p(current_pattern, chan, current_line); + + // Fixup event->note + if (event->note == 0xff) { // Key off + event->note = ch->event_table[chan].note | keyoff_flag; + } else if ((event->note >= fixed_note_flag + 1) /*&& (event->note <= fixed_note_flag + 12*8+1)*/) { + event->note -= fixed_note_flag; + } + + for (int slot = 0; slot < 2; slot++) { + ch->event_table[chan].eff[slot].def = event->eff[slot].def; + ch->event_table[chan].eff[slot].val = event->eff[slot].val; + } + + // alters ch->event_table[].instr_def + set_ins_data(event->instr_def, chan); + + // set effect_table here + process_effects(event, 0, chan); + process_effects(event, 1, chan); + + // TODO: is that needed here? + /*for (int slot = 0; slot < 2; slot++) { + if (event->eff[slot].def | event->eff[slot].val) { + ch->event_table[chan].eff[slot].def = event->eff[slot].def; + ch->event_table[chan].eff[slot].val = event->eff[slot].val; + } else if (ch->glfsld_table[slot][chan].def == 0 && ch->glfsld_table[slot][chan].val == 0) { + ch->effect_table[slot][chan].def = 0; + ch->effect_table[slot][chan].val = 0; + } + }*/ + + // alters ch->event_table[].note + new_process_note(event, chan); + + check_swap_arp_vibr(event, 0, chan); + check_swap_arp_vibr(event, 1, chan); + + update_fine_effects(0, chan); + update_fine_effects(1, chan); + } +} + +void Ca2mv2Player::generate_custom_vibrato(uint8_t value) +{ + const uint8_t vibtab_size[16] = { 16,16,16,16,32,32,32,32,64,64,64,64,128,128,128,128 }; + int idx, idx2; + + #define min0(VALUE) ((int)VALUE >= 0 ? (int)VALUE : 0) + + if (value == 0) { + // 0: set default speed table + vibtrem_table_size = def_vibtrem_table_size; + memcpy(&vibtrem_table, &def_vibtrem_table, sizeof(vibtrem_table)); + } else if (value <= 239) { + // 1-239: set custom speed table (fixed size = 32) + vibtrem_table_size = def_vibtrem_table_size; + double mul_r = (double)value / 16.0; + + for (idx2 = 0; idx2 <= 7; idx2++) { + vibtrem_table[idx2 * 32] = 0; + + for (idx = 1; idx <= 16; idx++) { + vibtrem_table[idx2 * 32 + idx] = (uint8_t)round(idx * mul_r); + } + + for (idx = 17; idx <= 31; idx++) { + vibtrem_table[idx2 * 32 + idx] = (uint8_t)round((32 - idx) * mul_r); + } + } + } else { + // 240-255: set custom speed table (speed factor = 1-4) + vibtrem_speed_factor = (value - 240) % 4 + 1; + vibtrem_table_size = 2 * vibtab_size[value - 240]; + int mul_b = 256 / (vibtab_size[value - 240]); + + for (idx2 = 0; idx2 <= 128 / vibtab_size[value - 240] - 1; idx2++) { + vibtrem_table[2 * vibtab_size[value - 240] * idx2] = 0; + + for (idx = 1; idx <= vibtab_size[value - 240]; idx++) { + vibtrem_table[2 * vibtab_size[value - 240] * idx2 + idx] = + min0(idx * mul_b - 1); + } + + for (idx = vibtab_size[value - 240] + 1; idx <= 2 * vibtab_size[value - 240] - 1; idx++) { + vibtrem_table[2 * vibtab_size[value - 240] * idx2 + idx] = + min0((2 * vibtab_size[value - 240] - idx) * mul_b - 1); + } + } + } +} + +void Ca2mv2Player::check_swap_arp_vibr(tADTRACK2_EVENT *event, int slot, int chan) +{ + // Check if second effect is ZFF - force no restart + bool is_norestart = (event->eff[slot ^ 1].def == ef_Extended) && + (event->eff[slot ^ 1].val == (ef_ex_ExtendedCmd2 * 16 + ef_ex_cmd2_NoRestart)); + + switch (event->eff[slot].def) { + case ef_SwapArpeggio: + if (is_norestart) { + tARPEGGIO_TABLE *arp = get_arpeggio_table(event->eff[slot].val); + uint8_t length = arp ? arp->length : 0; + + if (ch->macro_table[chan].arpg_pos > length) + ch->macro_table[chan].arpg_pos = length; + ch->macro_table[chan].arpg_table = event->eff[slot].val; + } else { + ch->macro_table[chan].arpg_count = 1; + ch->macro_table[chan].arpg_pos = 0; + ch->macro_table[chan].arpg_table = event->eff[slot].val; + ch->macro_table[chan].arpg_note = ch->event_table[chan].note; + } + break; + + case ef_SwapVibrato: + if (is_norestart) { + tVIBRATO_TABLE *vib = get_vibrato_table(event->eff[slot].val); + uint8_t length = vib ? vib->length : 0; + + if (ch->macro_table[chan].vib_pos > length) + ch->macro_table[chan].vib_pos = length; + ch->macro_table[chan].vib_table = event->eff[slot].val; + } else { + tVIBRATO_TABLE *vib = get_vibrato_table(ch->macro_table[chan].vib_table); + uint8_t vib_delay = vib ? vib->delay : 0; + + ch->macro_table[chan].vib_count = 1; + ch->macro_table[chan].vib_pos = 0; + ch->macro_table[chan].vib_table = event->eff[slot].val; + ch->macro_table[chan].vib_delay = vib_delay; + } + break; + case ef_SetCustomSpeedTab: + generate_custom_vibrato(event->eff[slot].val); + break; + } +} + +void Ca2mv2Player::portamento_up(int chan, uint16_t slide, uint16_t limit) +{ + uint16_t freq; + + if ((ch->freq_table[chan] & 0x1fff) == 0) return; + + freq = calc_freq_shift_up(ch->freq_table[chan] & 0x1fff, slide); + + change_frequency(chan, freq <= limit ? freq : limit); +} + +void Ca2mv2Player::portamento_down(int chan, uint16_t slide, uint16_t limit) +{ + uint16_t freq; + + if ((ch->freq_table[chan] & 0x1fff) == 0) return; + + freq = calc_freq_shift_down(ch->freq_table[chan] & 0x1fff, slide); + + change_frequency(chan, freq >= limit ? freq : limit); +} + +void Ca2mv2Player::macro_vibrato__porta_up(int chan, uint8_t depth) +{ + uint16_t freq = calc_freq_shift_up(ch->macro_table[chan].vib_freq & 0x1fff, depth); + uint16_t newfreq = freq <= nFreq(12*8+1) ? freq : nFreq(12*8+1); + + change_freq(chan, newfreq); +} + +void Ca2mv2Player::macro_vibrato__porta_down(int chan, uint8_t depth) +{ + uint16_t freq = calc_freq_shift_down(ch->macro_table[chan].vib_freq & 0x1fff, depth); + uint16_t newfreq = freq >= nFreq(0) ? freq : nFreq(0); + + change_freq(chan, newfreq); +} + +void Ca2mv2Player::tone_portamento(int slot, int chan) +{ + uint16_t freq = ch->freq_table[chan] & 0x1fff; + + if (freq > ch->porta_table[slot][chan].freq) { + portamento_down(chan, ch->porta_table[slot][chan].speed, ch->porta_table[slot][chan].freq); + } else if (freq < ch->porta_table[slot][chan].freq) { + portamento_up(chan, ch->porta_table[slot][chan].speed, ch->porta_table[slot][chan].freq); + } +} + +void Ca2mv2Player::slide_carrier_volume_up(uint8_t chan, uint8_t slide, uint8_t limit) +{ + uint8_t volC = ch->fmpar_table[chan].volC; + uint8_t newvolC = (volC - slide >= limit) ? volC - slide : limit; + + set_ins_volume(BYTE_NULL, newvolC, chan); +} + +void Ca2mv2Player::slide_modulator_volume_up(uint8_t chan, uint8_t slide, uint8_t limit) +{ + uint8_t volM = ch->fmpar_table[chan].volM; + uint8_t newvolM = (volM - slide >= limit) ? volM - slide : limit; + + set_ins_volume(newvolM, BYTE_NULL, chan); +} + +void Ca2mv2Player::slide_volume_up(int chan, uint8_t slide) +{ + uint8_t limit1 = 0, limit2 = 0; + t4OP_DATA d = get_4op_data(chan); + + if (!_4op_vol_valid_chan(chan)) { + tINSTR_DATA *ins = get_instr_data(ch->event_table[chan].instr_def); + + limit1 = ch->peak_lock[chan] ? ins->fm.volC : 0; + limit2 = ch->peak_lock[chan] ? ins->fm.volM : 0; + } + + switch (ch->volslide_type[chan]) { + case 0: + if (!_4op_vol_valid_chan(chan)) { + tINSTR_DATA *i = get_instr_data_by_ch(chan); + + slide_carrier_volume_up(chan, slide, limit1); + + if (i->fm.connect || (percussion_mode && (chan >= 16))) // in [17..20] + slide_modulator_volume_up(chan, slide, limit2); + } else { + // Can use get_instr_data_by_ch() + tINSTR_DATA *ins1 = get_instr_data(d.ins1); + tINSTR_DATA *ins2 = get_instr_data(d.ins2); + + uint8_t limit1_volC = ch->peak_lock[d.ch1] ? ins1->fm.volC : 0; + uint8_t limit1_volM = ch->peak_lock[d.ch1] ? ins1->fm.volM : 0; + uint8_t limit2_volC = ch->peak_lock[d.ch2] ? ins2->fm.volC : 0; + uint8_t limit2_volM = ch->peak_lock[d.ch2] ? ins2->fm.volM : 0; + + switch (d.conn) { + // FM/FM + case 0: + slide_carrier_volume_up(d.ch1, slide, limit1_volC); + break; + // FM/AM + case 1: + slide_carrier_volume_up(d.ch1, slide, limit1_volC); + slide_modulator_volume_up(d.ch2, slide, limit2_volM); + break; + // AM/FM + case 2: + slide_carrier_volume_up(d.ch1, slide, limit1_volC); + slide_carrier_volume_up(d.ch2, slide, limit2_volC); + break; + // AM/AM + case 3: + slide_carrier_volume_up(d.ch1, slide, limit1_volC); + slide_modulator_volume_up(d.ch1, slide, limit1_volM); + slide_modulator_volume_up(d.ch2, slide, limit2_volM); + break; + } + } + break; + + case 1: + slide_carrier_volume_up(chan, slide, limit1); + break; + + case 2: + slide_modulator_volume_up(chan, slide, limit2); + break; + + case 3: + slide_carrier_volume_up(chan, slide, limit1); + slide_modulator_volume_up(chan, slide, limit2); + break; + } +} + +void Ca2mv2Player::slide_carrier_volume_down(uint8_t chan, uint8_t slide) +{ + uint8_t volC = ch->fmpar_table[chan].volC; + uint8_t newvolC = volC + slide <= 63 ? volC + slide : 63; + + set_ins_volume(BYTE_NULL, newvolC, chan); +} + +void Ca2mv2Player::slide_modulator_volume_down(uint8_t chan, uint8_t slide) +{ + uint8_t volM = ch->fmpar_table[chan].volM; + uint8_t newvolM = volM + slide <= 63 ? volM + slide : 63; + + set_ins_volume(newvolM, BYTE_NULL, chan); +} + +void Ca2mv2Player::slide_volume_down(int chan, uint8_t slide) +{ + t4OP_DATA d = get_4op_data(chan); + + switch (ch->volslide_type[chan]) { + case 0: + if (!_4op_vol_valid_chan(chan)) { + tINSTR_DATA *i = get_instr_data_by_ch(chan); + + slide_carrier_volume_down(chan, slide); + + if (i->fm.connect || (percussion_mode && (chan >= 16))) { //in [17..20] + slide_modulator_volume_down(chan, slide); + } + } else { + switch (d.conn) { + // FM/FM + case 0: + slide_carrier_volume_down(d.ch1, slide); + break; + // FM/AM + case 1: + slide_carrier_volume_down(d.ch1, slide); + slide_modulator_volume_down(d.ch2, slide); + break; + // AM/FM + case 2: + slide_carrier_volume_down(d.ch1, slide); + slide_carrier_volume_down(d.ch2, slide); + break; + // AM/AM + case 3: + slide_carrier_volume_down(d.ch1, slide); + slide_modulator_volume_down(d.ch1, slide); + slide_modulator_volume_down(d.ch2, slide); + break; + } + } + break; + + case 1: + slide_carrier_volume_down(chan, slide); + break; + + case 2: + slide_modulator_volume_down(chan, slide); + break; + + case 3: + slide_carrier_volume_down(chan, slide); + slide_modulator_volume_down(chan, slide); + break; + } +} + +void Ca2mv2Player::volume_slide(int chan, uint8_t up_speed, uint8_t down_speed) +{ + if (up_speed) + slide_volume_up(chan, up_speed); + else if (down_speed) { + slide_volume_down(chan, down_speed); + } +} + +void Ca2mv2Player::global_volume_slide(uint8_t up_speed, uint8_t down_speed) +{ + if (up_speed != BYTE_NULL) + global_volume = max(global_volume + up_speed, 63); + + if (down_speed != BYTE_NULL) { + if (global_volume >= down_speed) + global_volume -= down_speed; + else + global_volume = 0; + } + + set_global_volume(); +} + +void Ca2mv2Player::arpeggio(int slot, int chan) +{ + static uint8_t arpgg_state[3] = {1, 2, 0}; + + uint16_t freq; + + switch (ch->arpgg_table[slot][chan].state) { + case 0: freq = nFreq(ch->arpgg_table[slot][chan].note - 1); break; + case 1: freq = nFreq(ch->arpgg_table[slot][chan].note - 1 + ch->arpgg_table[slot][chan].add1); break; + case 2: freq = nFreq(ch->arpgg_table[slot][chan].note - 1 + ch->arpgg_table[slot][chan].add2); break; + default: freq = 0; + } + + ch->arpgg_table[slot][chan].state = arpgg_state[ch->arpgg_table[slot][chan].state]; + change_frequency(chan, freq + + get_instr_fine_tune(ch->event_table[chan].instr_def)); +} + +void Ca2mv2Player::vibrato(int slot, int chan) +{ + uint16_t freq, slide; + uint8_t direction; + + freq = ch->freq_table[chan]; + + ch->vibr_table[slot][chan].pos += ch->vibr_table[slot][chan].speed; + slide = calc_vibrato_shift(ch->vibr_table[slot][chan].depth, ch->vibr_table[slot][chan].pos); + direction = ch->vibr_table[slot][chan].pos & 0x20; + + if (direction == 0) + portamento_down(chan, slide, nFreq(0)); + else + portamento_up(chan, slide, nFreq(12*8+1)); + + ch->freq_table[chan] = freq; +} + +void Ca2mv2Player::tremolo(int slot, int chan) +{ + uint16_t slide; + uint8_t direction; + + uint8_t volM = ch->fmpar_table[chan].volM; + uint8_t volC = ch->fmpar_table[chan].volC; + + ch->trem_table[slot][chan].pos += ch->trem_table[slot][chan].speed; + slide = calc_vibrato_shift(ch->trem_table[slot][chan].depth, ch->trem_table[slot][chan].pos); + direction = ch->trem_table[slot][chan].pos & 0x20; + + if (direction == 0) + slide_volume_down(chan, slide); + else + slide_volume_up(chan, slide); + + // is this needed? + ch->fmpar_table[chan].volM = volM; + ch->fmpar_table[chan].volC = volC; +} + +inline int Ca2mv2Player::chanvol(int chan) +{ + tINSTR_DATA *instr = get_instr_data_by_ch(chan); + + if (instr->fm.connect == 0) + return 63 - ch->fmpar_table[chan].volC; + else + return 63 - (ch->fmpar_table[chan].volM + ch->fmpar_table[chan].volC) / 2; +} + +void Ca2mv2Player::update_effects_slot(int slot, int chan) +{ + uint8_t def = ch->effect_table[slot][chan].def; + uint8_t val = ch->effect_table[slot][chan].val; + + switch (def) { + case ef_Arpeggio: + if (!val) + break; + + arpeggio(slot, chan); + break; + + case ef_ArpggVSlide: + volume_slide(chan, val / 16, val % 16); + arpeggio(slot, chan); + break; + + case ef_ArpggVSlideFine: + arpeggio(slot, chan); + break; + + case ef_FSlideUp: + portamento_up(chan, val, nFreq(12*8+1)); + break; + + case ef_FSlideDown: + portamento_down(chan, val, nFreq(0)); + break; + + case ef_FSlideUpVSlide: + portamento_up(chan, ch->fslide_table[slot][chan], nFreq(12*8+1)); + volume_slide(chan, val / 16, val % 16); + break; + + case ef_FSlUpVSlF: + portamento_up(chan, ch->fslide_table[slot][chan], nFreq(12*8+1)); + break; + + case ef_FSlideDownVSlide: + portamento_down(chan, ch->fslide_table[slot][chan], nFreq(0)); + volume_slide(chan, val / 16, val % 16); + break; + + case ef_FSlDownVSlF: + portamento_down(chan, ch->fslide_table[slot][chan], nFreq(0)); + break; + + case ef_FSlUpFineVSlide: + volume_slide(chan, val / 16, val % 16); + break; + + case ef_FSlDownFineVSlide: + volume_slide(chan, val / 16, val % 16); + break; + + case ef_TonePortamento: + tone_portamento(slot, chan); + break; + + case ef_TPortamVolSlide: + volume_slide(chan, val / 16, val % 16); + tone_portamento(slot, chan); + break; + + case ef_TPortamVSlideFine: + tone_portamento(slot, chan); + break; + + case ef_Vibrato: + if (!ch->vibr_table[slot][chan].fine) + vibrato(slot, chan); + break; + + case ef_Tremolo: + if (!ch->trem_table[slot][chan].fine) + tremolo(slot, chan); + break; + + case ef_VibratoVolSlide: + volume_slide(chan, val / 16, val % 16); + if (!ch->vibr_table[slot][chan].fine) + vibrato(slot, chan); + break; + + case ef_VibratoVSlideFine: + if (!ch->vibr_table[slot][chan].fine) + vibrato(slot, chan); + break; + + case ef_VolSlide: + volume_slide(chan, val / 16, val % 16); + break; + + case ef_RetrigNote: + if (ch->retrig_table[slot][chan] >= val) { + ch->retrig_table[slot][chan] = 0; + output_note(ch->event_table[chan].note, ch->event_table[chan].instr_def, chan, true, true); + } else { + ch->retrig_table[slot][chan]++; + } + break; + + case ef_MultiRetrigNote: + if (ch->retrig_table[slot][chan] >= val / 16) { + switch (val % 16) { + case 0: break; + case 8: break; + + case 1: slide_volume_down(chan, 1); break; + case 2: slide_volume_down(chan, 2); break; + case 3: slide_volume_down(chan, 4); break; + case 4: slide_volume_down(chan, 8); break; + case 5: slide_volume_down(chan, 16); break; + + case 9: slide_volume_up(chan, 1); break; + case 10: slide_volume_up(chan, 2); break; + case 11: slide_volume_up(chan, 4); break; + case 12: slide_volume_up(chan, 8); break; + case 13: slide_volume_up(chan, 16); break; + + case 6: slide_volume_down(chan, chanvol(chan) - chanvol(chan) * 2 / 3); + break; + + case 7: slide_volume_down(chan, chanvol(chan) - chanvol(chan) * 1 / 2); + break; + + case 14: slide_volume_up(chan, max(chanvol(chan) * 3 / 2 - chanvol(chan), 63)); + break; + + case 15: slide_volume_up(chan,max(chanvol(chan) * 2 - chanvol(chan), 63)); + break; + } + + ch->retrig_table[slot][chan] = 0; + output_note(ch->event_table[chan].note, ch->event_table[chan].instr_def, chan, true, true); + } else { + ch->retrig_table[slot][chan]++; + } + break; + + case ef_Tremor: + if (ch->tremor_table[slot][chan].pos >= 0) { + if ((ch->tremor_table[slot][chan].pos + 1) <= val / 16) { + ch->tremor_table[slot][chan].pos++; + } else { + slide_volume_down(chan, 63); + ch->tremor_table[slot][chan].pos = -1; + } + } else { + if ((ch->tremor_table[slot][chan].pos - 1) >= -(val % 16)) { + ch->tremor_table[slot][chan].pos--; + } else { + set_ins_volume(ch->tremor_table[slot][chan].volM, ch->tremor_table[slot][chan].volC, chan); + ch->tremor_table[slot][chan].pos = 1; + } + } + break; + + case ef_Extended2: + switch (val / 16) { + case ef_ex2_NoteDelay: + if (ch->notedel_table[chan] == 0) { + ch->notedel_table[chan] = BYTE_NULL; + output_note(ch->event_table[chan].note, ch->event_table[chan].instr_def, chan, true, true); + } else if (ch->notedel_table[chan] != BYTE_NULL) { + ch->notedel_table[chan]--; + } + break; + case ef_ex2_NoteCut: + if (ch->notecut_table[chan] == 0) { + ch->notecut_table[chan] = BYTE_NULL; + key_off(chan); + } else if (ch->notecut_table[chan] != BYTE_NULL) { + ch->notecut_table[chan]--; + } + break; + case ef_ex2_GlVolSlideUp: global_volume_slide(val & 0xf, BYTE_NULL); break; + case ef_ex2_GlVolSlideDn: global_volume_slide(BYTE_NULL, val & 0xf); break; + } + break; + } +} + +void Ca2mv2Player::update_effects() +{ + for (int chan = 0; chan < songinfo->nm_tracks; chan++) { + update_effects_slot(0, chan); + update_effects_slot(1, chan); + } +} + +void Ca2mv2Player::update_fine_effects(int slot, int chan) +{ + uint8_t def = ch->effect_table[slot][chan].def; + uint8_t val = ch->effect_table[slot][chan].val; + + switch (def) { + case ef_ArpggVSlideFine: volume_slide(chan, val / 16, val % 16); break; + case ef_FSlideUpFine: portamento_up(chan, val, nFreq(12*8+1)); break; + case ef_FSlideDownFine: portamento_down(chan, val, nFreq(0)); break; + case ef_FSlUpVSlF: volume_slide(chan, val / 16, val % 16); break; + case ef_FSlDownVSlF: volume_slide(chan, val / 16, val % 16); break; + case ef_FSlUpFineVSlide: portamento_up(chan, ch->fslide_table[slot][chan], nFreq(12*8+1)); break; + case ef_FSlDownFineVSlide: portamento_down(chan, ch->fslide_table[slot][chan], nFreq(0)); break; + + case ef_FSlUpFineVSlF: + portamento_up(chan, ch->fslide_table[slot][chan], nFreq(12*8+1)); + volume_slide(chan, val / 16, val % 16); + break; + + case ef_FSlDownFineVSlF: + portamento_down(chan, ch->fslide_table[slot][chan], nFreq(0)); + volume_slide(chan, val / 16, val % 16); + break; + + case ef_TPortamVSlideFine: volume_slide(chan, val / 16, val % 16); break; + case ef_Vibrato: if (ch->vibr_table[slot][chan].fine) vibrato(slot, chan); break; + case ef_Tremolo: if (ch->trem_table[slot][chan].fine) tremolo(slot, chan); break; + case ef_VibratoVolSlide: if (ch->vibr_table[slot][chan].fine) vibrato(slot, chan); break; + + case ef_VibratoVSlideFine: + volume_slide(chan, val / 16, val % 16); + if (ch->vibr_table[slot][chan].fine) + vibrato(slot, chan); + break; + + case ef_VolSlideFine: volume_slide(chan, val / 16, val % 16); break; + + case ef_Extended2: + switch(val / 16) { + case ef_ex2_GlVolSlideUpF: global_volume_slide(val & 0xf, BYTE_NULL); break; + case ef_ex2_GlVolSlideDnF: global_volume_slide(BYTE_NULL, val & 0xf); break; + } + break; + } +} + +void Ca2mv2Player::update_extra_fine_effects_slot(int slot, int chan) +{ + uint8_t def = ch->effect_table[slot][chan].def; + uint8_t val = ch->effect_table[slot][chan].val; + + switch (def) { + case ef_Extended2: + switch(val / 16) { + case ef_ex2_GlVolSldUpXF: global_volume_slide(val & 0xf, BYTE_NULL); break; + case ef_ex2_GlVolSldDnXF: global_volume_slide(BYTE_NULL, val & 0xf); break; + case ef_ex2_VolSlideUpXF: volume_slide(chan, val & 0xf, 0); break; + case ef_ex2_VolSlideDnXF: volume_slide(chan, 0, val & 0xf); break; + case ef_ex2_FreqSlideUpXF: portamento_up(chan, val & 0xf, nFreq(12*8+1)); break; + case ef_ex2_FreqSlideDnXF: portamento_down(chan, val & 0xf, nFreq(0)); break; + } + break; + + case ef_GlobalFreqSlideUpXF: portamento_up(chan, val, nFreq(12*8+1)); break; + case ef_GlobalFreqSlideDnXF: portamento_down(chan, val, nFreq(0)); break; + case ef_ExtraFineArpeggio: arpeggio(slot, chan); break; + case ef_ExtraFineVibrato: if (!ch->vibr_table[slot][chan].fine) vibrato(slot, chan); break; + case ef_ExtraFineTremolo: if (!ch->trem_table[slot][chan].fine) tremolo(slot, chan); break; + } +} + +void Ca2mv2Player::update_extra_fine_effects() +{ + for (int chan = 0; chan < songinfo->nm_tracks; chan++) { + update_extra_fine_effects_slot(0, chan); + update_extra_fine_effects_slot(1, chan); + } +} + +void Ca2mv2Player::set_current_order(uint8_t new_order) +{ + if (new_order >= 0x80) { + AdPlug_LogWrite("set_current_order parameter is out of bounds, possibly corrupt file\n"); + } + current_order = new_order < 0x80 ? new_order : 0; +} + +int Ca2mv2Player::calc_following_order(uint8_t order) +{ + int result; + uint8_t index, jump_count; + + result = -1; + index = order; + jump_count = 0; + + do { + if (songinfo->pattern_order[index] < 0x80) { + result = index; + } else { + index = songinfo->pattern_order[index] - 0x80; + jump_count++; + } + } while (!((jump_count > 0x7f) || (result != -1))); + + return result; +} + +int Ca2mv2Player::calc_order_jump() +{ + uint8_t temp = 0; + int result = 0; + + do { + if (songinfo->pattern_order[current_order] > 0x7f) { + set_current_order(songinfo->pattern_order[current_order] - 0x80); + songend = true; + } + temp++; + } while (!((temp > 0x7f) || (songinfo->pattern_order[current_order] < 0x80))); + + if (temp > 0x7f) { + a2t_stop(); + result = -1; + } + + return result; +} + +void Ca2mv2Player::update_song_position() +{ + if ((current_line < songinfo->patt_len - 1) && !pattern_break) { + current_line++; + } else { + if (!(pattern_break && ((next_line & 0xf0) == pattern_loop_flag)) && (current_order < 0x7f)) { + memset(ch->loopbck_table, BYTE_NULL, sizeof(ch->loopbck_table)); + memset(ch->loop_table, BYTE_NULL, sizeof(ch->loop_table)); + current_order++; + } + + if (pattern_break && ((next_line & 0xf0) == pattern_loop_flag)) { + uint8_t temp; + + temp = next_line - pattern_loop_flag; + next_line = ch->loopbck_table[temp]; + + if (ch->loop_table[temp][current_line] != 0) + ch->loop_table[temp][current_line]--; + } else { + if (pattern_break && ((next_line & 0xf0) == pattern_break_flag)) { + uint8_t old_order = current_order; + if (ch->event_table[next_line - pattern_break_flag].eff[1].def == ef_PositionJump) { + set_current_order(ch->event_table[next_line - pattern_break_flag].eff[1].val); + } else { + set_current_order(ch->event_table[next_line - pattern_break_flag].eff[0].val); + } + if (current_order <= old_order) + songend = true; + pattern_break = false; + } else { + if (current_order >= 0x7f) + set_current_order(0); + } + } + + if ((songinfo->pattern_order[current_order] > 0x7f) && (calc_order_jump() == -1)) { + return; + } + + current_pattern = songinfo->pattern_order[current_order]; + if (!pattern_break) { + current_line = 0; + } else { + pattern_break = false; + current_line = next_line; + } + } + + for (int chan = 0; chan < songinfo->nm_tracks; chan++) { + ch->glfsld_table[0][chan].def = 0; + ch->glfsld_table[0][chan].val = 0; + ch->glfsld_table[1][chan].def = 0; + ch->glfsld_table[1][chan].val = 0; + } + + if ((current_line == 0) && + (current_order == calc_following_order(0)) && speed_update) { + tempo = songinfo->tempo; + speed = songinfo->speed; + update_timer(tempo); + } +} + +void Ca2mv2Player::poll_proc() +{ + if (pattern_delay) { + update_effects(); + if (tickD > 1) { + tickD--; + } else { + pattern_delay = false; + } + } else { + if (ticks == 0) { + play_line(); + ticks = speed; + update_song_position(); + } + update_effects(); + ticks--; + } + + tickXF++; + if (tickXF % 4 == 0) { + update_extra_fine_effects(); + tickXF -= 4; + } +} + +void Ca2mv2Player::macro_poll_proc() +{ +#define IDLE 0xfff +#define FINISHED 0xffff + uint16_t chan; + uint16_t finished_flag; + + for (chan = 0; chan < 20; chan++) { + finished_flag = ch->keyoff_loop[chan] ? IDLE : FINISHED; + + tCH_MACRO_TABLE *mt = &ch->macro_table[chan]; + tFMREG_TABLE *rt = get_fmreg_table(mt->fmreg_ins); + + bool force_macro_keyon = false; + + if (rt && rt->length /* && (speed != 0)*/) { // FIXME: what speed? + if (mt->fmreg_duration > 1) { + mt->fmreg_duration--; + } else { + if (mt->fmreg_pos <= rt->length) { + if (rt->loop_begin && rt->loop_length) { + if (mt->fmreg_pos == rt->loop_begin + rt->loop_length - 1) { + mt->fmreg_pos = rt->loop_begin; + } else { + if (mt->fmreg_pos < rt->length) { + mt->fmreg_pos++; + } else { + mt->fmreg_pos = finished_flag; + } + } + } else { + if (mt->fmreg_pos < rt->length) { + mt->fmreg_pos++; + } else { + mt->fmreg_pos = finished_flag; + } + } + } else { + mt->fmreg_pos = finished_flag; + } + + if (((ch->freq_table[chan] | 0x2000) == ch->freq_table[chan]) && + (rt->keyoff_pos != 0) && + (mt->fmreg_pos >= rt->keyoff_pos)) { + mt->fmreg_pos = IDLE; + } else { + if (((ch->freq_table[chan] | 0x2000) != ch->freq_table[chan]) && + (mt->fmreg_pos != 0) && (rt->keyoff_pos != 0) && + ((mt->fmreg_pos < rt->keyoff_pos) || (mt->fmreg_pos == IDLE))) + mt->fmreg_pos = rt->keyoff_pos; + } + + if (mt->fmreg_pos && mt->fmreg_pos != IDLE && mt->fmreg_pos != finished_flag) { + mt->fmreg_duration = rt->data[mt->fmreg_pos - 1].duration; + + if (mt->fmreg_duration) { + tREGISTER_TABLE_DEF *d = &rt->data[mt->fmreg_pos - 1]; + // NOTE: if we are already here, no need to call get_instr() + uint32_t disabled = instrinfo->instruments[mt->fmreg_ins - 1].dis_fmreg_cols; + + // force KEY-ON with missing ADSR instrument data + force_macro_keyon = false; + if (mt->fmreg_pos == 1) { + uint32_t adsr_disabled = disabled & ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | + (1 << 12) | (1 << 13) | (1 << 14) | (1 << 15)); + if (is_ins_adsr_data_empty(ch->voice_table[chan]) && !adsr_disabled) { + force_macro_keyon = true; + } + } + + for (unsigned bit = 0; bit < 28; bit++) { + if (disabled & (1 << bit)) + continue; + + switch (bit) { + case 0: ch->fmpar_table[chan].attckM = d->fm.attckM; break; + case 1: ch->fmpar_table[chan].decM = d->fm.decM; break; + case 2: ch->fmpar_table[chan].sustnM = d->fm.sustnM; break; + case 3: ch->fmpar_table[chan].relM = d->fm.relM; break; + case 4: ch->fmpar_table[chan].wformM = d->fm.wformM; break; + case 5: set_ins_volume(63 - d->fm.volM, BYTE_NULL, chan); break; + case 6: ch->fmpar_table[chan].kslM = d->fm.kslM; break; + case 7: ch->fmpar_table[chan].multipM = d->fm.multipM; break; + case 8: ch->fmpar_table[chan].tremM = d->fm.tremM; break; + case 9: ch->fmpar_table[chan].vibrM = d->fm.vibrM; break; + case 10: ch->fmpar_table[chan].ksrM = d->fm.ksrM; break; + case 11: ch->fmpar_table[chan].sustM = d->fm.sustM; break; + case 12: ch->fmpar_table[chan].attckC = d->fm.attckC; break; + case 13: ch->fmpar_table[chan].decC = d->fm.decC; break; + case 14: ch->fmpar_table[chan].sustnC = d->fm.sustnC; break; + case 15: ch->fmpar_table[chan].relC = d->fm.relC; break; + case 16: ch->fmpar_table[chan].wformC = d->fm.wformC; break; + case 17: set_ins_volume(BYTE_NULL, 63 - d->fm.volC, chan); break; + case 18: ch->fmpar_table[chan].kslC = d->fm.kslC; break; + case 19: ch->fmpar_table[chan].multipC = d->fm.multipC; break; + case 20: ch->fmpar_table[chan].tremC = d->fm.tremC; break; + case 21: ch->fmpar_table[chan].vibrC = d->fm.vibrC; break; + case 22: ch->fmpar_table[chan].ksrC = d->fm.ksrC; break; + case 23: ch->fmpar_table[chan].sustC = d->fm.sustC; break; + case 24: ch->fmpar_table[chan].connect = d->fm.connect; break; + case 25: ch->fmpar_table[chan].feedb = d->fm.feedb; break; + case 27: if (!ch->pan_lock[chan]) ch->panning_table[chan] = d->panning; break; + } + } + + update_modulator_adsrw(chan); + update_carrier_adsrw(chan); + update_fmpar(chan); + + // TODO: check if those flags are really set by the editor + uint8_t macro_flags = d->fm.data[10]; + + if (force_macro_keyon || (macro_flags & 0x80)) { // MACRO_NOTE_RETRIG_FLAG + if (!((is_4op_chan(chan) && is_4op_chan_hi(chan)))) { + output_note(ch->event_table[chan].note, + ch->event_table[chan].instr_def, chan, false, true); + if (is_4op_chan(chan) && is_4op_chan_lo(chan)) + init_macro_table(chan - 1, 0, ch->voice_table[chan - 1], 0); + } + } else if (macro_flags & 0x40) { // MACRO_ENVELOPE_RESTART_FLAG + key_on(chan); + change_freq(chan, ch->freq_table[chan]); + } else if (macro_flags & 0x20) { // MACRO_ZERO_FREQ_FLAG + if (ch->freq_table[chan]) { + ch->zero_fq_table[chan] = ch->freq_table[chan]; + ch->freq_table[chan] &= ~0x1fff; + change_freq(chan, ch->freq_table[chan]); + } else if (ch->zero_fq_table[chan]) { + ch->freq_table[chan] = ch->zero_fq_table[chan]; + ch->zero_fq_table[chan] = 0; + change_freq(chan, ch->freq_table[chan]); + } + } + + int16_t freq_slide = INT16LE(d->freq_slide); + + if (!(disabled & (1 << 26))) { + if (freq_slide > 0) { + portamento_up(chan, freq_slide, nFreq(12*8+1)); + } else if (freq_slide < 0) { + portamento_down(chan, abs(freq_slide), nFreq(0)); + } + } + } + } + } + } + + tARPEGGIO_TABLE *at = get_arpeggio_table(mt->arpg_table); + + if (at && at->length && at->speed) { + if (mt->arpg_count == at->speed) { + mt->arpg_count = 1; + + if (mt->arpg_pos <= at->length) { + if ((at->loop_begin != 0) && (at->loop_length != 0)) { + if (mt->arpg_pos == at->loop_begin + (at->loop_length - 1)) { + mt->arpg_pos = at->loop_begin; + } else { + if (mt->arpg_pos < at->length) + mt->arpg_pos++; + else + mt->arpg_pos = finished_flag; + } + } else { + if (mt->arpg_pos < at->length) + mt->arpg_pos++; + else + mt->arpg_pos = finished_flag; + } + } else { + mt->arpg_pos = finished_flag; + } + + if (((ch->freq_table[chan] | 0x2000) == ch->freq_table[chan]) && + (at->keyoff_pos != 0) && + (mt->arpg_pos >= at->keyoff_pos)) { + mt->arpg_pos = IDLE; + } else { + if (((ch->freq_table[chan] | 0x2000) != ch->freq_table[chan]) && + (at->keyoff_pos != 0) && (at->keyoff_pos != 0) && + ((mt->arpg_pos < at->keyoff_pos) || (mt->arpg_pos == IDLE))) + mt->arpg_pos = at->keyoff_pos; + } + + if ((mt->arpg_pos != 0) && + (mt->arpg_pos != IDLE) && (mt->arpg_pos != finished_flag)) { + int8_t fine_tune = get_instr_fine_tune(ch->event_table[chan].instr_def); + uint8_t d = at->data[mt->arpg_pos - 1]; + + if (d == 0) { + change_frequency(chan, nFreq(mt->arpg_note - 1) + fine_tune); + } else if (d <= 96) { + // 1 - 96: + change_frequency(chan, nFreq(max(mt->arpg_note + at->data[mt->arpg_pos], 97) - 1) + + fine_tune); + } else if (d >= 0x80 && d <= 0x80+12*8+1) { + // 0x80 - 0x80+12*8+1: + change_frequency(chan, nFreq(at->data[mt->arpg_pos - 1] - 0x80 - 1) + + fine_tune); + } + } + } else { + mt->arpg_count++; + } + } + + tVIBRATO_TABLE *vt = get_vibrato_table(mt->vib_table); + + if (vt && vt->length && vt->speed && !mt->vib_paused) { + if (mt->vib_count == vt->speed) { + if (mt->vib_delay != 0) { + mt->vib_delay--; + } else { + mt->vib_count = 1; + + if (mt->vib_pos <= vt->length) { + if ((vt->loop_begin != 0) && (vt->loop_length != 0)) { + if (mt->vib_pos == vt->loop_begin + (vt->loop_length-1)) { + mt->vib_pos = vt->loop_begin; + } else { + if (mt->vib_pos < vt->length) + mt->vib_pos++; + else + mt->vib_pos = finished_flag; + } + } else { + if (mt->vib_pos < vt->length) + mt->vib_pos++; + else + mt->vib_pos = finished_flag; + } + } else { + mt->vib_pos = finished_flag; + } + + if (((ch->freq_table[chan] | 0x2000) == ch->freq_table[chan]) && + (vt->keyoff_pos != 0) & + (mt->vib_pos >= vt->keyoff_pos)) { + mt->vib_pos = IDLE; + } else { + if (((ch->freq_table[chan] | 0x2000) != ch->freq_table[chan]) && + (mt->vib_pos != 0) && (vt->keyoff_pos != 0) && + ((mt->vib_pos < vt->keyoff_pos) || (mt->vib_pos == IDLE))) + mt->vib_pos = vt->keyoff_pos; + } + + if ((mt->vib_pos != 0) && + (mt->vib_pos != IDLE) && (mt->vib_pos != finished_flag)) { + if (vt->data[mt->vib_pos - 1] > 0) + macro_vibrato__porta_up(chan, vt->data[mt->vib_pos]); + else if (vt->data[mt->vib_pos - 1] < 0) + macro_vibrato__porta_down(chan, abs(vt->data[mt->vib_pos])); + else + change_freq(chan, mt->vib_freq); + } + } + } else { + mt->vib_count++; + } + } + } +} + +void Ca2mv2Player::newtimer() +{ + if ((ticklooper == 0) && (irq_mode)) { + poll_proc(); + if (IRQ_freq != tempo * _macro_speedup()) { + IRQ_freq = (tempo < 18 ? 18 : tempo) * _macro_speedup(); + } + } + + if ((macro_ticklooper == 0) && (irq_mode)) + macro_poll_proc(); + + ticklooper++; + if (ticklooper >= IRQ_freq / tempo) + ticklooper = 0; + + macro_ticklooper++; + if (macro_ticklooper >= IRQ_freq / (tempo * _macro_speedup())) + macro_ticklooper = 0; +} + +void Ca2mv2Player::init_irq() +{ + if (irq_initialized) + return; + + irq_initialized = true; + update_timer(50); +} + +void Ca2mv2Player::done_irq() +{ + if (!irq_initialized) + return; + + irq_initialized = false; + irq_mode = true; + update_timer(0); + irq_mode = false; +} + +void Ca2mv2Player::init_buffers() +{ + memset(ch, 0, sizeof(*ch)); + + if (!lockvol) { + memset(ch->volume_lock, 0, sizeof(ch->volume_lock)); + } else { + for (int i = 0; i < 20; i++) + ch->volume_lock[i] = (bool)((songinfo->lock_flags[i] >> 4) & 1); + } + + if (!panlock) { + memset(ch->panning_table, 0, sizeof(ch->panning_table)); + } else { + for (int i = 0; i < 20; i++) + ch->panning_table[i] = songinfo->lock_flags[i] & 3; + } + + if (!lockVP) { + memset(ch->peak_lock, 0, sizeof(ch->peak_lock)); + } else { + for (int i = 0; i < 20; i++) + ch->peak_lock[i] = (bool)((songinfo->lock_flags[i] >> 5) & 1); + } + + static uint8_t _4op_main_chan[6] = { 1, 3, 5, 10, 12, 14 }; // 0-based + + memset(ch->vol4op_lock, false, sizeof(ch->vol4op_lock)); + for (int i = 0; i < 6; i++) { + ch->vol4op_lock[_4op_main_chan[i]] = + ((songinfo->lock_flags[_4op_main_chan[i]] | 0x40) == songinfo->lock_flags[_4op_main_chan[i]]); + ch->vol4op_lock[_4op_main_chan[i] - 1] = + ((songinfo->lock_flags[_4op_main_chan[i] - 1] | 0x40) == songinfo->lock_flags[_4op_main_chan[i] - 1]); + } + + for (int i = 0; i < 20; i++) + ch->volslide_type[i] = (songinfo->lock_flags[i] >> 2) & 3; + + memset(ch->notedel_table, BYTE_NULL, sizeof(ch->notedel_table)); + memset(ch->notecut_table, BYTE_NULL, sizeof(ch->notecut_table)); + memset(ch->loopbck_table, BYTE_NULL, sizeof(ch->loopbck_table)); + memset(ch->loop_table, BYTE_NULL, sizeof(ch->loop_table)); +} + +void Ca2mv2Player::init_player() +{ + opl2out(0x01, 0); + + for (int i = 0; i < 18; i++) + opl2out(0xb0 + regoffs_n(i), 0); + + for (int i = 0x80; i <= 0x8d; i++) + opl2out(i, BYTE_NULL); + + for (int i = 0x90; i <= 0x95; i++) + opl2out(i, BYTE_NULL); + + misc_register = (tremolo_depth << 7) + + (vibrato_depth << 6) + + (percussion_mode << 5); + + opl2out(0x01, 0x20); + opl2out(0x08, 0x40); + opl3exp(0x0105); + opl3exp(0x04 + (songinfo->flag_4op << 8)); + + key_off(16); + key_off(17); + opl2out(0xbd, misc_register); + + init_buffers(); + + current_tremolo_depth = tremolo_depth; + current_vibrato_depth = vibrato_depth; + global_volume = 63; + + vibtrem_speed_factor = def_vibtrem_speed_factor; + vibtrem_table_size = def_vibtrem_table_size; + memcpy(&vibtrem_table, &def_vibtrem_table, sizeof(vibtrem_table)); + + for (int i = 0; i < 20; i++) { + ch->arpgg_table[0][i].state = 1; + ch->arpgg_table[1][i].state = 1; + ch->voice_table[i] = i + 1; + } +} + +void Ca2mv2Player::a2t_stop() +{ + irq_mode = false; + play_status = isStopped; + global_volume = 63; + current_tremolo_depth = tremolo_depth; + current_vibrato_depth = vibrato_depth; + pattern_break = false; + current_order = 0; + current_pattern = 0; + current_line = 0; + playback_speed_shift = 0; + + for (int i = 0; i < 20; i++) + release_sustaining_sound(i); + + opl2out(0xbd, 0); + opl3exp(0x0004); + opl3exp(0x0005); + lockvol = false; + panlock = false; + lockVP = false; + init_buffers(); + + speed = 4; + update_timer(50); +} + +/* Clean songinfo before importing a2t tune */ +void Ca2mv2Player::init_songdata() +{ + memset(songinfo, 0, sizeof(*songinfo)); + memset(songinfo->pattern_order, 0x80, sizeof(songinfo->pattern_order)); + + IRQ_freq_shift = 0; + playback_speed_shift = 0; + songinfo->patt_len = 64; + songinfo->nm_tracks = 18; + songinfo->tempo = tempo; + songinfo->speed = speed; + songinfo->macro_speedup = 1; + speed_update = false; + lockvol = false; + panlock = false; + lockVP = false; + tremolo_depth = 0; + vibrato_depth = 0; + volume_scaling = false; + percussion_mode = false; +} + +bool Ca2mv2Player::a2t_play(char *tune, unsigned long size) // start_playing() +{ + bool err = a2_import(tune, size); + + if (!err) + return false; + + rewind (0); + + return true; +} + +/* LOADER FOR A2M/A2T */ + +void Ca2mv2Player::a2t_depack(char *src, int srcsize, char *dst, int dstsize) +{ + switch (ffver) { + case 1: + case 5: // sixpack + Sixdepak::decode((unsigned short *)src, srcsize, (unsigned char *)dst, dstsize); + break; + case 2: + case 6: // lzw + LZW_decompress(src, dst, srcsize, dstsize); + break; + case 3: + case 7: // lzss + LZSS_decompress(src, dst, srcsize, dstsize); + break; + case 4: + case 8: // unpacked + if (dstsize <= srcsize) + memcpy(dst, src, srcsize); + break; + case 9: + case 10: + case 11: // apack (aPlib) + aP_depack(src, dst, srcsize, dstsize); + break; + case 12: + case 13: + case 14: // lzh + LZH_decompress(src, dst, srcsize, dstsize); + break; + } +} + +// read the variable part of the header +int Ca2mv2Player::a2t_read_varheader(char *blockptr, unsigned long size) +{ + A2T_VARHEADER *varheader = (A2T_VARHEADER *)blockptr; + + switch (ffver) { + case 1: + case 2: + case 3: + case 4: + if (sizeof(A2T_VARHEADER_V1234) > size) + return INT_MAX; + for (int i = 0; i < 6; i++) + len[i] = UINT16LE(varheader->v1234.len[i]); + return sizeof(A2T_VARHEADER_V1234); + case 5: + case 6: + case 7: + case 8: + if (sizeof(A2T_VARHEADER_V5678) > size) + return INT_MAX; + songinfo->common_flag = varheader->v5678.common_flag; + for (int i = 0; i < 10; i++) + len[i] = UINT16LE(varheader->v5678.len[i]); + return sizeof(A2T_VARHEADER_V5678); + case 9: + if (sizeof(A2T_VARHEADER_V9) > size) + return INT_MAX; + songinfo->common_flag = varheader->v9.common_flag; + songinfo->patt_len = UINT16LE(varheader->v9.patt_len); + songinfo->nm_tracks = varheader->v9.nm_tracks; + songinfo->macro_speedup = UINT16LE(varheader->v9.macro_speedup); + for (int i = 0; i < 20; i++) + len[i] = UINT32LE(varheader->v9.len[i]); + return sizeof(A2T_VARHEADER_V9); + case 10: + if (sizeof(A2T_VARHEADER_V10) > size) + return INT_MAX; + songinfo->common_flag = varheader->v10.common_flag; + songinfo->patt_len = UINT16LE(varheader->v10.patt_len); + songinfo->nm_tracks = varheader->v10.nm_tracks; + songinfo->macro_speedup = UINT16LE(varheader->v10.macro_speedup); + songinfo->flag_4op = varheader->v10.flag_4op; + for (int i = 0; i < 20; i++) + songinfo->lock_flags[i] = varheader->v10.lock_flags[i]; + for (int i = 0; i < 20; i++) + len[i] = UINT32LE(varheader->v10.len[i]); + return sizeof(A2T_VARHEADER_V10); + case 11: + case 12: + case 13: + case 14: + if (sizeof(A2T_VARHEADER_V11) > size) + return INT_MAX; + songinfo->common_flag = varheader->v11.common_flag; + songinfo->patt_len = UINT16LE(varheader->v11.patt_len); + songinfo->nm_tracks = varheader->v11.nm_tracks; + songinfo->macro_speedup = UINT16LE(varheader->v11.macro_speedup); + songinfo->flag_4op = varheader->v11.flag_4op; + for (int i = 0; i < 20; i++) + songinfo->lock_flags[i] = varheader->v10.lock_flags[i]; + for (int i = 0; i < 21; i++) + len[i] = UINT32LE(varheader->v11.len[i]); + return sizeof(A2T_VARHEADER_V11); + } + + return INT_MAX; +} + +void Ca2mv2Player::instrument_import_v1_8(int ins, tINSTR_DATA_V1_8 *instr_s) +{ + tINSTR_DATA *instr_d = get_instr_data(ins); + assert(instr_d); + + instr_d->fm = instr_s->fm; // copy struct + instr_d->panning = instr_s->panning; + instr_d->fine_tune = instr_s->fine_tune; + if (instr_d->panning >= 3) + { + AdPlug_LogWrite("instrument_v1.8 %d, panning out of range\n", ins); + instr_d->panning = 0; + } +} + +void Ca2mv2Player::instrument_import(int ins, tINSTR_DATA *instr_s) +{ + tINSTR_DATA *instr_d = get_instr_data(ins); + assert(instr_d); + + *instr_d = *instr_s; // copy struct + if (instr_d->panning >= 3) + { + AdPlug_LogWrite("instrument %d, panning out of range\n", ins); + instr_d->panning = 0; + } +} + +int Ca2mv2Player::a2t_read_instruments(char *src, unsigned long size) +{ + if (len[0] > size) return INT_MAX; + + int instnum = (ffver < 9 ? 250 : 255); + int instsize = (ffver < 9 ? sizeof(tINSTR_DATA_V1_8) : sizeof(tINSTR_DATA)); + int dstsize = (instnum * instsize) + + (ffver > 11 ? sizeof(tBPM_DATA) + sizeof(tINS_4OP_FLAGS) + sizeof(tRESERVED) : 0); + char *dst = (char *)calloc(1, dstsize); + + a2t_depack(src, len[0], dst, dstsize); + + if (ffver == 14) { + //memcpy(&songinfo->bpm_data, dst, sizeof(songinfo->bpm_data)); + dst += sizeof(tBPM_DATA); + } + + if (ffver >= 12 && ffver <= 14) { + //memcpy(&songinfo->ins_4op_flags, dst, sizeof(songinfo->ins_4op_flags)); + dst += sizeof(tINS_4OP_FLAGS); + //memcpy(&songinfo->reserved_data, dst, sizeof(songinfo->reserved_data)); + dst += sizeof(tRESERVED); + } + + // Calculate the real number of used instruments + int count = instnum; + while (count && is_data_empty(dst + (count - 1) * instsize, instsize)) + count--; + + instruments_allocate(count); + + if (ffver < 9) { + tINSTR_DATA_V1_8 *instr_data = (tINSTR_DATA_V1_8 *)dst; + + for (int i = 0; i < count; i++) { + instrument_import_v1_8(i + 1, &instr_data[i]); + } + } else { + tINSTR_DATA *instr_data = (tINSTR_DATA *)dst; + + for (int i = 0; i < count; i++) { + instrument_import(i + 1, &instr_data[i]); + } + } + + free(dst); + + return len[0]; +} + +int Ca2mv2Player::a2t_read_fmregtable(char *src, unsigned long size) +{ + if (ffver < 9) return 0; + + if (len[1] > size) return INT_MAX; + + tFMREG_TABLE *data = (tFMREG_TABLE *)calloc(255, sizeof(tFMREG_TABLE)); + a2t_depack(src, len[1], (char *)data, 255 * sizeof(tFMREG_TABLE)); + + int count = instrinfo->count; + + // Allocate fmreg macro tables + fmreg_table_allocate(count, data); + + for (int i = 0; i < count; i++) { + // Instrument arpegio/vibrato references + tINSTR_DATA_EXT *dst = get_instr(i + 1); + assert(dst); + dst->arpeggio = data[i].arpeggio_table; + dst->vibrato = data[i].vibrato_table; + } + + free(data); + + return len[1]; +} + +int Ca2mv2Player::a2t_read_arpvibtable(char *src, unsigned long size) +{ + if (ffver < 9) return 0; + + if (len[2] > size) return INT_MAX; + + tARPVIB_TABLE *arpvib_table = (tARPVIB_TABLE *)calloc(255, sizeof(tARPVIB_TABLE)); + a2t_depack(src, len[2], (char *)arpvib_table, 255 * sizeof(tARPVIB_TABLE)); + + arpvib_tables_allocate(255, arpvib_table); + + free(arpvib_table); + + return len[2]; +} + +int Ca2mv2Player::a2t_read_disabled_fmregs(char *src, unsigned long size) +{ + if (ffver < 11) return 0; + + if (len[3] > size) return INT_MAX; + + bool (*dis_fmregs)[255][28] = (bool (*)[255][28])calloc(255, 28); + + a2t_depack(src, len[3], (char *)*dis_fmregs, 255 * 28); + + disabled_fmregs_import(instrinfo->count, *dis_fmregs); + + free(dis_fmregs); + + return len[3]; +} + +int Ca2mv2Player::a2t_read_order(char *src, unsigned long size) +{ + int blocknum[14] = {1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 4, 4, 4, 4}; + int i = blocknum[ffver - 1]; + + if (len[i] > size) return INT_MAX; + + a2t_depack(src, len[i], (char *)songinfo->pattern_order, sizeof (songinfo->pattern_order)); + + return len[i]; +} + +void Ca2mv2Player::convert_v1234_event(tADTRACK2_EVENT_V1234 *ev, int chan) +{ + switch (ev->effect_def) { + case fx_Arpeggio: ev->effect_def = ef_Arpeggio; break; + case fx_FSlideUp: ev->effect_def = ef_FSlideUp; break; + case fx_FSlideDown: ev->effect_def = ef_FSlideDown; break; + case fx_FSlideUpFine: ev->effect_def = ef_FSlideUpFine; break; + case fx_FSlideDownFine: ev->effect_def = ef_FSlideDownFine; break; + case fx_TonePortamento: ev->effect_def = ef_TonePortamento; break; + case fx_TPortamVolSlide: ev->effect_def = ef_TPortamVolSlide; break; + case fx_Vibrato: ev->effect_def = ef_Vibrato; break; + case fx_VibratoVolSlide: ev->effect_def = ef_VibratoVolSlide; break; + case fx_SetInsVolume: ev->effect_def = ef_SetInsVolume; break; + case fx_PatternJump: ev->effect_def = ef_PositionJump; break; + case fx_PatternBreak: ev->effect_def = ef_PatternBreak; break; + case fx_SetTempo: ev->effect_def = ef_SetSpeed; break; + case fx_SetTimer: ev->effect_def = ef_SetTempo; break; + case fx_SetOpIntensity: { + if (ev->effect & 0xf0) { + ev->effect_def = ef_SetCarrierVol; + ev->effect = (ev->effect >> 4) * 4 + 3; + } else if (ev->effect & 0x0f) { + ev->effect_def = ef_SetModulatorVol; + ev->effect = (ev->effect & 0x0f) * 4 + 3; + } else ev->effect_def = 0; + break; + } + case fx_Extended: { + switch (ev->effect >> 4) { + case fx_ex_DefAMdepth: + ev->effect_def = ef_Extended; + ev->effect = ef_ex_SetTremDepth << 4 | (ev->effect & 0x0f); + break; + case fx_ex_DefVibDepth: + ev->effect_def = ef_Extended; + ev->effect = ef_ex_SetVibDepth << 4 | (ev->effect & 0x0f); + break; + case fx_ex_DefWaveform: + ev->effect_def = ef_SetWaveform; + if ((ev->effect & 0x0f) < 4) { + ev->effect = ((ev->effect & 0x0f) << 4) | 0x0f; // 0..3 + } else { + ev->effect = ((ev->effect & 0x0f) - 4) | 0xf0; // 4..7 + } + break; + case fx_ex_VSlideUp: + ev->effect_def = ef_VolSlide; + ev->effect = (ev->effect & 0x0f) << 4; + break; + case fx_ex_VSlideDown: + ev->effect_def = ef_VolSlide; + ev->effect = ev->effect & 0x0f; + break; + case fx_ex_VSlideUpFine: + ev->effect_def = ef_VolSlideFine; + ev->effect = (ev->effect & 0x0f) << 4; + break; + case fx_ex_VSlideDownFine: + ev->effect_def = ef_VolSlideFine; + ev->effect = ev->effect & 0x0f; + break; + case fx_ex_ManSlideUp: + ev->effect_def = ef_Extended2; + ev->effect = (ef_ex2_FineTuneUp << 4) | (ev->effect & 0x0f); + break; + case fx_ex_ManSlideDown: + ev->effect_def = ef_Extended2; + ev->effect = (ef_ex2_FineTuneDown << 4) | (ev->effect & 0x0f); + break; + case fx_ex_RetrigNote: + ev->effect_def = ef_RetrigNote; + ev->effect = (ev->effect & 0x0f) + 1; + break; + case fx_ex_SetAttckRate: + ev->effect_def = ef_Extended; + ev->effect = ev->effect & 0x0f; + if (!adsr_carrier[chan]) { + ev->effect |= ef_ex_SetAttckRateM << 4; + } else { + ev->effect |= ef_ex_SetAttckRateC << 4; + } + break; + case fx_ex_SetDecayRate: + ev->effect_def = ef_Extended; + ev->effect = ev->effect & 0x0f; + if (!adsr_carrier[chan]) { + ev->effect |= ef_ex_SetDecayRateM << 4; + } else { + ev->effect |= ef_ex_SetDecayRateC << 4; + } + break; + case fx_ex_SetSustnLevel: + ev->effect_def = ef_Extended; + ev->effect = ev->effect & 0x0f; + if (!adsr_carrier[chan]) { + ev->effect |= ef_ex_SetSustnLevelM << 4; + } else { + ev->effect |= ef_ex_SetSustnLevelC << 4; + } + break; + case fx_ex_SetReleaseRate: + ev->effect_def = ef_Extended; + ev->effect = ev->effect & 0x0f; + if (!adsr_carrier[chan]) { + ev->effect |= ef_ex_SetRelRateM << 4; + } else { + ev->effect |= ef_ex_SetRelRateC << 4; + } + break; + case fx_ex_SetFeedback: + ev->effect_def = ef_Extended; + ev->effect = (ef_ex_SetFeedback << 4) | (ev->effect & 0x0f); + break; + case fx_ex_ExtendedCmd: + ev->effect_def = ef_Extended; + ev->effect = ef_ex_ExtendedCmd2 << 4; + if ((ev->effect & 0x0f) < 10) { + // FIXME: Should be a parameter + const bool whole_song = false; + + switch (ev->effect & 0x0f) { + case 0: ev->effect |= ef_ex_cmd2_RSS; break; + case 1: ev->effect |= ef_ex_cmd2_LockVol; break; + case 2: ev->effect |= ef_ex_cmd2_UnlockVol; break; + case 3: ev->effect |= ef_ex_cmd2_LockVP; break; + case 4: ev->effect |= ef_ex_cmd2_UnlockVP; break; + case 5: + ev->effect_def = (whole_song ? 255 : 0); + ev->effect = 0; + adsr_carrier[chan] = true; + break; + case 6: + ev->effect_def = (whole_song ? 255 : 0); + ev->effect = (whole_song ? 1 : 0); + adsr_carrier[chan] = false; + break; + case 7: ev->effect |= ef_ex_cmd2_VSlide_car; break; + case 8: ev->effect |= ef_ex_cmd2_VSlide_mod; break; + case 9: ev->effect |= ef_ex_cmd2_VSlide_def; break; + } + } else { + ev->effect_def = 0; + ev->effect = 0; + } + break; + } + break; + } + default: + ev->effect_def = 0; + ev->effect = 0; + } +} + +// common for both a2t/a2m +int Ca2mv2Player::a2_read_patterns(char *src, int s, unsigned long size) +{ + int retval = 0; + switch (ffver) { + case 1: + case 2: + case 3: + case 4: // [4][16][64][9][4] + { + tPATTERN_DATA_V1234 *old = (tPATTERN_DATA_V1234 *)calloc(16, sizeof(*old)); + + memset(adsr_carrier, false, sizeof(adsr_carrier)); + + for (int i = 0; i < 4; i++) { + if (!len[i+s]) continue; + + if (len[i+s] > size) { + free(old); + return INT_MAX; + } + + a2t_depack(src, len[i+s], (char *)old, 16 * sizeof (*old)); + + for (int p = 0; p < 16; p++) { // pattern + if (i * 8 + p >= eventsinfo->patterns) + break; + for (int r = 0; r < 64; r++) // row + for (int c = 0; c < 9; c++) { // channel + tADTRACK2_EVENT_V1234 *src = &old[p].row[r].ch[c].ev; + tADTRACK2_EVENT *dst = get_event_p(i * 16 + p, c, r); + + convert_v1234_event(src, c); + + dst->note = src->note; + dst->instr_def = src->instr_def; + dst->eff[0].def = src->effect_def; + dst->eff[0].val = src->effect; + } + } + + src += len[i+s]; + size -= len[i+s]; + retval += len[i+s]; + } + + free(old); + break; + } + case 5: + case 6: + case 7: + case 8: // [8][8][18][64][4] + { + tPATTERN_DATA_V5678 *old = (tPATTERN_DATA_V5678 *)calloc(8, sizeof(*old)); + + for (int i = 0; i < 8; i++) { + if (!len[i+s]) continue; + + if (len[i+s] > size) { + free(old); + return INT_MAX; + } + + a2t_depack(src, len[i+s], (char *)old, 8 * sizeof (*old)); + + for (int p = 0; p < 8; p++) { // pattern + if (i * 8 + p >= eventsinfo->patterns) + break; + for (int c = 0; c < 18; c++) // channel + for (int r = 0; r < 64; r++) { // row + tADTRACK2_EVENT_V1234 *src = &old[p].ch[c].row[r].ev; + tADTRACK2_EVENT *dst = get_event_p(i * 8 + p, c, r); + + dst->note = src->note; + dst->instr_def = src->instr_def; + dst->eff[0].def = src->effect_def; + dst->eff[0].val = src->effect; + } + } + + src += len[i+s]; + size -= len[i+s]; + retval += len[i+s]; + } + + free(old); + break; + } + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: // [16][8][20][256][6] + { + tPATTERN_DATA *old = (tPATTERN_DATA *)calloc(8, sizeof(*old)); + + // 16 groups of 8 patterns + for (int i = 0; i < 16; i++) { + if (!len[i+s]) continue; + if (len[i+s] > size) { + free(old); + return INT_MAX; + } + a2t_depack(src, len[i+s], (char *)old, 8 * sizeof (*old)); + src += len[i+s]; + size -= len[i+s]; + retval += len[i+s]; + + for (int p = 0; p < 8; p++) { // pattern + if (i * 8 + p >= eventsinfo->patterns) + break; + + for (int c = 0; c < eventsinfo->channels; c++) // channel + for (int r = 0; r < eventsinfo->rows; r++) { // row + tADTRACK2_EVENT *dst = get_event_p(i * 8 + p, c, r); + tADTRACK2_EVENT *src = &old[p].ch[c].row[r].ev; + *dst = *src; // copy struct + } + } + } + + free(old); + break; + } + } + + return retval; +} + +int Ca2mv2Player::a2t_read_patterns(char *src, unsigned long size) +{ + int blockstart[14] = {2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 5, 5, 5, 5}; + int s = blockstart[ffver - 1]; + + return a2_read_patterns(src, s, size); +} + +bool Ca2mv2Player::a2t_import(char *tune, unsigned long size) +{ + A2T_HEADER *header = (A2T_HEADER *)tune; + char *blockptr = tune + sizeof(A2T_HEADER); + int result; + + if (sizeof(A2T_HEADER) > size) + return false; + + if (strncmp(header->id, "_A2tiny_module_", 15)) + return false; + + init_songdata(); + + memset(len, 0, sizeof(len)); + + ffver = header->ffver; + type = 1; + + if (!ffver || ffver > 14) + return false; + + songinfo->tempo = header->tempo; + songinfo->speed = header->speed; + songinfo->patt_len = 64; + songinfo->nm_tracks = 18; + songinfo->macro_speedup = 1; + + // Read variable part after header, fill len[] with values + result = a2t_read_varheader(blockptr, size - (blockptr - tune)); + if (result == INT_MAX) return false; + blockptr += result; + + speed_update = (songinfo->common_flag >> 0) & 1; + lockvol = (songinfo->common_flag >> 1) & 1; + lockVP = (songinfo->common_flag >> 2) & 1; + tremolo_depth = (songinfo->common_flag >> 3) & 1; + vibrato_depth = (songinfo->common_flag >> 4) & 1; + panlock = (songinfo->common_flag >> 5) & 1; + percussion_mode = (songinfo->common_flag >> 6) & 1; + volume_scaling = (songinfo->common_flag >> 7) & 1; + + // Read instruments; all versions + result = a2t_read_instruments(blockptr, size - (blockptr - tune)); + if (result == INT_MAX) return false; + blockptr += result; + + // Read instrument macro (v >= 9,10,11) + result = a2t_read_fmregtable(blockptr, size - (blockptr - tune)); + if (result == INT_MAX) return false; + blockptr += result; + + // Read arpeggio/vibrato macro table (v >= 9,10,11) + result = a2t_read_arpvibtable(blockptr, size - (blockptr - tune)); + if (result == INT_MAX) return false; + blockptr += result; + + // Read disabled fm regs (v == 11) + result = a2t_read_disabled_fmregs(blockptr, size - (blockptr - tune)); + if (result == INT_MAX) return false; + blockptr += result; + + // Read pattern_order + result = a2t_read_order(blockptr, size - (blockptr - tune)); + if (result == INT_MAX) return false; + blockptr += result; + + // Allocate patterns + patterns_allocate(header->npatt, songinfo->nm_tracks, songinfo->patt_len); + + // Read patterns + result = a2t_read_patterns(blockptr, size - (blockptr - tune)); + if (result == INT_MAX) return false; + + return true; +} + +typedef uint8_t (tUINT16)[2]; +typedef uint8_t (tUINT32)[4]; + +int Ca2mv2Player::a2m_read_varheader(char *blockptr, int npatt, unsigned long size) +{ + int lensize; + int maxblock = (ffver < 5 ? npatt / 16 : npatt / 8) + 1; + + tUINT16 *src16 = (tUINT16 *)blockptr; + tUINT32 *src32 = (tUINT32 *)blockptr; + + if (ffver < 5) lensize = 5; // 1,2,3,4 - uint16_t len[5]; + else if (ffver < 9) lensize = 9; // 5,6,7,8 - uint16_t len[9]; + else lensize = 17; // 9,10,11 - uint32_t len[17]; + + if (ffver >= 1 && ffver <= 8) { // 1 - 8 + if (lensize * sizeof(tUINT16) > size) return INT_MAX; + + // skip possible rubbish (MARIO.A2M) + for (int i = 0; (i < lensize) && (i <= maxblock); i++) + len[i] = UINT16LE(src16[i]); + + return lensize * sizeof(tUINT16); + } else if (ffver >= 9 && ffver <= 14) { // 9 - 14 + if (lensize * sizeof(tUINT32) > size) return INT_MAX; + + for (int i = 0; i < lensize; i++) + len[i] = UINT32LE(src32[i]); + + return lensize * sizeof(tUINT32); + } + + return INT_MAX; +} + +int Ca2mv2Player::a2m_read_songdata(char *src, unsigned long size) +{ + if (ffver < 9) { // 1 - 8 + if (len[0] > size) return INT_MAX; + A2M_SONGDATA_V1_8 *data = (A2M_SONGDATA_V1_8 *)calloc(1, sizeof(*data)); + a2t_depack(src, len[0], (char *)data, sizeof (*data)); + + memcpy(songinfo->songname, data->songname + 1, 42); + memcpy(songinfo->composer, data->composer + 1, 42); + + // Calculate the real number of used instruments + int count = 250; + while (count && is_data_empty((char *)&data->instr_data[count - 1], sizeof(tINSTR_DATA_V1_8))) + count--; + + instruments_allocate(count); + + for (int i = 0; i < 250; i++) + memcpy(songinfo->instr_names[i], data->instr_names[i] + 1, 32); + + for (int i = 0; i < count; i++) { + instrument_import_v1_8(i + 1, &data->instr_data[i]); + } + + memcpy(songinfo->pattern_order, data->pattern_order, 128); + + songinfo->tempo = data->tempo; + songinfo->speed = data->speed; + + if (ffver > 4) { // 5 - 8 + songinfo->common_flag = data->common_flag; + } + + free(data); + } else { // 9 - 14 + if (len[0] > size) return INT_MAX; + A2M_SONGDATA_V9_14 *data = (A2M_SONGDATA_V9_14 *)calloc(1, sizeof(*data)); + a2t_depack(src, len[0], (char *)data, sizeof (*data)); + + memcpy(songinfo->songname, data->songname + 1, 42); + memcpy(songinfo->composer, data->composer + 1, 42); + + // Calculate the real number of used instruments + int count = 255; + while (count && is_data_empty((char *)&data->instr_data[count - 1], sizeof(tINSTR_DATA))) + count--; + + instruments_allocate(count); + + for (int i = 0; i < 255; i++) + memcpy(songinfo->instr_names[i], data->instr_names[i] + 1, 42); + + for (int i = 0; i < count; i++) { + instrument_import(i + 1, &data->instr_data[i]); + + // Instrument arpegio/vibrato references + tINSTR_DATA_EXT *dst = get_instr(i + 1); + assert(dst); + dst->arpeggio = data->fmreg_table[i].arpeggio_table; + dst->vibrato = data->fmreg_table[i].vibrato_table; + } + + // Allocate fmreg macro tables + fmreg_table_allocate(count, data->fmreg_table); + + // Allocate arpeggio/vibrato macro tables + arpvib_tables_allocate(255, data->arpvib_table); + + memcpy(songinfo->pattern_order, data->pattern_order, 128); + + songinfo->tempo = data->tempo; + songinfo->speed = data->speed; + songinfo->common_flag = data->common_flag; + songinfo->patt_len = UINT16LE(data->patt_len); + songinfo->nm_tracks = data->nm_tracks; + songinfo->macro_speedup = UINT16LE(data->macro_speedup); + + // v10 + songinfo->flag_4op = data->flag_4op; + memcpy(songinfo->lock_flags, data->lock_flags, sizeof(data->lock_flags)); + + // v11 + // NOTE: not used anywhere + //memcpy(songinfo->pattern_names, data->pattern_names, 128 * 43); + disabled_fmregs_import(count, (bool (*)[28])data->dis_fmreg_col); + + // v12-13 + // NOTE: not used anywhere + //songinfo->ins_4op_flags.num_4op = data->ins_4op_flags.num_4op; + //memcpy(songinfo->ins_4op_flags.idx_4op, data->ins_4op_flags.idx_4op, 128); + //memcpy(songinfo->reserved_data, data->reserved_data, 1024); + + // v14 + // NOTE: not used anywhere + //songinfo->bpm_data.rows_per_beat = data->bpm_data.rows_per_beat; + //songinfo->bpm_data.tempo_finetune = INT16LE(data->bpm_data.tempo_finetune); + + free(data); + } + + speed_update = (songinfo->common_flag >> 0) & 1; + lockvol = (songinfo->common_flag >> 1) & 1; + lockVP = (songinfo->common_flag >> 2) & 1; + tremolo_depth = (songinfo->common_flag >> 3) & 1; + vibrato_depth = (songinfo->common_flag >> 4) & 1; + panlock = (songinfo->common_flag >> 5) & 1; + percussion_mode = (songinfo->common_flag >> 6) & 1; + volume_scaling = (songinfo->common_flag >> 7) & 1; + + return len[0]; +} + +int Ca2mv2Player::a2m_read_patterns(char *src, unsigned long size) +{ + return a2_read_patterns(src, 1, size); +} + +bool Ca2mv2Player::a2m_import(char *tune, unsigned long size) +{ + A2M_HEADER *header = (A2M_HEADER *)tune; + char *blockptr = tune + sizeof(A2M_HEADER); + int result; + + if (sizeof(A2M_HEADER) > size) + return false; + + if (strncmp(header->id, "_A2module_", 10)) + return false; + + memset(songinfo, 0, sizeof(*songinfo)); + + memset(len, 0, sizeof(len)); + + ffver = header->ffver; + type = 0; + + if (!ffver || ffver > 14) + return false; + + songinfo->patt_len = 64; + songinfo->nm_tracks = 18; + songinfo->macro_speedup = 1; + + // Read variable part after header, fill len[] with values + result = a2m_read_varheader(blockptr, header->npatt, size - (blockptr - tune)); + if (result == INT_MAX) return false; + blockptr += result; + + // Read songdata + result = a2m_read_songdata(blockptr, size - (blockptr - tune)); + if (result == INT_MAX) return false; + blockptr += result; + + // Allocate patterns + patterns_allocate(header->npatt, songinfo->nm_tracks, songinfo->patt_len); + + // Read patterns + result = a2m_read_patterns(blockptr, size - (blockptr - tune)); + if (result == INT_MAX) return false; + + return true; +} + +bool Ca2mv2Player::a2_import(char *tune, unsigned long size) +{ + if ((size > 10) && !strncmp(tune, "_A2module_", 10)) { + return a2m_import(tune, size); + } + + if ((size > 15) && !strncmp(tune, "_A2tiny_module_", 15)) { + return a2t_import(tune, size); + } + + return false; +} diff --git a/plugins/adplug/adplug/a2m-v2.h b/plugins/adplug/adplug/a2m-v2.h new file mode 100644 index 0000000000..c95bfe991a --- /dev/null +++ b/plugins/adplug/adplug/a2m-v2.h @@ -0,0 +1,868 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2008 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * a2m-v2.cpp - Adlib Tracker II Player by Dmitry Smagin + * Originally by Stanislav Baranec + * + * NOTES: + * This player loads a2m and a2t modules versions 1 - 14. + * The code is adapted directly from FreePascal sources of the Adlib Tracker II + * + * REFERENCES: + * https://github.com/ijsf/at2 + * http://www.adlibtracker.net/ + * + */ + +#ifndef H_ADPLUG_A2MPLAYER +#define H_ADPLUG_A2MPLAYER + +#include +#include +#include +#include +#include +#include + +#include "player.h" +#include "depack.h" +#include "sixdepack.h" +#include "unlzh.h" +#include "unlzss.h" +#include "unlzw.h" + +/* https://github.com/wc-duck/dbgtools/blob/master/include/dbgtools/static_assert.h */ + +#define STATIC_ASSERT(cond, msg) +#undef STATIC_ASSERT + +// ... clang ... +#if defined( __clang__ ) + #if defined( __cplusplus ) && __has_feature(cxx_static_assert) + #define STATIC_ASSERT(cond, msg) static_assert(cond, msg) + #elif __has_feature(c_static_assert) + #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) + #endif + +// ... msvc ... +#elif defined(_MSC_VER) && ( defined(_MSC_VER) && (_MSC_VER >= 1600) ) + #define STATIC_ASSERT(cond, msg) static_assert(cond, msg) + +// ... gcc ... +#elif defined(__cplusplus) + #if __cplusplus >= 201103L || (defined(_MSC_VER) && (_MSC_VER >= 1600)) + #define STATIC_ASSERT(cond, msg) static_assert(cond, msg) + #endif +#elif defined( __STDC__ ) + #if defined(__STDC_VERSION__) + #if __STDC_VERSION__ >= 201112L + #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) + #else + #define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) + #endif + #endif +#endif + +/* if we couldn't detect a builtin static assert, lets define one! */ +#ifndef STATIC_ASSERT + #define STATIC_ASSERT(cond, msg) typedef char __STATIC_ASSERT__[(cond) ? 1 : -1] +#endif + + +// Macros for extracting little-endian integers from filedata +#define INT16LE(A) (int16_t)((A[0]) | (A[1] << 8)) +#define UINT16LE(A) (uint16_t)((A[0]) | (A[1] << 8)) +#define INT32LE(A) (int32_t)((A[0]) | (A[1] << 8) | (A[2] << 16) | (A[3] << 24)) +#define UINT32LE(A) (uint32_t)((A[0]) | (A[1] << 8) | (A[2] << 16) | (A[3] << 24)) + +#define keyoff_flag 0x80 +#define fixed_note_flag 0x90 +#define pattern_loop_flag 0xe0 +#define pattern_break_flag 0xf0 + +typedef enum { + isPlaying = 0, isPaused, isStopped +} tPLAY_STATUS; + +#define BYTE_NULL (uint8_t)(0xFFFFFFFF) + +#define MIN_IRQ_FREQ 50 +#define MAX_IRQ_FREQ 1000 + +/* + When loading A2T/A2M, FreePascal structures (no padding and little-endian) should be emulated, + because AdlibTracker 2 was saving structures directly from memory into the file. + + That's why: + 1) only chars are used in structs to avoid any padding or alignment (default C/C++ behaviour) + 2) ints and longs are represented as arrays of chars, little-endian order is implied + 3) static_assert is used to make sure structs have the correct size +*/ + +typedef struct { + union { + struct { + uint8_t multipM: 4, ksrM: 1, sustM: 1, vibrM: 1, tremM : 1; + uint8_t multipC: 4, ksrC: 1, sustC: 1, vibrC: 1, tremC : 1; + uint8_t volM: 6, kslM: 2; + uint8_t volC: 6, kslC: 2; + uint8_t decM: 4, attckM: 4; + uint8_t decC: 4, attckC: 4; + uint8_t relM: 4, sustnM: 4; + uint8_t relC: 4, sustnC: 4; + uint8_t wformM: 3, : 5; + uint8_t wformC: 3, : 5; + uint8_t connect: 1, feedb: 3, : 4; // panning is not used here + }; + uint8_t data[11]; + }; +} tFM_INST_DATA; + +STATIC_ASSERT(sizeof(tFM_INST_DATA) == 11, "sizeof(tFM_INST_DATA) != 11"); + +typedef struct { + tFM_INST_DATA fm; + uint8_t panning; + int8_t fine_tune; + uint8_t perc_voice; +} tINSTR_DATA; + +STATIC_ASSERT(sizeof(tINSTR_DATA) == 14, "sizeof(tINSTR_DATA) != 14"); + +typedef struct { + uint8_t length; + uint8_t speed; + uint8_t loop_begin; + uint8_t loop_length; + uint8_t keyoff_pos; + uint8_t data[255]; +} tARPEGGIO_TABLE; + +typedef struct { + uint8_t length; + uint8_t speed; + uint8_t delay; + uint8_t loop_begin; + uint8_t loop_length; + uint8_t keyoff_pos; + int8_t data[255]; // array[1..255] of Shortint; +} tVIBRATO_TABLE; + +typedef struct { + tFM_INST_DATA fm; + uint8_t freq_slide[2]; // int16_t + uint8_t panning; + uint8_t duration; +} tREGISTER_TABLE_DEF; + +typedef struct { + uint8_t length; + uint8_t loop_begin; + uint8_t loop_length; + uint8_t keyoff_pos; + uint8_t arpeggio_table; + uint8_t vibrato_table; + tREGISTER_TABLE_DEF data[255]; +} tFMREG_TABLE; + +typedef struct { + tARPEGGIO_TABLE arpeggio; + tVIBRATO_TABLE vibrato; +} tARPVIB_TABLE; + +STATIC_ASSERT(sizeof(tFMREG_TABLE) == 3831, "sizeof(tFMREG_TABLE) != 3831"); +STATIC_ASSERT(sizeof(tARPVIB_TABLE) == 521, "sizeof(tARPVIB_TABLE) != 521"); + +typedef struct { + uint8_t note; + uint8_t instr_def; // TODO: rename to 'ins' + struct { + uint8_t def; + uint8_t val; + } eff[2]; +} tADTRACK2_EVENT; + +STATIC_ASSERT(sizeof(tADTRACK2_EVENT) == 6, "sizeof(tADTRACK2_EVENT) != 6"); + +typedef struct { + struct { + struct { + tADTRACK2_EVENT ev; + } row[256]; + } ch[20]; +} tPATTERN_DATA; + +STATIC_ASSERT(sizeof(tPATTERN_DATA) == 20 * 256 * 6, "sizeof(tPATTERN_DATA) != 30720"); + +#define ef_Arpeggio 0 +#define ef_FSlideUp 1 +#define ef_FSlideDown 2 +#define ef_TonePortamento 3 +#define ef_Vibrato 4 +#define ef_TPortamVolSlide 5 +#define ef_VibratoVolSlide 6 +#define ef_FSlideUpFine 7 +#define ef_FSlideDownFine 8 +#define ef_SetModulatorVol 9 +#define ef_VolSlide 10 +#define ef_PositionJump 11 +#define ef_SetInsVolume 12 +#define ef_PatternBreak 13 +#define ef_SetTempo 14 +#define ef_SetSpeed 15 +#define ef_TPortamVSlideFine 16 +#define ef_VibratoVSlideFine 17 +#define ef_SetCarrierVol 18 +#define ef_SetWaveform 19 +#define ef_VolSlideFine 20 +#define ef_RetrigNote 21 +#define ef_Tremolo 22 +#define ef_Tremor 23 +#define ef_ArpggVSlide 24 +#define ef_ArpggVSlideFine 25 +#define ef_MultiRetrigNote 26 +#define ef_FSlideUpVSlide 27 +#define ef_FSlideDownVSlide 28 +#define ef_FSlUpFineVSlide 29 +#define ef_FSlDownFineVSlide 30 +#define ef_FSlUpVSlF 31 +#define ef_FSlDownVSlF 32 +#define ef_FSlUpFineVSlF 33 +#define ef_FSlDownFineVSlF 34 +#define ef_Extended 35 +#define ef_Extended2 36 +#define ef_SetGlobalVolume 37 +#define ef_SwapArpeggio 38 +#define ef_SwapVibrato 39 +#define ef_ForceInsVolume 40 +#define ef_Extended3 41 +#define ef_ExtraFineArpeggio 42 +#define ef_ExtraFineVibrato 43 +#define ef_ExtraFineTremolo 44 +#define ef_SetCustomSpeedTab 45 +#define ef_GlobalFSlideUp 46 +#define ef_GlobalFSlideDown 47 +#define ef_GlobalFreqSlideUpXF 48 // ef_fix2 replacement for >xx + ZFE +#define ef_GlobalFreqSlideDnXF 49 // ef_fix2 replacement for songname); } + std::string getauthor() { return std::string(songinfo->composer); } + unsigned int getinstruments() { return instrinfo->count; } + std::string getinstrument(unsigned int n) { return std::string(n < instrinfo->count ? songinfo->instr_names[n] : ""); } + +private: + uint8_t current_order = 0; + uint8_t current_pattern = 0; + uint8_t current_line = 0; // TODO: rename to current_row + + uint8_t tempo = 50; + uint8_t speed = 6; + + uint16_t macro_speedup = 1; + bool irq_mode = false; + + int16_t IRQ_freq = 50; + int IRQ_freq_shift = 0; + bool irq_initialized = false; + bool timer_fix = true; + + bool pattern_break = false; + bool pattern_delay = false; + uint8_t next_line = 0; + + int playback_speed_shift = 0; + tPLAY_STATUS play_status = isStopped; + uint8_t overall_volume = 63; + uint8_t global_volume = 63; + + const uint8_t def_vibtrem_speed_factor = 1; + const uint8_t def_vibtrem_table_size = 32; + + uint8_t vibtrem_speed_factor; + uint8_t vibtrem_table_size; + uint8_t vibtrem_table[256]; + + uint8_t misc_register; + + uint8_t current_tremolo_depth = 0; + uint8_t current_vibrato_depth = 0; + + bool speed_update, lockvol, panlock, lockVP; + uint8_t tremolo_depth, vibrato_depth; + bool volume_scaling, percussion_mode; + uint8_t last_order; + + bool editor_mode = false; // true to allocate max resources + + tSONGINFO *songinfo; + tINSTR_INFO *instrinfo; + unsigned int arpvib_count; + tVIBRATO_TABLE **vibrato_table = 0; + tARPEGGIO_TABLE **arpeggio_table = 0; + tEVENTS_INFO *eventsinfo; + tCHDATA *ch; + + // Timer + int ticks, tickD, tickXF; + int ticklooper, macro_ticklooper; + + // Loader + int type = 0; // 0 - a2m, 1 - a2t + int ffver = 1; + unsigned int len[21]; + bool adsr_carrier[9]; // For importing from a2m v1234 + + bool songend = false; + int chip = 0; + + // Helpers for instruments + void instruments_free(); + void instruments_allocate(size_t number); + tINSTR_DATA_EXT *get_instr(uint8_t ins); + int8_t get_instr_fine_tune(uint8_t ins); + tINSTR_DATA *get_instr_data_by_ch(int chan); + tINSTR_DATA *get_instr_data(uint8_t ins); + + // Helpers for macro tables + void fmreg_table_allocate(size_t n, tFMREG_TABLE rt[]); + void disabled_fmregs_import(size_t n, bool dis_fmregs[255][28]); + void arpvib_tables_free(); + void arpvib_tables_allocate(size_t n, tARPVIB_TABLE mt[]); + tARPEGGIO_TABLE *get_arpeggio_table(uint8_t arp_table); + tVIBRATO_TABLE *get_vibrato_table(uint8_t vib_table); + tFMREG_TABLE *get_fmreg_table(uint8_t fmreg_ins); + + // Helpers for patterns + tADTRACK2_EVENT *get_event_p(int pattern, int channel, int row); + void patterns_free(); + void patterns_allocate(int patterns, int channels, int rows); + + uint16_t regoffs_n(int chan); + uint16_t regoffs_m(int chan); + uint16_t regoffs_c(int chan); + bool is_4op_chan_hi(int chan); + bool is_4op_chan_lo(int chan); + void opl2out(uint16_t reg, uint16_t data); + void opl3out(uint16_t reg, uint8_t data); + void opl3exp(uint16_t data); + void change_freq(int chan, uint16_t freq); + bool is_chan_adsr_data_empty(int chan); + bool is_ins_adsr_data_empty(int ins); + void change_frequency(int chan, uint16_t freq); + uint16_t _macro_speedup(); + void set_clock_rate(uint8_t clock_rate); + void update_timer(int Hz); + void update_playback_speed(int speed_shift); + void key_on(int chan); + void key_off(int chan); + void release_sustaining_sound(int chan); + + t4OP_DATA get_4op_data(uint8_t chan); + bool _4op_vol_valid_chan(int chan); + void set_ins_volume(uint8_t modulator, uint8_t carrier, uint8_t chan); + void set_volume(uint8_t modulator, uint8_t carrier, uint8_t chan); + void set_ins_volume_4op(uint8_t volume, uint8_t chan); + void reset_ins_volume(int chan); + void set_global_volume(); + void set_overall_volume(unsigned char level); + + void init_macro_table(int chan, uint8_t note, uint8_t ins, uint16_t freq); + void set_ins_data(uint8_t ins, int chan); + void update_modulator_adsrw(int chan); + void update_carrier_adsrw(int chan); + void update_fmpar(int chan); + bool is_4op_chan(int chan); + void output_note(uint8_t note, uint8_t ins, int chan, bool restart_macro, bool restart_adsr); + bool no_loop(uint8_t current_chan, uint8_t current_line); + void update_effect_table(int slot, int chan, int eff_group, uint8_t def, uint8_t val); + void process_effects(tADTRACK2_EVENT *event, int slot, int chan); + void new_process_note(tADTRACK2_EVENT *event, int chan); + void play_line(); + void generate_custom_vibrato(uint8_t value); + void check_swap_arp_vibr(tADTRACK2_EVENT *event, int slot, int chan); + void portamento_up(int chan, uint16_t slide, uint16_t limit); + void portamento_down(int chan, uint16_t slide, uint16_t limit); + void macro_vibrato__porta_up(int chan, uint8_t depth); + void macro_vibrato__porta_down(int chan, uint8_t depth); + void tone_portamento(int slot, int chan); + void slide_carrier_volume_up(uint8_t chan, uint8_t slide, uint8_t limit); + void slide_modulator_volume_up(uint8_t chan, uint8_t slide, uint8_t limit); + void slide_volume_up(int chan, uint8_t slide); + void slide_carrier_volume_down(uint8_t chan, uint8_t slide); + void slide_modulator_volume_down(uint8_t chan, uint8_t slide); + void slide_volume_down(int chan, uint8_t slide); + void volume_slide(int chan, uint8_t up_speed, uint8_t down_speed); + void global_volume_slide(uint8_t up_speed, uint8_t down_speed); + void arpeggio(int slot, int chan); + void vibrato(int slot, int chan); + void tremolo(int slot, int chan); + int chanvol(int chan); + void update_effects_slot(int slot, int chan); + void update_effects(); + void update_fine_effects(int slot, int chan); + void update_extra_fine_effects_slot(int slot, int chan); + void update_extra_fine_effects(); + + void set_current_order(uint8_t new_order); + int calc_following_order(uint8_t order); + int calc_order_jump(); + void update_song_position(); + void poll_proc(); + void macro_poll_proc(); + void newtimer(); + void init_irq(); + void done_irq(); + + void init_buffers(); + void init_player(); + bool a2t_play(char *tune, unsigned long size); + void a2t_stop(); + void init_songdata(); + + // Loader + void a2t_depack(char *src, int srcsize, char *dst, int dstsize); + int a2t_read_varheader(char *blockptr, unsigned long size); + void instrument_import_v1_8(int ins, tINSTR_DATA_V1_8 *instr_s); + void instrument_import(int ins, tINSTR_DATA *instr_s); + int a2t_read_instruments(char *src, unsigned long size); + int a2t_read_fmregtable(char *src, unsigned long size); + int a2t_read_arpvibtable(char *src, unsigned long size); + int a2t_read_disabled_fmregs(char *src, unsigned long size); + int a2t_read_order(char *src, unsigned long size); + void convert_v1234_event(tADTRACK2_EVENT_V1234 *ev, int chan); + int a2_read_patterns(char *src, int s, unsigned long size); + int a2t_read_patterns(char *src, unsigned long size); + bool a2t_import(char *tune, unsigned long size); + + int a2m_read_varheader(char *blockptr, int npatt, unsigned long size); + int a2m_read_songdata(char *src, unsigned long size); + int a2m_read_patterns(char *src, unsigned long size); + bool a2m_import(char *tune, unsigned long size); + bool a2_import(char *tune, unsigned long size); +}; + +#endif diff --git a/plugins/adplug/adplug/a2m.cpp b/plugins/adplug/adplug/a2m.cpp index 907a448b9e..407d593555 100644 --- a/plugins/adplug/adplug/a2m.cpp +++ b/plugins/adplug/adplug/a2m.cpp @@ -30,29 +30,14 @@ */ #include +#include #include "a2m.h" -const unsigned int Ca2mLoader::MAXFREQ = 2000, -Ca2mLoader::MINCOPY = ADPLUG_A2M_MINCOPY, -Ca2mLoader::MAXCOPY = ADPLUG_A2M_MAXCOPY, -Ca2mLoader::COPYRANGES = ADPLUG_A2M_COPYRANGES, -Ca2mLoader::CODESPERRANGE = ADPLUG_A2M_CODESPERRANGE, -Ca2mLoader::TERMINATE = 256, -Ca2mLoader::FIRSTCODE = ADPLUG_A2M_FIRSTCODE, -Ca2mLoader::MAXCHAR = FIRSTCODE + COPYRANGES * CODESPERRANGE - 1, -Ca2mLoader::SUCCMAX = MAXCHAR + 1, -Ca2mLoader::TWICEMAX = ADPLUG_A2M_TWICEMAX, -Ca2mLoader::ROOT = 1, Ca2mLoader::MAXBUF = 42 * 1024, -Ca2mLoader::MAXDISTANCE = 21389, Ca2mLoader::MAXSIZE = 21389 + MAXCOPY; - -const unsigned short Ca2mLoader::bitvalue[14] = - {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192}; - -const signed short Ca2mLoader::copybits[COPYRANGES] = - {4, 6, 8, 10, 12, 14}; - -const signed short Ca2mLoader::copymin[COPYRANGES] = - {0, 16, 80, 336, 1360, 5456}; +// Limit length byte not to exceed size of the string +#define fixstringlength(s) __fixlength((s)[0], sizeof(s) - 1); +static void __fixlength(char &len, size_t max) { + if ((unsigned char)len > max) len = (char)max; +} CPlayer *Ca2mLoader::factory(Copl *newopl) { @@ -61,186 +46,228 @@ CPlayer *Ca2mLoader::factory(Copl *newopl) bool Ca2mLoader::load(const std::string &filename, const CFileProvider &fp) { - binistream *f = fp.open(filename); if(!f) return false; - char id[10]; - int i,j,k,t; + binistream *f = fp.open(filename); if (!f) return false; + unsigned int i; + int j, k, t; unsigned int l; - unsigned char *org = NULL, *orgptr, flags = 0, numpats, version; - unsigned long crc, alength; + unsigned char *org, *orgptr, flags = 0; + unsigned long alength; unsigned short len[9], *secdata, *secptr; - const unsigned char convfx[16] = {0,1,2,23,24,3,5,4,6,9,17,13,11,19,7,14}; - const unsigned char convinf1[16] = {0,1,2,6,7,8,9,4,5,3,10,11,12,13,14,15}; - const unsigned char newconvfx[] = {0,1,2,3,4,5,6,23,24,21,10,11,17,13,7,19, - 255,255,22,25,255,15,255,255,255,255,255, - 255,255,255,255,255,255,255,255,14,255}; + static const unsigned char convfx[16] = { + 0, 1, 2, 23, 24, 3, 5, 4, 6, 9, 17, 13, 11, 19, 7, 14 + }; + static const unsigned char convinf1[16] = { + 0, 1, 2, 6, 7, 8, 9, 4, 5, 3, 10, 11, 12, 13, 14, 15 + }; + static const unsigned char newconvfx[] = { + 0, 1, 2, 3, 4, 5, 6, 23, 24, 21, 10, 11, 17, 13, 7, 19, + 255, 255, 22, 25, 255, 15, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 14, 255 + }; // read header - f->readString(id, 10); crc = f->readInt(4); - version = f->readInt(1); numpats = f->readInt(1); + char id[10]; + f->readString(id, sizeof(id)); + /*unsigned long crc = */ f->readInt(4); + unsigned char version = f->readInt(1); + unsigned char numpats = f->readInt(1); // file validation section - if(strncmp(id,"_A2module_",10) || (version != 1 && version != 5 && - version != 4 && version != 8)) { + if (memcmp(id, "_A2module_", sizeof(id)) || + (version != 1 && version != 5 && version != 4 && version != 8) || + numpats < 1 || numpats > 64) { fp.close(f); return false; } + nop = numpats; + length = 128; + restartpos = 0; + // load, depack & convert section - nop = numpats; length = 128; restartpos = 0; - if(version < 5) { - for(i=0;i<5;i++) len[i] = f->readInt(2); + if (version < 5) { + for (i = 0; i < 5; i++) len[i] = f->readInt(2); t = 9; } else { // version >= 5 - for(i=0;i<9;i++) len[i] = f->readInt(2); + for (i = 0; i < 9; i++) len[i] = f->readInt(2); t = 18; } // block 0 - secdata = new unsigned short [len[0] / 2]; - if(version == 1 || version == 5) { - for(i=0;ireadInt(2); - org = new unsigned char [MAXBUF]; orgptr = org; - sixdepak(secdata,org,len[0]); + size_t needed = sizeof(songname) + sizeof(author) + sizeof(instname) + + NUMINST * INSTDATASIZE + length + 2 + (version >= 5); + if (version == 1 || version == 5) { + // needed bytes are used, so don't allocate or decode more than that. + orgptr = org = new unsigned char [needed]; + secdata = new unsigned short [len[0] / 2]; + + for (i = 0; i < len[0] / 2; i++) secdata[i] = f->readInt(2); + + // What if len[0] is odd: ignore, skip extra byte, or fail? + l = Sixdepak::decode(secdata, len[0], org, needed); + + delete [] secdata; } else { - orgptr = (unsigned char *)secdata; - for(i=0;ireadInt(1); + orgptr = org = new unsigned char [len[0]]; + + for(l = 0; l < len[0]; l++) orgptr[l] = f->readInt(1); } - memcpy(songname,orgptr,43); orgptr += 43; - memcpy(author,orgptr,43); orgptr += 43; - memcpy(instname,orgptr,250*33); orgptr += 250*33; - - for(i=0;i<250;i++) { // instruments - inst[i].data[0] = *(orgptr+i*13+10); - inst[i].data[1] = *(orgptr+i*13); - inst[i].data[2] = *(orgptr+i*13+1); - inst[i].data[3] = *(orgptr+i*13+4); - inst[i].data[4] = *(orgptr+i*13+5); - inst[i].data[5] = *(orgptr+i*13+6); - inst[i].data[6] = *(orgptr+i*13+7); - inst[i].data[7] = *(orgptr+i*13+8); - inst[i].data[8] = *(orgptr+i*13+9); - inst[i].data[9] = *(orgptr+i*13+2); - inst[i].data[10] = *(orgptr+i*13+3); - - if(version < 5) - inst[i].misc = *(orgptr+i*13+11); + if (l < needed) { + // Block is too short; fail. + delete [] org; + fp.close(f); + return false; + } + + memcpy(songname, orgptr, sizeof(songname)); + orgptr += sizeof(songname); + fixstringlength(songname); + memcpy(author, orgptr, sizeof(author)); + orgptr += sizeof(author); + fixstringlength(author); + memcpy(instname, orgptr, sizeof(instname)); + orgptr += sizeof(instname); + + for (i = 0; i < NUMINST; i++) { // instrument data + fixstringlength(instname[i]); + + inst[i].data[0] = orgptr[i * INSTDATASIZE + 10]; + inst[i].data[1] = orgptr[i * INSTDATASIZE + 0]; + inst[i].data[2] = orgptr[i * INSTDATASIZE + 1]; + inst[i].data[3] = orgptr[i * INSTDATASIZE + 4]; + inst[i].data[4] = orgptr[i * INSTDATASIZE + 5]; + inst[i].data[5] = orgptr[i * INSTDATASIZE + 6]; + inst[i].data[6] = orgptr[i * INSTDATASIZE + 7]; + inst[i].data[7] = orgptr[i * INSTDATASIZE + 8]; + inst[i].data[8] = orgptr[i * INSTDATASIZE + 9]; + inst[i].data[9] = orgptr[i * INSTDATASIZE + 2]; + inst[i].data[10] = orgptr[i * INSTDATASIZE + 3]; + + if (version < 5) + inst[i].misc = orgptr[i * INSTDATASIZE + 11]; else { // version >= 5 -> OPL3 format - int pan = *(orgptr+i*13+11); + int pan = orgptr[i * INSTDATASIZE + 11]; - if(pan) + if (pan) inst[i].data[0] |= (pan & 3) << 4; // set pan else - inst[i].data[0] |= 48; // enable both speakers + inst[i].data[0] |= 0x30; // enable both speakers } - inst[i].slide = *(orgptr+i*13+12); + inst[i].slide = orgptr[i * INSTDATASIZE + 12]; } + orgptr += NUMINST * INSTDATASIZE; + + memcpy(order, orgptr, length); + orgptr += length; + for (i = 0; i < length; i++) + if ((order[i] & 0x7f) >= numpats) { // invalid pattern in order list + // What to do in this case in not defined in documentation? There are songs that contain this fault. + // We have two options: + // 1. allocate an empty pattern and redirect to this - more work and lots of quirks + // 2. adjust order list to point to pattern 0 / jump to order 0 instead + order[i] &= 0x80; // make it point to pattern 0 / jump to order 0 + } + + bpm = *orgptr++; + initspeed = *orgptr++; + if (version >= 5) flags = *orgptr; - orgptr += 250*13; - memcpy(order,orgptr,128); orgptr += 128; - bpm = *orgptr; orgptr++; - initspeed = *orgptr; orgptr++; - if(version >= 5) flags = *orgptr; - if(version == 1 || version == 5) delete [] org; - delete [] secdata; + delete [] org; // blocks 1-4 or 1-8 + unsigned char ppb = version < 5 ? 16 : 8; // patterns per block + unsigned char blocks = (numpats + ppb - 1) / ppb; // excluding block 0 alength = len[1]; - for(i = 0; i < (version < 5 ? numpats / 16 : numpats / 8); i++) - alength += len[i+2]; + for (i = 2; i <= blocks; i++) + alength += len[i]; + + needed = numpats * 64 * t * 4; + if (version == 1 || version == 5) { + org = new unsigned char [needed]; + secdata = new unsigned short [alength / 2]; + + for (l = 0; l < alength / 2; l++) + secdata[l] = f->readInt(2); - secdata = new unsigned short [alength / 2]; - if(version == 1 || version == 5) { - for(l=0;lreadInt(2); - org = new unsigned char [MAXBUF * (numpats / (version == 1 ? 16 : 8) + 1)]; orgptr = org; secptr = secdata; - orgptr += sixdepak(secptr,orgptr,len[1]); secptr += len[1] / 2; - if(version == 1) { - if(numpats > 16) - orgptr += sixdepak(secptr,orgptr,len[2]); secptr += len[2] / 2; - if(numpats > 32) - orgptr += sixdepak(secptr,orgptr,len[3]); secptr += len[3] / 2; - if(numpats > 48) - sixdepak(secptr,orgptr,len[4]); - } else { - if(numpats > 8) - orgptr += sixdepak(secptr,orgptr,len[2]); secptr += len[2] / 2; - if(numpats > 16) - orgptr += sixdepak(secptr,orgptr,len[3]); secptr += len[3] / 2; - if(numpats > 24) - orgptr += sixdepak(secptr,orgptr,len[4]); secptr += len[4] / 2; - if(numpats > 32) - orgptr += sixdepak(secptr,orgptr,len[5]); secptr += len[5] / 2; - if(numpats > 40) - orgptr += sixdepak(secptr,orgptr,len[6]); secptr += len[6] / 2; - if(numpats > 48) - orgptr += sixdepak(secptr,orgptr,len[7]); secptr += len[7] / 2; - if(numpats > 56) - sixdepak(secptr,orgptr,len[8]); + for (i = 1; i <= blocks; i++) { + orgptr += Sixdepak::decode(secptr, len[i], orgptr, org + needed - orgptr); + secptr += len[i] / 2; } delete [] secdata; } else { - org = (unsigned char *)secdata; - for(l=0;lreadInt(1); + org = new unsigned char [alength]; + f->readString((char *)org, alength); + orgptr = org + alength; } - if(version < 5) { - for(i=0;inote = o[0] == 255 ? 127 : o[0]; - track->inst = o[1]; - track->command = convfx[o[2]]; + track->inst = o[1] <= NUMINST ? o[1] : 0; // ignore invalid instrument + track->command = o[2] < sizeof(convfx) ? convfx[o[2]] : 255; track->param2 = o[3] & 0x0f; - if(track->command != 14) - track->param1 = o[3] >> 4; - else { - track->param1 = convinf1[o[3] >> 4]; - if(track->param1 == 15 && !track->param2) { // convert key-off - track->command = 8; - track->param1 = 0; - track->param2 = 0; - } - } - if(track->command == 14) { + track->param1 = o[3] >> 4; + + if (track->command == 14) { + track->param1 = convinf1[track->param1]; switch(track->param1) { case 2: // convert define waveform track->command = 25; track->param1 = track->param2; track->param2 = 0xf; break; + case 8: // convert volume slide up track->command = 26; track->param1 = track->param2; track->param2 = 0; break; + case 9: // convert volume slide down track->command = 26; track->param1 = 0; break; + + case 15: // convert key-off + if(!track->param2) { + track->command = 8; + track->param1 = 0; + // param2 is already zero + } + break; } } } } else { // version >= 5 - realloc_patterns(64, 64, 18); + realloc_patterns(numpats, 64, t); - for(i=0;inote = o[0] == 255 ? 127 : o[0]; - track->inst = o[1]; - track->command = newconvfx[o[2]]; + track->inst = o[1] <= NUMINST ? o[1] : 0; // ignore invalid instrument + track->command = o[2] < sizeof(newconvfx) ? newconvfx[o[2]] : 255; track->param1 = o[3] >> 4; track->param2 = o[3] & 0x0f; // Convert '&' command - if(o[2] == 36) + if (o[2] == 36) switch(track->param1) { case 0: // pattern delay (frames) track->command = 29; @@ -259,18 +286,17 @@ bool Ca2mLoader::load(const std::string &filename, const CFileProvider &fp) init_trackord(); - if(version == 1 || version == 5) - delete [] org; - else - delete [] secdata; + delete [] org; // Process flags - if(version >= 5) { + if (version >= 5) { CmodPlayer::flags |= Opl3; // All versions >= 5 are OPL3 - if(flags & 8) CmodPlayer::flags |= Tremolo; // Tremolo depth - if(flags & 16) CmodPlayer::flags |= Vibrato; // Vibrato depth + if (flags & 8) CmodPlayer::flags |= Tremolo; // Tremolo depth + if (flags & 16) CmodPlayer::flags |= Vibrato; // Vibrato depth } + // Note: crc value is not checked. + fp.close(f); rewind(0); return true; @@ -278,212 +304,8 @@ bool Ca2mLoader::load(const std::string &filename, const CFileProvider &fp) float Ca2mLoader::getrefresh() { - if(tempo != 18) - return (float) (tempo); - else + if (tempo == 18) return 18.2f; -} - -/*** private methods *************************************/ - -void Ca2mLoader::inittree() -{ - unsigned short i; - - for(i=2;i<=TWICEMAX;i++) { - dad[i] = i / 2; - freq[i] = 1; - } - - for(i=1;i<=MAXCHAR;i++) { - leftc[i] = 2 * i; - rghtc[i] = 2 * i + 1; - } -} - -void Ca2mLoader::updatefreq(unsigned short a,unsigned short b) -{ - do { - freq[dad[a]] = freq[a] + freq[b]; - a = dad[a]; - if(a != ROOT) - { - if(leftc[dad[a]] == a) - { - b = rghtc[dad[a]]; - } - else - { - b = leftc[dad[a]]; - } - } - } while(a != ROOT); - - if(freq[ROOT] == MAXFREQ) - for(a=1;a<=TWICEMAX;a++) - freq[a] >>= 1; -} - -void Ca2mLoader::updatemodel(unsigned short code) -{ - unsigned short a=code+SUCCMAX,b,c,code1,code2; - - freq[a]++; - if(dad[a] != ROOT) { - code1 = dad[a]; - if(leftc[code1] == a) - updatefreq(a,rghtc[code1]); - else - updatefreq(a,leftc[code1]); - - do { - code2 = dad[code1]; - if(leftc[code2] == code1) - b = rghtc[code2]; - else - b = leftc[code2]; - - if(freq[a] > freq[b]) { - if(leftc[code2] == code1) - rghtc[code2] = a; - else - leftc[code2] = a; - - if(leftc[code1] == a) { - leftc[code1] = b; - c = rghtc[code1]; - } else { - rghtc[code1] = b; - c = leftc[code1]; - } - - dad[b] = code1; - dad[a] = code2; - updatefreq(b,c); - a = b; - } - - a = dad[a]; - code1 = dad[a]; - } while(code1 != ROOT); - } -} - -unsigned short Ca2mLoader::inputcode(unsigned short bits) -{ - unsigned short i,code=0; - - for(i=1;i<=bits;i++) { - if(!ibitcount) { - if(ibitcount == MAXBUF) - ibufcount = 0; - ibitbuffer = wdbuf[ibufcount]; - ibufcount++; - ibitcount = 15; - } else - ibitcount--; - - if(ibitbuffer > 0x7fff) - code |= bitvalue[i-1]; - ibitbuffer <<= 1; - } - - return code; -} - -unsigned short Ca2mLoader::uncompress() -{ - unsigned short a=1; - - do { - if(!ibitcount) { - if(ibufcount == MAXBUF) - ibufcount = 0; - ibitbuffer = wdbuf[ibufcount]; - ibufcount++; - ibitcount = 15; - } else - ibitcount--; - - if(ibitbuffer > 0x7fff) - a = rghtc[a]; - else - a = leftc[a]; - ibitbuffer <<= 1; - } while(a <= MAXCHAR); - - a -= SUCCMAX; - updatemodel(a); - return a; -} - -void Ca2mLoader::decode() -{ - unsigned short i,j,k,t,c,count=0,dist,len,index; - - inittree(); - c = uncompress(); - - while(c != TERMINATE) { - if(c < 256) { - obuf[obufcount] = (unsigned char)c; - obufcount++; - if(obufcount == MAXBUF) { - output_size = MAXBUF; - obufcount = 0; - } - - buf[count] = (unsigned char)c; - count++; - if(count == MAXSIZE) - count = 0; - } else { - t = c - FIRSTCODE; - index = t / CODESPERRANGE; - len = t + MINCOPY - index * CODESPERRANGE; - dist = inputcode(copybits[index]) + len + copymin[index]; - - j = count; - k = count - dist; - if(count < dist) - k += MAXSIZE; - - for(i=0;i<=len-1;i++) { - obuf[obufcount] = buf[k]; - obufcount++; - if(obufcount == MAXBUF) { - output_size = MAXBUF; - obufcount = 0; - } - - buf[j] = buf[k]; - j++; k++; - if(j == MAXSIZE) j = 0; - if(k == MAXSIZE) k = 0; - } - - count += len; - if(count >= MAXSIZE) - count -= MAXSIZE; - } - c = uncompress(); - } - output_size = obufcount; -} - -unsigned short Ca2mLoader::sixdepak(unsigned short *source, unsigned char *dest, - unsigned short size) -{ - if((unsigned int)size + 4096 > MAXBUF) - return 0; - - buf = new unsigned char [MAXSIZE]; - input_size = size; - ibitcount = 0; ibitbuffer = 0; - obufcount = 0; ibufcount = 0; - wdbuf = source; obuf = dest; - - decode(); - delete [] buf; - return output_size; + else + return (float) (tempo); } diff --git a/plugins/adplug/adplug/a2m.h b/plugins/adplug/adplug/a2m.h index a5a0905f71..3cb795a84c 100644 --- a/plugins/adplug/adplug/a2m.h +++ b/plugins/adplug/adplug/a2m.h @@ -23,6 +23,7 @@ #define H_ADPLUG_A2MLOADER #include "protrack.h" +#include "sixdepack.h" class Ca2mLoader: public CmodPlayer { @@ -38,46 +39,17 @@ class Ca2mLoader: public CmodPlayer std::string gettype() { return std::string("AdLib Tracker 2"); } std::string gettitle() - { if(*songname) return std::string(songname,1,*songname); else return std::string(); } + { return std::string(songname + 1, *songname); } std::string getauthor() - { if(*author) return std::string(author,1,*author); else return std::string(); } + { return std::string(author + 1, *author); } unsigned int getinstruments() - { return 250; } + { return NUMINST; } std::string getinstrument(unsigned int n) - { return std::string(instname[n],1,*instname[n]); } + { return n < NUMINST ? std::string(instname[n] + 1, *instname[n]) : std::string(); } private: - -#define ADPLUG_A2M_COPYRANGES 6 -#define ADPLUG_A2M_FIRSTCODE 257 -#define ADPLUG_A2M_MINCOPY 3 -#define ADPLUG_A2M_MAXCOPY 255 -#define ADPLUG_A2M_CODESPERRANGE (ADPLUG_A2M_MAXCOPY - ADPLUG_A2M_MINCOPY + 1) -#define ADPLUG_A2M_MAXCHAR (ADPLUG_A2M_FIRSTCODE + ADPLUG_A2M_COPYRANGES * ADPLUG_A2M_CODESPERRANGE - 1) -#define ADPLUG_A2M_TWICEMAX (2 * ADPLUG_A2M_MAXCHAR + 1) - - static const unsigned int MAXFREQ, MINCOPY, MAXCOPY, COPYRANGES, - CODESPERRANGE, TERMINATE, FIRSTCODE, MAXCHAR, SUCCMAX, TWICEMAX, ROOT, - MAXBUF, MAXDISTANCE, MAXSIZE; - - static const unsigned short bitvalue[14]; - static const signed short copybits[ADPLUG_A2M_COPYRANGES], - copymin[ADPLUG_A2M_COPYRANGES]; - - void inittree(); - void updatefreq(unsigned short a,unsigned short b); - void updatemodel(unsigned short code); - unsigned short inputcode(unsigned short bits); - unsigned short uncompress(); - void decode(); - unsigned short sixdepak(unsigned short *source,unsigned char *dest,unsigned short size); - - char songname[43], author[43], instname[250][33]; - - unsigned short ibitcount, ibitbuffer, ibufcount, obufcount, input_size, - output_size, leftc[ADPLUG_A2M_MAXCHAR+1], rghtc[ADPLUG_A2M_MAXCHAR+1], - dad[ADPLUG_A2M_TWICEMAX+1], freq[ADPLUG_A2M_TWICEMAX+1], *wdbuf; - unsigned char *obuf, *buf; + enum {NUMINST = 250, INSTDATASIZE = 13}; + char songname[43], author[43], instname[NUMINST][33]; }; #endif diff --git a/plugins/adplug/adplug/adl.cpp b/plugins/adplug/adplug/adl.cpp index 498aeec137..8b27efc637 100644 --- a/plugins/adplug/adplug/adl.cpp +++ b/plugins/adplug/adplug/adl.cpp @@ -3,49 +3,56 @@ * * Original ADL player by Torbjorn Andersson and Johannes Schickel * 'lordhoto' of the ScummVM project. + * + * https://github.com/scummvm/scummvm/blob/master/engines/kyra/sound/drivers/adlib.cpp */ -/* ScummVM - Scumm Interpreter - * - * This file is licensed under both GPL and LGPL - * Copyright (C) 2006 The ScummVM project - * Copyright (C) 2006 Torbjorn Andersson and Johannes Schickel +/* ScummVM - Graphic Adventure Engine * - * GPL License + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * LPGL License + * LGPL License * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * $URL: https://svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/kyra/sound_adlib.cpp $ - * $Id$ + */ + +/* AdLib implementation of the sound output device. * + * It uses a sound file format special to EoB I, II, Dune II, + * Kyrandia 1 and 2, and LoL. There are slightly different + * variants: EoB I uses the oldest format (version 1); + * EoB II (version 2), Dune II and Kyrandia 1 (version 3) have + * the same file format (but need different offset adjustments); + * Kyrandia 2 and LoL format (version 4) is different again. */ #include @@ -57,34 +64,24 @@ #include "adl.h" #include "debug.h" +// Compatibility layer: + #ifdef ADL_DEBUG -# define warning(...) AdPlug_LogWrite(__VA_ARGS__); \ -AdPlug_LogWrite("\n") +# define warning(...) do { \ + AdPlug_LogWrite(__VA_ARGS__); \ + AdPlug_LogWrite("\n"); \ + } while (0) -# define debugC(i1, i2, ...) AdPlug_LogWrite(__VA_ARGS__); \ -AdPlug_LogWrite("\n") +# define debugC(i1, i2, ...) warning(__VA_ARGS__) #else # define kDebugLevelSound 1 -static inline void warning(const char *str, ...) -{ -} - -static inline void debugC(int i1, int i2, const char *str, ...) -{ -} +static inline void warning(const char *str, ...) {} +static inline void debugC(int i1, int i2, const char *str, ...) {} #endif -// #define warning(...) -// #define debugC(i1, i2, ...) - #define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0]))) -// Basic Adlib Programming: -// http://www.gamedev.net/reference/articles/article446.asp - -#define CALLBACKS_PER_SECOND 72 - typedef uint8_t uint8; typedef int8_t int8; typedef uint16_t uint16; @@ -94,669 +91,713 @@ typedef int32_t int32; typedef uint8_t byte; static inline uint16 READ_LE_UINT16(const void *ptr) { - const byte *b = (const byte *)ptr; - return (b[1] << 8) + b[0]; + const byte *b = (const byte *)ptr; + return (b[1] << 8) + b[0]; } static inline uint16 READ_BE_UINT16(const void *ptr) { - const byte *b = (const byte *)ptr; - return (b[0] << 8) + b[1]; + const byte *b = (const byte *)ptr; + return (b[0] << 8) + b[1]; +} + +template +static inline T CLIP(const T &value, const T &min, const T &max) { + return value < min ? min : value > max ? max : value; } -class AdlibDriver { +#if !defined(nullptr) && __cplusplus < 201103L && _MSC_VER < 1600 +const class nullptr_t { public: - AdlibDriver(Copl *opl); - ~AdlibDriver(); - - int callback(int opcode, ...); - void callback(); - - // AudioStream API - // int readBuffer(int16 *buffer, const int numSamples) { - // int32 samplesLeft = numSamples; - // memset(buffer, 0, sizeof(int16) * numSamples); - // while (samplesLeft) { - // if (!_samplesTillCallback) { - // callback(); - // _samplesTillCallback = _samplesPerCallback; - // _samplesTillCallbackRemainder += _samplesPerCallbackRemainder; - // if (_samplesTillCallbackRemainder >= CALLBACKS_PER_SECOND) { - // _samplesTillCallback++; - // _samplesTillCallbackRemainder -= CALLBACKS_PER_SECOND; - // } - // } - - // int32 render = MIN(samplesLeft, _samplesTillCallback); - // samplesLeft -= render; - // _samplesTillCallback -= render; - // YM3812UpdateOne(_adlib, buffer, render); - // buffer += render; - // } - // return numSamples; - // } - - bool isStereo() const { return false; } - bool endOfData() const { return false; } - // int getRate() const { return _mixer->getOutputRate(); } - - struct OpcodeEntry { - typedef int (AdlibDriver::*DriverOpcode)(va_list &list); - DriverOpcode function; - const char *name; - }; - - void setupOpcodeList(); - const OpcodeEntry *_opcodeList; - int _opcodesEntries; - - int snd_ret0x100(va_list &list); - int snd_ret0x1983(va_list &list); - int snd_initDriver(va_list &list); - int snd_deinitDriver(va_list &list); - int snd_setSoundData(va_list &list); - int snd_unkOpcode1(va_list &list); - int snd_startSong(va_list &list); - int snd_unkOpcode2(va_list &list); - int snd_unkOpcode3(va_list &list); - int snd_readByte(va_list &list); - int snd_writeByte(va_list &list); - int snd_getSoundTrigger(va_list &list); - int snd_unkOpcode4(va_list &list); - int snd_dummy(va_list &list); - int snd_getNullvar4(va_list &list); - int snd_setNullvar3(va_list &list); - int snd_setFlag(va_list &list); - int snd_clearFlag(va_list &list); - - // These variables have not yet been named, but some of them are partly - // known nevertheless: - // - // unk16 - Sound-related. Possibly some sort of pitch bend. - // unk18 - Sound-effect. Used for secondaryEffect1() - // unk19 - Sound-effect. Used for secondaryEffect1() - // unk20 - Sound-effect. Used for secondaryEffect1() - // unk21 - Sound-effect. Used for secondaryEffect1() - // unk22 - Sound-effect. Used for secondaryEffect1() - // unk29 - Sound-effect. Used for primaryEffect1() - // unk30 - Sound-effect. Used for primaryEffect1() - // unk31 - Sound-effect. Used for primaryEffect1() - // unk32 - Sound-effect. Used for primaryEffect2() - // unk33 - Sound-effect. Used for primaryEffect2() - // unk34 - Sound-effect. Used for primaryEffect2() - // unk35 - Sound-effect. Used for primaryEffect2() - // unk36 - Sound-effect. Used for primaryEffect2() - // unk37 - Sound-effect. Used for primaryEffect2() - // unk38 - Sound-effect. Used for primaryEffect2() - // unk39 - Currently unused, except for updateCallback56() - // unk40 - Currently unused, except for updateCallback56() - // unk41 - Sound-effect. Used for primaryEffect2() - - struct Channel { - uint8 opExtraLevel2; - uint8 *dataptr; - uint8 duration; - uint8 repeatCounter; - int8 baseOctave; - uint8 priority; - uint8 dataptrStackPos; - uint8 *dataptrStack[4]; - int8 baseNote; - uint8 unk29; - uint8 unk31; - uint16 unk30; - uint16 unk37; - uint8 unk33; - uint8 unk34; - uint8 unk35; - uint8 unk36; - uint8 unk32; - uint8 unk41; - uint8 unk38; - uint8 opExtraLevel1; - uint8 spacing2; - uint8 baseFreq; - uint8 tempo; - uint8 position; - uint8 regAx; - uint8 regBx; - typedef void (AdlibDriver::*Callback)(Channel&); - Callback primaryEffect; - Callback secondaryEffect; - uint8 fractionalSpacing; - uint8 opLevel1; - uint8 opLevel2; - uint8 opExtraLevel3; - uint8 twoChan; - uint8 unk39; - uint8 unk40; - uint8 spacing1; - uint8 durationRandomness; - uint8 unk19; - uint8 unk18; - int8 unk20; - int8 unk21; - uint8 unk22; - uint16 offset; - uint8 tempoReset; - uint8 rawNote; - int8 unk16; - }; - - void primaryEffect1(Channel &channel); - void primaryEffect2(Channel &channel); - void secondaryEffect1(Channel &channel); - - void resetAdlibState(); - void writeOPL(byte reg, byte val); - void initChannel(Channel &channel); - void noteOff(Channel &channel); - void unkOutput2(uint8 num); - - uint16 getRandomNr(); - void setupDuration(uint8 duration, Channel &channel); - - void setupNote(uint8 rawNote, Channel &channel, bool flag = false); - void setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel); - void noteOn(Channel &channel); - - void adjustVolume(Channel &channel); - - uint8 calculateOpLevel1(Channel &channel); - uint8 calculateOpLevel2(Channel &channel); - - uint16 checkValue(int16 val) { - if (val < 0) - val = 0; - else if (val > 0x3F) - val = 0x3F; - return val; - } - - // The sound data has at least two lookup tables: - // - // * One for programs, starting at offset 0. - // * One for instruments, starting at offset depending on version. - - uint8 *getProgram(int progId) { - return _soundData + READ_LE_UINT16(_soundData + 2 * progId); - } - - uint8 *getInstrument(int instrumentId) { - uint16 instOffset = 0; - switch (ADLVer) - { - case 1: - instOffset = 150 * 2; - break; - case 2: - instOffset = 250 * 2; - break; - case 3: - instOffset = 500 * 2; - break; - } - return _soundData + READ_LE_UINT16(_soundData + instOffset + 2 * instrumentId); - } - - void setupPrograms(); - void executePrograms(); - - struct ParserOpcode { - typedef int (AdlibDriver::*POpcode)(uint8 *&dataptr, Channel &channel, uint8 value); - POpcode function; - const char *name; - }; - - void setupParserOpcodeTable(); - const ParserOpcode *_parserOpcodeTable; - int _parserOpcodeTableSize; - - int update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value); - int update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value); - int update_jump(uint8 *&dataptr, Channel &channel, uint8 value); - int update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value); - int update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value); - int update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value); - int update_playRest(uint8 *&dataptr, Channel &channel, uint8 value); - int update_writeAdlib(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value); - int update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value); - int update_playNote(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value); - int update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback41(uint8 *&dataptr, Channel &channel, uint8 value); - int update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int update_nop1(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value); - int update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value); - int update_nop2(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value); - int update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value); - int update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value); - int update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value); - int updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value); - - // These variables have not yet been named, but some of them are partly - // known nevertheless: - // - // _unkValue1 - Unknown. Used for updating _unkValue2 - // _unkValue2 - Unknown. Used for updating _unkValue4 - // _unkValue3 - Unknown. Used for updating _unkValue2 - // _unkValue4 - Unknown. Used for updating _unkValue5 - // _unkValue5 - Unknown. Used for controlling updateCallback24(). - // _unkValue6 - Unknown. Rhythm section volume? - // _unkValue7 - Unknown. Rhythm section volume? - // _unkValue8 - Unknown. Rhythm section volume? - // _unkValue9 - Unknown. Rhythm section volume? - // _unkValue10 - Unknown. Rhythm section volume? - // _unkValue11 - Unknown. Rhythm section volume? - // _unkValue12 - Unknown. Rhythm section volume? - // _unkValue13 - Unknown. Rhythm section volume? - // _unkValue14 - Unknown. Rhythm section volume? - // _unkValue15 - Unknown. Rhythm section volume? - // _unkValue16 - Unknown. Rhythm section volume? - // _unkValue17 - Unknown. Rhythm section volume? - // _unkValue18 - Unknown. Rhythm section volume? - // _unkValue19 - Unknown. Rhythm section volume? - // _unkValue20 - Unknown. Rhythm section volume? - // _unkTable[] - Probably frequences for the 12-tone scale. - // _unkTable2[] - Unknown. Currently only used by updateCallback46() - // _unkTable2_1[] - One of the tables in _unkTable2[] - // _unkTable2_2[] - One of the tables in _unkTable2[] - // _unkTable2_3[] - One of the tables in _unkTable2[] - - uint8 ADLVer; - - int32 _samplesPerCallback; - int32 _samplesPerCallbackRemainder; - int32 _samplesTillCallback; - int32 _samplesTillCallbackRemainder; - - int _lastProcessed; - int8 _flagTrigger; - int _curChannel; - uint8 _soundTrigger; - int _soundsPlaying; - - uint16 _rnd; - - uint8 _unkValue1; - uint8 _unkValue2; - uint8 _unkValue3; - uint8 _unkValue4; - uint8 _unkValue5; - uint8 _unkValue6; - uint8 _unkValue7; - uint8 _unkValue8; - uint8 _unkValue9; - uint8 _unkValue10; - uint8 _unkValue11; - uint8 _unkValue12; - uint8 _unkValue13; - uint8 _unkValue14; - uint8 _unkValue15; - uint8 _unkValue16; - uint8 _unkValue17; - uint8 _unkValue18; - uint8 _unkValue19; - uint8 _unkValue20; - - int _flags; - - uint8 *_soundData; - size_t soundDataSize; - - uint8 _soundIdTable[0x10]; - Channel _channels[10]; - - uint8 _vibratoAndAMDepthBits; - uint8 _rhythmSectionBits; - - uint8 _curRegOffset; - uint8 _tempo; - - const uint8 *_tablePtr1; - const uint8 *_tablePtr2; - - static const uint8 _regOffset[]; - static const uint16 _unkTable[]; - static const uint8 *_unkTable2[]; - static const uint8 _unkTable2_1[]; - static const uint8 _unkTable2_2[]; - static const uint8 _unkTable2_3[]; - static const uint8 _unkTables[][32]; - - Copl *opl; + template // convertible to any type of null non-member pointer... + operator T*() const { + return 0; + } + + template // or any type of null member pointer... + operator T C::*() const { + return 0; + } + +private: + void operator&() const; // Can't take address of nullptr +} nullptr = {}; // and whose name is nullptr +#endif + +#define override // AdLibDriver has no base class here, so no overrides + +// ### Start of engines/kyra/sound/drivers/adlib.cpp ### + + // Basic AdLib Programming: + // https://web.archive.org/web/20050322080425/http://www.gamedev.net/reference/articles/article446.asp + +/* +#include "kyra/sound/drivers/pc_base.h" +#include "audio/fmopl.h" +#include "common/mutex.h" +*/ + +#define CALLBACKS_PER_SECOND 72 + +/* +namespace Kyra { +*/ + +class AdLibDriver /* : public PCSoundDriver */ { +public: + // AdLibDriver(Audio::Mixer *mixer, int version); + AdLibDriver(Copl *opl); + ~AdLibDriver() override; + + void initDriver() override; + void setSoundData(uint8 *data, uint32 size) override; + void startSound(int track, int volume) override; + bool isChannelPlaying(int channel) const override; + void stopAllChannels() override; + int getSoundTrigger() const override { return _soundTrigger; } + void resetSoundTrigger() override { _soundTrigger = 0; } + + void callback(); + + /* + void setSyncJumpMask(uint16 mask) override { _syncJumpMask = mask; } + + void setMusicVolume(uint8 volume) override; + void setSfxVolume(uint8 volume) override; + */ + + void setVersion(uint8 v) { // added in AdPlug + _version = v; + _numPrograms = (_version == 1) ? 150 : ((_version == 4) ? 500 : 250); + } + + bool isChannelRepeating(int i) { // added in AdPlug + return _channels[i].repeating; + } + +private: + // These variables have not yet been named, but some of them are partly + // known nevertheless: + // + // unk39 - Currently unused, except for updateCallback56() + // unk40 - Currently unused, except for updateCallback56() + + struct Channel { + bool lock; // New to ScummVM + bool repeating; // Added in Adplug + uint8 opExtraLevel2; + const uint8 *dataptr; + uint8 duration; + uint8 repeatCounter; + int8 baseOctave; + uint8 priority; + uint8 dataptrStackPos; + const uint8 *dataptrStack[4]; + int8 baseNote; + uint8 slideTempo; + uint8 slideTimer; + int16 slideStep; + int16 vibratoStep; + uint8 vibratoStepRange; + uint8 vibratoStepsCountdown; + uint8 vibratoNumSteps; + uint8 vibratoDelay; + uint8 vibratoTempo; + uint8 vibratoTimer; + uint8 vibratoDelayCountdown; + uint8 opExtraLevel1; + uint8 spacing2; + uint8 baseFreq; + uint8 tempo; + uint8 timer; + uint8 regAx; + uint8 regBx; + typedef void (AdLibDriver::*Callback)(Channel&); + Callback primaryEffect; + Callback secondaryEffect; + uint8 fractionalSpacing; + uint8 opLevel1; + uint8 opLevel2; + uint8 opExtraLevel3; + uint8 twoChan; + uint8 unk39; + uint8 unk40; + uint8 spacing1; + uint8 durationRandomness; + uint8 secondaryEffectTempo; + uint8 secondaryEffectTimer; + int8 secondaryEffectSize; + int8 secondaryEffectPos; + uint8 secondaryEffectRegbase; + uint16 secondaryEffectData; + uint8 tempoReset; + uint8 rawNote; + int8 pitchBend; + uint8 volumeModifier; + }; + + void primaryEffectSlide(Channel &channel); + void primaryEffectVibrato(Channel &channel); + void secondaryEffect1(Channel &channel); + + void resetAdLibState(); + void writeOPL(byte reg, byte val); + void initChannel(Channel &channel); + void noteOff(Channel &channel); + void initAdlibChannel(uint8 num); + + uint16 getRandomNr(); + void setupDuration(uint8 duration, Channel &channel); + + void setupNote(uint8 rawNote, Channel &channel, bool flag = false); + void setupInstrument(uint8 regOffset, const uint8 *dataptr, Channel &channel); + void noteOn(Channel &channel); + + void adjustVolume(Channel &channel); + + uint8 calculateOpLevel1(Channel &channel); + uint8 calculateOpLevel2(Channel &channel); + + static uint16 checkValue(int16 val) { return CLIP(val, 0, 0x3F); } + + // The driver uses timer/tempo pairs in several places. On every + // callback, the tempo is added to the timer. This will frequently + // cause the timer to "wrap around", which is the signal to go ahead + // and do more stuff. + static bool advance(uint8 &timer, uint8 tempo) { + uint8 old = timer; + timer += tempo; + return timer < old; + } + + const uint8 *checkDataOffset(const uint8 *ptr, long n) { + if (ptr) { + long offset = ptr - _soundData; + if (n >= -offset && n <= (long)_soundDataSize - offset) + return ptr + n; + } + return nullptr; + } + + // The sound data has two lookup tables: + // * One for programs, starting at offset 0. + // * One for instruments, starting at offset 300, 500, or 1000. + + // Method moved to patent class in scummvm: + uint8 *getProgram(int progId) { + // Safety check: invalid progId would crash. + if (progId < 0 || progId >= (int32)_soundDataSize / 2) + return nullptr; + + const uint16 offset = READ_LE_UINT16(_soundData + 2 * progId); + + // In case an invalid offset is specified we return nullptr to + // indicate an error. 0xFFFF seems to indicate "this is not a valid + // program/instrument". However, 0 is also invalid because it points + // inside the offset table itself. We also ignore any offsets outside + // of the actual data size. + // The original does not contain any safety checks and will simply + // read outside of the valid sound data in case an invalid offset is + // encountered. + if (offset == 0 || offset >= _soundDataSize) { + return nullptr; + } else { + return _soundData + offset; + } + } + + const uint8 *getInstrument(int instrumentId) { + return getProgram(_numPrograms + instrumentId); + } + + void setupPrograms(); + void executePrograms(); + + struct ParserOpcode { + typedef int (AdLibDriver::*POpcode)(Channel &channel, const uint8 *values); + POpcode function; + const char *name; + int values; + }; + + static const ParserOpcode _parserOpcodeTable[]; + static const int _parserOpcodeTableSize; + + int update_setRepeat(Channel &channel, const uint8 *values); + int update_checkRepeat(Channel &channel, const uint8 *values); + int update_setupProgram(Channel &channel, const uint8 *values); + int update_setNoteSpacing(Channel &channel, const uint8 *values); + int update_jump(Channel &channel, const uint8 *values); + int update_jumpToSubroutine(Channel &channel, const uint8 *values); + int update_returnFromSubroutine(Channel &channel, const uint8 *values); + int update_setBaseOctave(Channel &channel, const uint8 *values); + int update_stopChannel(Channel &channel, const uint8 *values); + int update_playRest(Channel &channel, const uint8 *values); + int update_writeAdLib(Channel &channel, const uint8 *values); + int update_setupNoteAndDuration(Channel &channel, const uint8 *values); + int update_setBaseNote(Channel &channel, const uint8 *values); + int update_setupSecondaryEffect1(Channel &channel, const uint8 *values); + int update_stopOtherChannel(Channel &channel, const uint8 *values); + int update_waitForEndOfProgram(Channel &channel, const uint8 *values); + int update_setupInstrument(Channel &channel, const uint8 *values); + int update_setupPrimaryEffectSlide(Channel &channel, const uint8 *values); + int update_removePrimaryEffectSlide(Channel &channel, const uint8 *values); + int update_setBaseFreq(Channel &channel, const uint8 *values); + int update_setupPrimaryEffectVibrato(Channel &channel, const uint8 *values); + int update_setPriority(Channel &channel, const uint8 *values); + int update_setBeat(Channel &channel, const uint8 *values); + int update_waitForNextBeat(Channel &channel, const uint8 *values); + int update_setExtraLevel1(Channel &channel, const uint8 *values); + int update_setupDuration(Channel &channel, const uint8 *values); + int update_playNote(Channel &channel, const uint8 *values); + int update_setFractionalNoteSpacing(Channel &channel, const uint8 *values); + int update_setTempo(Channel &channel, const uint8 *values); + int update_removeSecondaryEffect1(Channel &channel, const uint8 *values); + int update_setChannelTempo(Channel &channel, const uint8 *values); + int update_setExtraLevel3(Channel &channel, const uint8 *values); + int update_setExtraLevel2(Channel &channel, const uint8 *values); + int update_changeExtraLevel2(Channel &channel, const uint8 *values); + int update_setAMDepth(Channel &channel, const uint8 *values); + int update_setVibratoDepth(Channel &channel, const uint8 *values); + int update_changeExtraLevel1(Channel &channel, const uint8 *values); + int update_clearChannel(Channel &channel, const uint8 *values); + int update_changeNoteRandomly(Channel &channel, const uint8 *values); + int update_removePrimaryEffectVibrato(Channel &channel, const uint8 *values); + int update_pitchBend(Channel &channel, const uint8 *values); + int update_resetToGlobalTempo(Channel &channel, const uint8 *values); + int update_nop(Channel &channel, const uint8 *values); + int update_setDurationRandomness(Channel &channel, const uint8 *values); + int update_changeChannelTempo(Channel &channel, const uint8 *values); + int updateCallback46(Channel &channel, const uint8 *values); + int update_setupRhythmSection(Channel &channel, const uint8 *values); + int update_playRhythmSection(Channel &channel, const uint8 *values); + int update_removeRhythmSection(Channel &channel, const uint8 *values); + int update_setRhythmLevel2(Channel &channel, const uint8 *values); + int update_changeRhythmLevel1(Channel &channel, const uint8 *values); + int update_setRhythmLevel1(Channel &channel, const uint8 *values); + int update_setSoundTrigger(Channel &channel, const uint8 *values); + int update_setTempoReset(Channel &channel, const uint8 *values); + int updateCallback56(Channel &channel, const uint8 *values); +private: + // These variables have not yet been named, but some of them are partly + // known nevertheless: + // + // _unkTable2[] - Unknown. Currently only used by updateCallback46() + // _unkTable2_1[] - One of the tables in _unkTable2[] + // _unkTable2_2[] - One of the tables in _unkTable2[] + // _unkTable2_3[] - One of the tables in _unkTable2[] + + int _curChannel; + uint8 _soundTrigger; + + uint16 _rnd; + + uint8 _beatDivider; + uint8 _beatDivCnt; + uint8 _callbackTimer; + uint8 _beatCounter; + uint8 _beatWaiting; + uint8 _opLevelBD; + uint8 _opLevelHH; + uint8 _opLevelSD; + uint8 _opLevelTT; + uint8 _opLevelCY; + uint8 _opExtraLevel1HH; + uint8 _opExtraLevel2HH; + uint8 _opExtraLevel1CY; + uint8 _opExtraLevel2CY; + uint8 _opExtraLevel2TT; + uint8 _opExtraLevel1TT; + uint8 _opExtraLevel1SD; + uint8 _opExtraLevel2SD; + uint8 _opExtraLevel1BD; + uint8 _opExtraLevel2BD; + + // OPL::OPL *_adlib; + Copl *_opl; // added in AdPlug + + uint8 *_soundData; // moved to parent class in scummvm + uint32 _soundDataSize; // moved to parent class in scummvm + + struct QueueEntry { + QueueEntry() : data(0), id(0), volume(0) {} + QueueEntry(uint8 *ptr, uint8 track, uint8 vol) : data(ptr), id(track), volume(vol) {} + uint8 *data; + uint8 id; + uint8 volume; + }; + + QueueEntry _programQueue[16]; + int _programStartTimeout; + int _programQueueStart, _programQueueEnd; + bool _retrySounds; + + void adjustSfxData(uint8 *data, int volume); + uint8 *_sfxPointer; + int _sfxPriority; + int _sfxVelocity; + + Channel _channels[10]; + + uint8 _vibratoAndAMDepthBits; + uint8 _rhythmSectionBits; + + uint8 _curRegOffset; + uint8 _tempo; + + const uint8 *_tablePtr1; + const uint8 *_tablePtr2; + + static const uint8 _regOffset[]; + static const uint16 _freqTable[]; + static const uint8 *const _unkTable2[]; + static const int _unkTable2Size; + static const uint8 _unkTable2_1[]; + static const uint8 _unkTable2_2[]; + static const uint8 _unkTable2_3[]; + static const uint8 _pitchBendTables[][32]; + + uint16 _syncJumpMask; + + /* + Common::Mutex _mutex; + Audio::Mixer *_mixer; + */ + + uint8 _musicVolume, _sfxVolume; + + int _numPrograms; + int _version; }; -AdlibDriver::AdlibDriver(Copl *newopl) - : opl(newopl) +//AdLibDriver::AdLibDriver(Audio::Mixer *mixer, int version) : PCSoundDriver() { +AdLibDriver::AdLibDriver(Copl *newopl) : + _opl(newopl), _soundData(0), _soundDataSize(0), _numPrograms(0), _version(0) { - setupOpcodeList(); - setupParserOpcodeTable(); + /* + _version = version; + _numPrograms = (_version == 1) ? 150 : ((_version == 4) ? 500 : 250); + + _mixer = mixer; + + _adlib = OPL::Config::create(); + if (!_adlib || !_adlib->init()) + error("Failed to create OPL"); + */ - // _mixer = mixer; + memset(_channels, 0, sizeof(_channels)); - ADLVer = 0; - _flags = 0; - // _adlib = makeAdlibOPL(getRate()); - // assert(_adlib); + _vibratoAndAMDepthBits = _curRegOffset = 0; - memset(_channels, 0, sizeof(_channels)); - _soundData = 0; + _curChannel = _rhythmSectionBits = 0; + _rnd = 0x1234; - _vibratoAndAMDepthBits = _curRegOffset = 0; + _tempo = 0; + _soundTrigger = 0; + _programStartTimeout = 0; - _lastProcessed = _flagTrigger = _curChannel = _rhythmSectionBits = 0; - _soundsPlaying = 0; - _rnd = 0x1234; + _callbackTimer = 0xFF; + _beatDivider = _beatDivCnt = _beatCounter = _beatWaiting = 0; + _opLevelBD = _opLevelHH = _opLevelSD = _opLevelTT = _opLevelCY = 0; + _opExtraLevel1HH = _opExtraLevel2HH = + _opExtraLevel1CY = _opExtraLevel2CY = + _opExtraLevel2TT = _opExtraLevel1TT = + _opExtraLevel1SD = _opExtraLevel2SD = + _opExtraLevel1BD = _opExtraLevel2BD = 0; - _tempo = 0; - _soundTrigger = 0; + _tablePtr1 = _tablePtr2 = nullptr; - _unkValue3 = 0xFF; - _unkValue1 = _unkValue2 = _unkValue4 = _unkValue5 = 0; - _unkValue6 = _unkValue7 = _unkValue8 = _unkValue9 = _unkValue10 = 0; - _unkValue11 = _unkValue12 = _unkValue13 = _unkValue14 = _unkValue15 = - _unkValue16 = _unkValue17 = _unkValue18 = _unkValue19 = _unkValue20 = 0; + _syncJumpMask = 0; - _tablePtr1 = _tablePtr2 = 0; + // _musicVolume = 0; + // _sfxVolume = 0; + _musicVolume = _sfxVolume = 0xFF; - // _mixer->setupPremix(this); + _sfxPointer = nullptr; - // _samplesPerCallback = getRate() / CALLBACKS_PER_SECOND; - // _samplesPerCallbackRemainder = getRate() % CALLBACKS_PER_SECOND; - _samplesTillCallback = 0; - _samplesTillCallbackRemainder = 0; + _programQueueStart = _programQueueEnd = 0; + _retrySounds = false; + + // _adlib->start(new Common::Functor0Mem(this, &AdLibDriver::callback), CALLBACKS_PER_SECOND); } -AdlibDriver::~AdlibDriver() { - // _mixer->setupPremix(0); - // OPLDestroy(_adlib); - // _adlib = 0; +AdLibDriver::~AdLibDriver() { + /* + delete _adlib; + _adlib = nullptr; + */ } -int AdlibDriver::callback(int opcode, ...) { - // lock(); - if (opcode >= _opcodesEntries || opcode < 0) { - warning("AdlibDriver: calling unknown opcode '%d'", opcode); - return 0; - } +/* +void AdLibDriver::setMusicVolume(uint8 volume) { + Common::StackLock lock(_mutex); - debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d)", _opcodeList[opcode].name, opcode); + _musicVolume = volume; - va_list args; - va_start(args, opcode); - int returnValue = (this->*(_opcodeList[opcode].function))(args); - va_end(args); - // unlock(); - return returnValue; -} + for (uint i = 0; i < 6; ++i) { + Channel &chan = _channels[i]; + chan.volumeModifier = volume; -// Opcodes + const uint8 regOffset = _regOffset[i]; -int AdlibDriver::snd_ret0x100(va_list &list) { - return 0x100; -} + // Level Key Scaling / Total Level + writeOPL(0x40 + regOffset, calculateOpLevel1(chan)); + writeOPL(0x43 + regOffset, calculateOpLevel2(chan)); + } -int AdlibDriver::snd_ret0x1983(va_list &list) { - return 0x1983; -} + // For now we use the music volume for both sfx and music in Kyra1 and EoB + if (_version < 4) { + _sfxVolume = volume; -int AdlibDriver::snd_initDriver(va_list &list) { - _lastProcessed = _soundsPlaying = 0; - resetAdlibState(); - return 0; -} + for (uint i = 6; i < 9; ++i) { + Channel &chan = _channels[i]; + chan.volumeModifier = volume; -int AdlibDriver::snd_deinitDriver(va_list &list) { - resetAdlibState(); - return 0; -} + const uint8 regOffset = _regOffset[i]; -int AdlibDriver::snd_setSoundData(va_list &list) { - if (_soundData) { - delete [] _soundData; - _soundData = 0; - } - _soundData = va_arg(list, uint8*); - return 0; + // Level Key Scaling / Total Level + writeOPL(0x40 + regOffset, calculateOpLevel1(chan)); + writeOPL(0x43 + regOffset, calculateOpLevel2(chan)); + } + } } -int AdlibDriver::snd_unkOpcode1(va_list &list) { - warning("unimplemented snd_unkOpcode1"); - return 0; -} +void AdLibDriver::setSfxVolume(uint8 volume) { + // We only support sfx volume in version 4 games. + if (_version < 4) + return; -int AdlibDriver::snd_startSong(va_list &list) { - int songId = va_arg(list, int); - _flags |= 8; - _flagTrigger = 1; + Common::StackLock lock(_mutex); - uint8 *ptr = getProgram(songId); - if (ptr >= _soundData + soundDataSize) { - return 0; - } - uint8 chan = *ptr; + _sfxVolume = volume; - if ((songId << 1) != 0) { - if (chan == 9) { - if (_flags & 2) - return 0; - } else { - if (_flags & 1) - return 0; - } - } + for (uint i = 6; i < 9; ++i) { + Channel &chan = _channels[i]; + chan.volumeModifier = volume; - _soundIdTable[_soundsPlaying++] = songId; - _soundsPlaying &= 0x0F; + const uint8 regOffset = _regOffset[i]; - return 0; + // Level Key Scaling / Total Level + writeOPL(0x40 + regOffset, calculateOpLevel1(chan)); + writeOPL(0x43 + regOffset, calculateOpLevel2(chan)); + } } +*/ -int AdlibDriver::snd_unkOpcode2(va_list &list) { - warning("unimplemented snd_unkOpcode2"); - return 0; +void AdLibDriver::initDriver() { + // Common::StackLock lock(_mutex); + resetAdLibState(); } -int AdlibDriver::snd_unkOpcode3(va_list &list) { - int value = va_arg(list, int); - int loop = value; - if (value < 0) { - value = 0; - loop = 9; - } - loop -= value; - ++loop; +void AdLibDriver::setSoundData(uint8 *data, uint32 size) { + // Common::StackLock lock(_mutex); - while (loop--) { - _curChannel = value; - Channel &channel = _channels[_curChannel]; - channel.priority = 0; - channel.dataptr = 0; - if (value != 9) { - noteOff(channel); - } - ++value; - } + // Drop all tracks that are still queued. These would point to the old + // sound data. + _programQueueStart = _programQueueEnd = 0; + _programQueue[0] = QueueEntry(); - return 0; -} + _sfxPointer = nullptr; -int AdlibDriver::snd_readByte(va_list &list) { - int a = va_arg(list, int); - int b = va_arg(list, int); - uint8 *ptr = getProgram(a) + b; - if (ptr >= _soundData + soundDataSize) { - return 0; - } - return *ptr; + _soundData = data; + _soundDataSize = size; } -int AdlibDriver::snd_writeByte(va_list &list) { - int a = va_arg(list, int); - int b = va_arg(list, int); - int c = va_arg(list, int); - uint8 *ptr = getProgram(a) + b; - if (ptr >= _soundData + soundDataSize) { - return 0; - } - uint8 oldValue = *ptr; - *ptr = (uint8)c; - return oldValue; -} +void AdLibDriver::startSound(int track, int volume) { + // Common::StackLock lock(_mutex); -int AdlibDriver::snd_getSoundTrigger(va_list &list) { - return _soundTrigger; -} + uint8 *trackData = getProgram(track); + if (!trackData) + return; -int AdlibDriver::snd_unkOpcode4(va_list &list) { - warning("unimplemented snd_unkOpcode4"); - return 0; -} + if (_programQueueEnd == _programQueueStart && _programQueue[_programQueueEnd].data != 0) { + // Don't warn when dropping tracks in EoB. The queue is always full there if a couple of monsters are around. + if (_version >= 3) + warning("AdLibDriver: Program queue full, dropping track %d", track); + return; + } -int AdlibDriver::snd_dummy(va_list &list) { - return 0; + _programQueue[_programQueueEnd] = QueueEntry(trackData, track, volume); + ++_programQueueEnd &= 15; } -int AdlibDriver::snd_getNullvar4(va_list &list) { - warning("unimplemented snd_getNullvar4"); - return 0; +bool AdLibDriver::isChannelPlaying(int channel) const { + // Common::StackLock lock(_mutex); + + assert(channel >= 0 && channel <= 9); + return (_channels[channel].dataptr != 0); } -int AdlibDriver::snd_setNullvar3(va_list &list) { - warning("unimplemented snd_setNullvar3"); - return 0; +void AdLibDriver::stopAllChannels() { + // Common::StackLock lock(_mutex); + + for (int channel = 0; channel <= 9; ++channel) { + _curChannel = channel; + + Channel &chan = _channels[_curChannel]; + chan.priority = 0; + chan.dataptr = 0; + + if (channel != 9) + noteOff(chan); + } + _retrySounds = false; + + _programQueueStart = _programQueueEnd = 0; + _programQueue[0] = QueueEntry(); + _programStartTimeout = 0; } -int AdlibDriver::snd_setFlag(va_list &list) { - int oldFlags = _flags; - _flags |= va_arg(list, int); - return oldFlags; +// timer callback +// +// Starts and executes programs and maintains a global beat that channels +// can synchronize on. + +void AdLibDriver::callback() { + // Common::StackLock lock(_mutex); + if (_programStartTimeout) + --_programStartTimeout; + else + setupPrograms(); + executePrograms(); + + if (advance(_callbackTimer, _tempo)) { + if (!(--_beatDivCnt)) { + _beatDivCnt = _beatDivider; + ++_beatCounter; + } + } } -int AdlibDriver::snd_clearFlag(va_list &list) { - int oldFlags = _flags; - _flags &= ~(va_arg(list, int)); - return oldFlags; +void AdLibDriver::setupPrograms() { + QueueEntry &entry = _programQueue[_programQueueStart]; + uint8 *ptr = entry.data; + + // If there is no program queued, we skip this. + if (_programQueueStart == _programQueueEnd && !ptr) + return; + + // The AdLib driver (in its old versions used for EOB) is not suitable for modern (fast) CPUs. + // The stop sound track (track 0 which has a priority of 50) will often still be busy when the + // next sound (with a lower priority) starts which will cause that sound to be skipped. We simply + // restart incoming sounds during stop sound execution. + // UPDATE: This still applies after introduction of the _programQueue. + // UPDATE: This can also happen with the HOF main menu, so I commented out the version < 3 limitation. + QueueEntry retrySound; + if (/*_version < 3 &&*/ entry.id == 0) + _retrySounds = true; + else if (_retrySounds) + retrySound = entry; + + // Clear the queue entry + entry.data = nullptr; + ++_programQueueStart &= 15; + + // Safety check: 2 bytes (channel, priority) are required for each + // program, plus 2 more bytes (opcode, _sfxVelocity) for sound effects. + // More data is needed, but executePrograms() checks for that. + // Also ignore request for invalid channel number. + if (!checkDataOffset(ptr, 2)) + return; + + const int chan = *ptr; + if (chan > 9 || (chan < 9 && !checkDataOffset(ptr, 4))) + return; + + Channel &channel = _channels[chan]; + + // Adjust data in case we hit a sound effect. + adjustSfxData(ptr++, entry.volume); + + const int priority = *ptr++; + + // Only start this sound if its priority is higher than the one + // already playing. + + if (priority >= channel.priority) { + initChannel(channel); + channel.priority = priority; + channel.dataptr = ptr; + channel.tempo = 0xFF; + channel.timer = 0xFF; + channel.duration = 1; + + if (chan <= 5) + channel.volumeModifier = _musicVolume; + else + channel.volumeModifier = _sfxVolume; + + initAdlibChannel(chan); + + // We need to wait two callback calls till we can start another track. + // This is (probably) required to assure that the sfx are started with + // the correct priority and velocity. + _programStartTimeout = 2; + + retrySound = QueueEntry(); + } + + if (retrySound.data) { + debugC(9, kDebugLevelSound, "AdLibDriver::setupPrograms(): WORKAROUND - Restarting skipped sound %d)", retrySound.id); + startSound(retrySound.id, retrySound.volume); + } } -// timer callback +void AdLibDriver::adjustSfxData(uint8 *ptr, int volume) { + // Check whether we need to reset the data of an old sfx which has been + // started. + if (_sfxPointer) { + _sfxPointer[1] = _sfxPriority; + _sfxPointer[3] = _sfxVelocity; + _sfxPointer = nullptr; + } -void AdlibDriver::callback() { - // lock(); - --_flagTrigger; - if (_flagTrigger < 0) - _flags &= ~8; - setupPrograms(); - executePrograms(); - - uint8 temp = _unkValue3; - _unkValue3 += _tempo; - if (_unkValue3 < temp) { - if (!(--_unkValue2)) { - _unkValue2 = _unkValue1; - ++_unkValue4; - } - } - // unlock(); -} - -void AdlibDriver::setupPrograms() { - while (_lastProcessed != _soundsPlaying) { - uint8 *ptr = getProgram(_soundIdTable[_lastProcessed]); - if (ptr >= _soundData + soundDataSize) { - return; - } - uint8 chan = *ptr++; - if (ptr >= _soundData + soundDataSize) { - return; - } - if (chan >= 10) { - return; - } - uint8 priority = *ptr++; - if (ptr >= _soundData + soundDataSize) { - return; - } - - // Only start this sound if its priority is higher than the one - // already playing. - - Channel &channel = _channels[chan]; - - if (priority >= channel.priority) { - initChannel(channel); - channel.priority = priority; - channel.dataptr = ptr; - channel.tempo = 0xFF; - channel.position = 0xFF; - channel.duration = 1; - unkOutput2(chan); - } - - ++_lastProcessed; - _lastProcessed &= 0x0F; - } + // Only music tracks are started on channel 9, thus we need to make sure + // we do not have a music track here. + if (*ptr == 9) + return; + + // Store the pointer so we can reset the data when a new program is started. + _sfxPointer = ptr; + + // Store the old values. + _sfxPriority = ptr[1]; + _sfxVelocity = ptr[3]; + + // Adjust the values. + if (volume != 0xFF) { + if (_version >= 3) { + int newVal = ((((ptr[3]) + 63) * volume) >> 8) & 0xFF; + ptr[3] = -newVal + 63; + ptr[1] = ((ptr[1] * volume) >> 8) & 0xFF; + } else { + int newVal = ((_sfxVelocity << 2) ^ 0xFF) * volume; + ptr[3] = (newVal >> 10) ^ 0x3F; + ptr[1] = newVal >> 11; + } + } } // A few words on opcode parsing and timing: // -// First of all, We simulate a timer callback 72 times per second. Each timeout +// First of all, we simulate a timer callback 72 times per second. Each timeout // we update each channel that has something to play. // -// Each channel has its own individual tempo, which is added to its position. -// This will frequently cause the position to "wrap around" but that is -// intentional. In fact, it's the signal to go ahead and do more stuff with -// that channel. +// Each channel has its own individual tempo and timer. The timer is updated, +// and when it wraps around, we go ahead and do more stuff with that channel. +// Otherwise we skip straiht to the effect callbacks. // -// Each channel also has a duration, indicating how much time is left on the -// its current task. This duration is decreased by one. As long as it still has +// Each channel also has a duration, indicating how much time is left on its +// current task. This duration is decreased by one. As long as it still has // not reached zero, the only thing that can happen is that the note is turned // off depending on manual or automatic note spacing. Once the duration reaches // zero, a new set of musical opcodes are executed. // -// An opcode is one byte, followed by a variable number of parameters. Since -// most opcodes have at least one one-byte parameter, we read that as well. Any -// opcode that doesn't have that one parameter is responsible for moving the -// data pointer back again. -// +// An opcode is one byte, followed by a variable number of parameters. // If the most significant bit of the opcode is 1, it's a function; call it. -// The opcode functions return either 0 (continue), 1 (stop) or 2 (stop, and do -// not run the effects callbacks). +// An opcode function can change control flow by updating the channel's data +// pointer (which is set to the next opcode before the call). The function's +// return value is either 0 (continue), 1 (stop) or 2 (stop, and do not run +// the effects callbacks). // // If the most significant bit of the opcode is 0, it's a note, and the first // parameter is its duration. (There are cases where the duration is modified @@ -767,477 +808,530 @@ void AdlibDriver::setupPrograms() { // effects callbacks. The final opcode in a set can prevent this, if it's a // function and it returns anything other than 1. -void AdlibDriver::executePrograms() { - // Each channel runs its own program. There are ten channels: One for - // each Adlib channel (0-8), plus one "control channel" (9) which is - // the one that tells the other channels what to do. - - for (_curChannel = 9; _curChannel >= 0; --_curChannel) { - int result = 1; - - if (!_channels[_curChannel].dataptr) { - continue; - } - - Channel &channel = _channels[_curChannel]; - if (_curChannel != 9) { - _curRegOffset = _regOffset[_curChannel]; - } - - if (channel.tempoReset) { - channel.tempo = _tempo; - } - - uint8 backup = channel.position; - channel.position += channel.tempo; - if (channel.position < backup) { - if (--channel.duration) { - if (channel.duration == channel.spacing2) - noteOff(channel); - if (channel.duration == channel.spacing1 && _curChannel != 9) - noteOff(channel); - } else { - // An opcode is not allowed to modify its own - // data pointer except through the 'dataptr' - // parameter. To enforce that, we have to work - // on a copy of the data pointer. - // - // This fixes a subtle music bug where the - // wrong music would play when getting the - // quill in Kyra 1. - uint8 *dataptr = channel.dataptr; - while (dataptr) { - uint8 opcode = *dataptr++; - uint8 param = *dataptr++; - - if (opcode & 0x80) { - opcode &= 0x7F; - if (opcode >= _parserOpcodeTableSize) - opcode = _parserOpcodeTableSize - 1; - debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d) (channel: %d)", _parserOpcodeTable[opcode].name, opcode, _curChannel); - result = (this->*(_parserOpcodeTable[opcode].function))(dataptr, channel, param); - channel.dataptr = dataptr; - if (result) - break; - } else { - debugC(9, kDebugLevelSound, "Note on opcode 0x%02X (duration: %d) (channel: %d)", opcode, param, _curChannel); - setupNote(opcode, channel); - noteOn(channel); - setupDuration(param, channel); - if (param) { - channel.dataptr = dataptr; - break; - } - } - } - } - } - - if (result == 1) { - if (channel.primaryEffect) - (this->*(channel.primaryEffect))(channel); - if (channel.secondaryEffect) - (this->*(channel.secondaryEffect))(channel); - } - } -} - -// - -void AdlibDriver::resetAdlibState() { - debugC(9, kDebugLevelSound, "resetAdlibState()"); - _rnd = 0x1234; - - // Authorize the control of the waveforms - writeOPL(0x01, 0x20); - - // Select FM music mode - writeOPL(0x08, 0x00); - - // I would guess the main purpose of this is to turn off the rhythm, - // thus allowing us to use 9 melodic voices instead of 6. - writeOPL(0xBD, 0x00); - - int loop = 10; - while (loop--) { - if (loop != 9) { - // Silence the channel - writeOPL(0x40 + _regOffset[loop], 0x3F); - writeOPL(0x43 + _regOffset[loop], 0x3F); - } - initChannel(_channels[loop]); - } +void AdLibDriver::executePrograms() { + // Each channel runs its own program. There are ten channels: One for + // each AdLib channel (0-8), plus one "control channel" (9) which is + // the one that tells the other channels what to do. + + if (_syncJumpMask) { + // This is where we ensure that channels that are made to jump + // "in sync" do so. + + for (_curChannel = 9; _curChannel >= 0; --_curChannel) { + if ((_syncJumpMask & (1 << _curChannel)) && _channels[_curChannel].dataptr && !_channels[_curChannel].lock) + break; // don't unlock + } + + if (_curChannel < 0) { + // force unlock + for (_curChannel = 9; _curChannel >= 0; --_curChannel) + if (_syncJumpMask & (1 << _curChannel)) + _channels[_curChannel].lock = false; + } + } + + for (_curChannel = 9; _curChannel >= 0; --_curChannel) { + Channel &channel = _channels[_curChannel]; + const uint8 *&dataptr = channel.dataptr; + + if (!dataptr) + continue; + + if (channel.lock && (_syncJumpMask & (1 << _curChannel))) + continue; + + if (_curChannel == 9) + _curRegOffset = 0; + else + _curRegOffset = _regOffset[_curChannel]; + + if (channel.tempoReset) + channel.tempo = _tempo; + + int result = 1; + if (advance(channel.timer, channel.tempo)) { + if (--channel.duration) { + if (channel.duration == channel.spacing2) + noteOff(channel); + if (channel.duration == channel.spacing1 && _curChannel != 9) + noteOff(channel); + } else { + // Process some opcodes. + result = 0; + } + } + + while (result == 0 && dataptr) { + uint8 opcode = 0xFF; + // Safety check to avoid illegal access. + // Stop channel if not enough data. + if (checkDataOffset(dataptr, 1)) + opcode = *dataptr++; + + if (opcode & 0x80) { + opcode = CLIP(opcode & 0x7F, 0, _parserOpcodeTableSize - 1); + const ParserOpcode &op = _parserOpcodeTable[opcode]; + + // Safety check for end of data. + if (!checkDataOffset(dataptr, op.values)) { + result = update_stopChannel(channel, dataptr); + break; + } + + debugC(9, kDebugLevelSound, "Calling opcode '%s' (%d) (channel: %d)", op.name, opcode, _curChannel); + + dataptr += op.values; + result = (this->*(op.function))(channel, dataptr - op.values); + } else { + // Safety check for end of data. + if (!checkDataOffset(dataptr, 1)) { + result = update_stopChannel(channel, dataptr); + break; + } + + uint8 duration = *dataptr++; + debugC(9, kDebugLevelSound, "Note on opcode 0x%02X (duration: %d) (channel: %d)", opcode, duration, _curChannel); + + setupNote(opcode, channel); + noteOn(channel); + setupDuration(duration, channel); + // We need to make sure we are always running the + // effects after this. Otherwise some sounds are + // wrong. Like the sfx when bumping into a wall in + // LoL. + result = duration != 0; + } + } + + if (result == 1) { + if (channel.primaryEffect) + (this->*(channel.primaryEffect))(channel); + if (channel.secondaryEffect) + (this->*(channel.secondaryEffect))(channel); + } + } +} + +// + +void AdLibDriver::resetAdLibState() { + debugC(9, kDebugLevelSound, "resetAdLibState()"); + _rnd = 0x1234; + + // Authorize the control of the waveforms + writeOPL(0x01, 0x20); + + // Select FM music mode + writeOPL(0x08, 0x00); + + // I would guess the main purpose of this is to turn off the rhythm, + // thus allowing us to use 9 melodic voices instead of 6. + writeOPL(0xBD, 0x00); + + initChannel(_channels[9]); + for (int loop = 8; loop >= 0; loop--) { + // Silence the channel + writeOPL(0x40 + _regOffset[loop], 0x3F); + writeOPL(0x43 + _regOffset[loop], 0x3F); + initChannel(_channels[loop]); + } } // Old calling style: output0x388(0xABCD) // New calling style: writeOPL(0xAB, 0xCD) -void AdlibDriver::writeOPL(byte reg, byte val) { - opl->write(reg, val); +void AdLibDriver::writeOPL(byte reg, byte val) { + // _adlib->writeReg(reg, val); + _opl->write(reg, val); } -void AdlibDriver::initChannel(Channel &channel) { - debugC(9, kDebugLevelSound, "initChannel(%lu)", (long)(&channel - _channels)); - memset(&channel.dataptr, 0, sizeof(Channel) - ((char*)&channel.dataptr - (char*)&channel)); +void AdLibDriver::initChannel(Channel &channel) { + debugC(9, kDebugLevelSound, "initChannel(%lu)", (long)(&channel - _channels)); + uint8 backupEL2 = channel.opExtraLevel2; + memset(&channel, 0, sizeof(Channel)); - channel.tempo = 0xFF; - channel.priority = 0; - // normally here are nullfuncs but we set 0 for now - channel.primaryEffect = 0; - channel.secondaryEffect = 0; - channel.spacing1 = 1; + channel.opExtraLevel2 = backupEL2; + channel.tempo = 0xFF; + channel.priority = 0; + // normally here are nullfuncs but we set nullptr for now + channel.primaryEffect = nullptr; + channel.secondaryEffect = nullptr; + channel.spacing1 = 1; + channel.lock = false; + channel.repeating = false; } -void AdlibDriver::noteOff(Channel &channel) { - debugC(9, kDebugLevelSound, "noteOff(%lu)", (long)(&channel - _channels)); +void AdLibDriver::noteOff(Channel &channel) { + debugC(9, kDebugLevelSound, "noteOff(%lu)", (long)(&channel - _channels)); - // The control channel has no corresponding Adlib channel + // The control channel has no corresponding AdLib channel - if (_curChannel >= 9) - return; + if (_curChannel >= 9) + return; - // When the rhythm section is enabled, channels 6, 7 and 8 are special. + // When the rhythm section is enabled, channels 6, 7 and 8 are special. - if (_rhythmSectionBits && _curChannel >= 6) - return; + if (_rhythmSectionBits && _curChannel >= 6) + return; - // This means the "Key On" bit will always be 0 - channel.regBx &= 0xDF; + // This means the "Key On" bit will always be 0 + channel.regBx &= 0xDF; - // Octave / F-Number / Key-On - writeOPL(0xB0 + _curChannel, channel.regBx); + // Octave / F-Number / Key-On + writeOPL(0xB0 + _curChannel, channel.regBx); } -void AdlibDriver::unkOutput2(uint8 chan) { - debugC(9, kDebugLevelSound, "unkOutput2(%d)", chan); +void AdLibDriver::initAdlibChannel(uint8 chan) { + debugC(9, kDebugLevelSound, "initAdlibChannel(%d)", chan); - // The control channel has no corresponding Adlib channel + // The control channel has no corresponding AdLib channel - if (chan >= 9) - return; + if (chan >= 9) + return; - // I believe this has to do with channels 6, 7, and 8 being special - // when Adlib's rhythm section is enabled. + // I believe this has to do with channels 6, 7, and 8 being special + // when AdLib's rhythm section is enabled. - if (_rhythmSectionBits && chan >= 6) - return; + if (_rhythmSectionBits && chan >= 6) + return; - uint8 offset = _regOffset[chan]; + uint8 offset = _regOffset[chan]; - // The channel is cleared: First the attack/delay rate, then the - // sustain level/release rate, and finally the note is turned off. + // The channel is cleared: First the attack/delay rate, then the + // sustain level/release rate, and finally the note is turned off. - writeOPL(0x60 + offset, 0xFF); - writeOPL(0x63 + offset, 0xFF); + writeOPL(0x60 + offset, 0xFF); + writeOPL(0x63 + offset, 0xFF); - writeOPL(0x80 + offset, 0xFF); - writeOPL(0x83 + offset, 0xFF); + writeOPL(0x80 + offset, 0xFF); + writeOPL(0x83 + offset, 0xFF); - writeOPL(0xB0 + chan, 0x00); + writeOPL(0xB0 + chan, 0x00); - // ...and then the note is turned on again, with whatever value is - // still lurking in the A0 + chan register, but everything else - - // including the two most significant frequency bit, and the octave - - // set to zero. - // - // This is very strange behaviour, and causes problems with the ancient - // FMOPL code we borrowed from AdPlug. I've added a workaround. See - // fmopl.cpp for more details. - // - // More recent versions of the MAME FMOPL don't seem to have this - // problem, but cannot currently be used because of licensing and - // performance issues. - // - // Ken Silverman's Adlib emulator (which can be found on his Web page - - // http://www.advsys.net/ken - and as part of AdPlug) also seems to be - // immune, but is apparently not as feature complete as MAME's. + // ...and then the note is turned on again, with whatever value is + // still lurking in the A0 + chan register, but everything else - + // including the two most significant frequency bit, and the octave - + // set to zero. + // + // This is very strange behavior, and causes problems with the ancient + // FMOPL code we borrowed from AdPlug. I've added a workaround. See + // audio/softsynth/opl/mame.cpp for more details. + // + // Fortunately, the more modern DOSBox FMOPL code does not seem to have + // any trouble with this. - writeOPL(0xB0 + chan, 0x20); + writeOPL(0xB0 + chan, 0x20); } // I believe this is a random number generator. It actually does seem to // generate an even distribution of almost all numbers from 0 through 65535, // though in my tests some numbers were never generated. -uint16 AdlibDriver::getRandomNr() { - _rnd += 0x9248; - uint16 lowBits = _rnd & 7; - _rnd >>= 3; - _rnd |= (lowBits << 13); - return _rnd; +uint16 AdLibDriver::getRandomNr() { + _rnd += 0x9248; + uint16 lowBits = _rnd & 7; + _rnd >>= 3; + _rnd |= (lowBits << 13); + return _rnd; } -void AdlibDriver::setupDuration(uint8 duration, Channel &channel) { - debugC(9, kDebugLevelSound, "setupDuration(%d, %lu)", duration, (long)(&channel - _channels)); - if (channel.durationRandomness) { - channel.duration = duration + (getRandomNr() & channel.durationRandomness); - return; - } - if (channel.fractionalSpacing) { - channel.spacing2 = (duration >> 3) * channel.fractionalSpacing; - } - channel.duration = duration; +void AdLibDriver::setupDuration(uint8 duration, Channel &channel) { + debugC(9, kDebugLevelSound, "setupDuration(%d, %lu)", duration, (long)(&channel - _channels)); + if (channel.durationRandomness) { + channel.duration = duration + (getRandomNr() & channel.durationRandomness); + return; + } + if (channel.fractionalSpacing) + channel.spacing2 = (duration >> 3) * channel.fractionalSpacing; + channel.duration = duration; } // This function may or may not play the note. It's usually followed by a call // to noteOn(), which will always play the current note. -void AdlibDriver::setupNote(uint8 rawNote, Channel &channel, bool flag) { - debugC(9, kDebugLevelSound, "setupNote(%d, %lu)", rawNote, (long)(&channel - _channels)); +void AdLibDriver::setupNote(uint8 rawNote, Channel &channel, bool flag) { + debugC(9, kDebugLevelSound, "setupNote(%d, %lu)", rawNote, (long)(&channel - _channels)); - channel.rawNote = rawNote; + if (_curChannel >= 9) + return; - int8 note = (rawNote & 0x0F) + channel.baseNote; - int8 octave = ((rawNote + channel.baseOctave) >> 4) & 0x0F; + channel.rawNote = rawNote; - // There are only twelve notes. If we go outside that, we have to - // adjust the note and octave. + int8 note = (rawNote & 0x0F) + channel.baseNote; + int8 octave = ((rawNote + channel.baseOctave) >> 4) & 0x0F; - if (note >= 12) { - note -= 12; - octave++; - } else if (note < 0) { - note += 12; - octave--; - } + // There are only twelve notes. If we go outside that, we have to + // adjust the note and octave. - // The calculation of frequency looks quite different from the original - // disassembly at a first glance, but when you consider that the - // largest possible value would be 0x0246 + 0xFF + 0x47 (and that's if - // baseFreq is unsigned), freq is still a 10-bit value, just as it - // should be to fit in the Ax and Bx registers. - // - // If it were larger than that, it could have overflowed into the - // octave bits, and that could possibly have been used in some sound. - // But as it is now, I can't see any way it would happen. + if (note >= 12) { + octave += note / 12; + note %= 12; + } else if (note < 0) { + int8 octaves = -(note + 1) / 12 + 1; + octave -= octaves; + note += 12 * octaves; + } - uint16 freq = _unkTable[note] + channel.baseFreq; + // The calculation of frequency looks quite different from the original + // disassembly at a first glance, but when you consider that the + // largest possible value would be 0x0246 + 0xFF + 0x47 (and that's if + // baseFreq is unsigned), freq is still a 10-bit value, just as it + // should be to fit in the Ax and Bx registers. + // + // If it were larger than that, it could have overflowed into the + // octave bits, and that could possibly have been used in some sound. + // But as it is now, I can't see any way it would happen. + + uint16 freq = _freqTable[note] + channel.baseFreq; + + // When called from callback 41, the behavior is slightly different: + // We adjust the frequency, even when channel.pitchBend is 0. + + if (channel.pitchBend || flag) { + const uint8 *table; + // For safety, limit the values used to index the tables. + uint8 indexNote = CLIP(rawNote & 0x0F, 0, 11); + + if (channel.pitchBend >= 0) { + table = _pitchBendTables[indexNote + 2]; + freq += table[CLIP(+channel.pitchBend, 0, 31)]; + } else { + table = _pitchBendTables[indexNote]; + freq -= table[CLIP(-channel.pitchBend, 0, 31)]; + } + } - // When called from callback 41, the behaviour is slightly different: - // We adjust the frequency, even when channel.unk16 is 0. + // Shift octave to correct bit position and limit to valid range. + octave = CLIP(octave, 0, 7) << 2; - if (channel.unk16 || flag) { - const uint8 *table; + // Update octave & frequency, but keep on/off state. + channel.regAx = freq & 0xFF; + channel.regBx = (channel.regBx & 0x20) | octave | ((freq >> 8) & 0x03); - if (channel.unk16 >= 0) { - table = _unkTables[(channel.rawNote & 0x0F) + 2]; - freq += table[channel.unk16]; - } else { - table = _unkTables[channel.rawNote & 0x0F]; - freq -= table[-channel.unk16]; - } - } + writeOPL(0xA0 + _curChannel, channel.regAx); + writeOPL(0xB0 + _curChannel, channel.regBx); +} - channel.regAx = freq & 0xFF; - channel.regBx = (channel.regBx & 0x20) | (octave << 2) | ((freq >> 8) & 0x03); +void AdLibDriver::setupInstrument(uint8 regOffset, const uint8 *dataptr, Channel &channel) { + debugC(9, kDebugLevelSound, "setupInstrument(%d, %p, %lu)", regOffset, (const void *)dataptr, (long)(&channel - _channels)); - // Keep the note on or off - writeOPL(0xA0 + _curChannel, channel.regAx); - writeOPL(0xB0 + _curChannel, channel.regBx); -} + if (_curChannel >= 9) + return; -void AdlibDriver::setupInstrument(uint8 regOffset, uint8 *dataptr, Channel &channel) { - debugC(9, kDebugLevelSound, "setupInstrument(%d, %p, %lu)", regOffset, (const void *)dataptr, (long)(&channel - _channels)); - // Amplitude Modulation / Vibrato / Envelope Generator Type / - // Keyboard Scaling Rate / Modulator Frequency Multiple - writeOPL(0x20 + regOffset, *dataptr++); - writeOPL(0x23 + regOffset, *dataptr++); + // Safety check: need 11 bytes of data. + if (!checkDataOffset(dataptr, 11)) + return; - uint8 temp = *dataptr++; + // Amplitude Modulation / Vibrato / Envelope Generator Type / + // Keyboard Scaling Rate / Modulator Frequency Multiple + writeOPL(0x20 + regOffset, *dataptr++); + writeOPL(0x23 + regOffset, *dataptr++); - // Feedback / Algorithm + uint8 temp = *dataptr++; - // It is very likely that _curChannel really does refer to the same - // channel as regOffset, but there's only one Cx register per channel. + // Feedback / Algorithm - writeOPL(0xC0 + _curChannel, temp); + // It is very likely that _curChannel really does refer to the same + // channel as regOffset, but there's only one Cx register per channel. - // The algorithm bit. I don't pretend to understand this fully, but - // "If set to 0, operator 1 modulates operator 2. In this case, - // operator 2 is the only one producing sound. If set to 1, both - // operators produce sound directly. Complex sounds are more easily - // created if the algorithm is set to 0." + writeOPL(0xC0 + _curChannel, temp); - channel.twoChan = temp & 1; + // The algorithm bit. I don't pretend to understand this fully, but + // "If set to 0, operator 1 modulates operator 2. In this case, + // operator 2 is the only one producing sound. If set to 1, both + // operators produce sound directly. Complex sounds are more easily + // created if the algorithm is set to 0." - // Waveform Select - writeOPL(0xE0 + regOffset, *dataptr++); - writeOPL(0xE3 + regOffset, *dataptr++); + channel.twoChan = temp & 1; - channel.opLevel1 = *dataptr++; - channel.opLevel2 = *dataptr++; + // Waveform Select + writeOPL(0xE0 + regOffset, *dataptr++); + writeOPL(0xE3 + regOffset, *dataptr++); - // Level Key Scaling / Total Level - writeOPL(0x40 + regOffset, calculateOpLevel1(channel)); - writeOPL(0x43 + regOffset, calculateOpLevel2(channel)); + channel.opLevel1 = *dataptr++; + channel.opLevel2 = *dataptr++; - // Attack Rate / Decay Rate - writeOPL(0x60 + regOffset, *dataptr++); - writeOPL(0x63 + regOffset, *dataptr++); + // Level Key Scaling / Total Level + writeOPL(0x40 + regOffset, calculateOpLevel1(channel)); + writeOPL(0x43 + regOffset, calculateOpLevel2(channel)); - // Sustain Level / Release Rate - writeOPL(0x80 + regOffset, *dataptr++); - writeOPL(0x83 + regOffset, *dataptr++); + // Attack Rate / Decay Rate + writeOPL(0x60 + regOffset, *dataptr++); + writeOPL(0x63 + regOffset, *dataptr++); + + // Sustain Level / Release Rate + writeOPL(0x80 + regOffset, *dataptr++); + writeOPL(0x83 + regOffset, *dataptr++); } // Apart from playing the note, this function also updates the variables for -// primary effect 2. +// the vibrato primary effect. + +void AdLibDriver::noteOn(Channel &channel) { + debugC(9, kDebugLevelSound, "noteOn(%lu)", (long)(&channel - _channels)); -void AdlibDriver::noteOn(Channel &channel) { - debugC(9, kDebugLevelSound, "noteOn(%lu)", (long)(&channel - _channels)); + // The "note on" bit is set, and the current note is played. - // The "note on" bit is set, and the current note is played. + if (_curChannel >= 9) + return; - channel.regBx |= 0x20; - writeOPL(0xB0 + _curChannel, channel.regBx); + channel.regBx |= 0x20; + writeOPL(0xB0 + _curChannel, channel.regBx); - int8 shift = 9 - channel.unk33; - uint16 temp = channel.regAx | (channel.regBx << 8); - channel.unk37 = ((temp & 0x3FF) >> shift) & 0xFF; - channel.unk38 = channel.unk36; + // Update vibrato effect variables: vibratoStep is set to a + // vibratoStepRange+1-bit value proportional to the note's f-number. + // Reinitialize delay countdown; vibratoStepsCountdown reinitialization omitted. + int8 shift = 9 - CLIP(channel.vibratoStepRange, 0, 9); + uint16 freq = ((channel.regBx << 8) | channel.regAx) & 0x3FF; + channel.vibratoStep = (freq >> shift) & 0xFF; + channel.vibratoDelayCountdown = channel.vibratoDelay; } -void AdlibDriver::adjustVolume(Channel &channel) { - debugC(9, kDebugLevelSound, "adjustVolume(%lu)", (long)(&channel - _channels)); - // Level Key Scaling / Total Level +void AdLibDriver::adjustVolume(Channel &channel) { + debugC(9, kDebugLevelSound, "adjustVolume(%lu)", (long)(&channel - _channels)); + + if (_curChannel >= 9) + return; - writeOPL(0x43 + _regOffset[_curChannel], calculateOpLevel2(channel)); - if (channel.twoChan) - writeOPL(0x40 + _regOffset[_curChannel], calculateOpLevel1(channel)); + // Level Key Scaling / Total Level + + writeOPL(0x43 + _regOffset[_curChannel], calculateOpLevel2(channel)); + if (channel.twoChan) + writeOPL(0x40 + _regOffset[_curChannel], calculateOpLevel1(channel)); } // This is presumably only used for some sound effects, e.g. Malcolm blowing up // the trees in the intro (but not the effect where he "booby-traps" the big // tree) and turning Kallak to stone. Related functions and variables: // -// update_setupPrimaryEffect1() -// - Initialises unk29, unk30 and unk31 -// - unk29 is not further modified -// - unk30 is not further modified, except by update_removePrimaryEffect1() +// update_setupPrimaryEffectSlide() +// - Initializes slideTempo, slideStep and slideTimer +// - slideTempo is not further modified +// - slideStep is not further modified, except by update_removePrimaryEffectSlide() // -// update_removePrimaryEffect1() -// - Deinitialises unk30 +// update_removePrimaryEffectSlide() +// - Deinitializes slideStep // -// unk29 - determines how often the notes are played -// unk30 - modifies the frequency -// unk31 - determines how often the notes are played - -void AdlibDriver::primaryEffect1(Channel &channel) { - debugC(9, kDebugLevelSound, "Calling primaryEffect1 (channel: %d)", _curChannel); - uint8 temp = channel.unk31; - channel.unk31 += channel.unk29; - if (channel.unk31 >= temp) - return; - - // Initialise unk1 to the current frequency - uint16 unk1 = ((channel.regBx & 3) << 8) | channel.regAx; - - // This is presumably to shift the "note on" bit so far to the left - // that it won't be affected by any of the calculations below. - uint16 unk2 = ((channel.regBx & 0x20) << 8) | (channel.regBx & 0x1C); - - int16 unk3 = (int16)channel.unk30; - - if (unk3 >= 0) { - unk1 += unk3; - if (unk1 >= 734) { - // The new frequency is too high. Shift it down and go - // up one octave. - unk1 >>= 1; - if (!(unk1 & 0x3FF)) - ++unk1; - unk2 = (unk2 & 0xFF00) | ((unk2 + 4) & 0xFF); - unk2 &= 0xFF1C; - } - } else { - unk1 += unk3; - if (unk1 < 388) { - // The new frequency is too low. Shift it up and go - // down one octave. - unk1 <<= 1; - if (!(unk1 & 0x3FF)) - --unk1; - unk2 = (unk2 & 0xFF00) | ((unk2 - 4) & 0xFF); - unk2 &= 0xFF1C; - } - } - - // Make sure that the new frequency is still a 10-bit value. - unk1 &= 0x3FF; - - writeOPL(0xA0 + _curChannel, unk1 & 0xFF); - channel.regAx = unk1 & 0xFF; - - // Shift down the "note on" bit again. - uint8 value = unk1 >> 8; - value |= (unk2 >> 8) & 0xFF; - value |= unk2 & 0xFF; - - writeOPL(0xB0 + _curChannel, value); - channel.regBx = value; +// slideTempo - determines how often the frequency is updated +// slideStep - amount the frequency changes each update +// slideTimer - keeps track of time + +void AdLibDriver::primaryEffectSlide(Channel &channel) { + debugC(9, kDebugLevelSound, "Calling primaryEffectSlide (channel: %d)", _curChannel); + + if (_curChannel >= 9) + return; + + // Time for next frequency update? + if (!advance(channel.slideTimer, channel.slideTempo)) + return; + + // Extract current frequency, (shifted) octave, and "note on" bit into + // separate variable so calculations can't overflow into other fields. + int16 freq = ((channel.regBx & 0x03) << 8) | channel.regAx; + uint8 octave = channel.regBx & 0x1C; + uint8 note_on = channel.regBx & 0x20; + + // Limit slideStep to prevent integer overflow. + freq += CLIP(channel.slideStep, -0x3FF, 0x3FF); + + if (channel.slideStep >= 0 && freq >= 734) { + // The new frequency is too high. Shift it down and go + // up one octave. + freq >>= 1; + if (!(freq & 0x3FF)) + ++freq; + octave += 4; + } else if (channel.slideStep < 0 && freq < 388) { + // Safety check: a negative frequency triggers undefined + // behavior for the left shift operator below. + if (freq < 0) + freq = 0; + + // The new frequency is too low. Shift it up and go + // down one octave. + freq <<= 1; + if (!(freq & 0x3FF)) + --freq; + octave -= 4; + } + + // Set new frequency and octave. + channel.regAx = freq & 0xFF; + channel.regBx = note_on | (octave & 0x1C) | ((freq >> 8) & 0x03); + + writeOPL(0xA0 + _curChannel, channel.regAx); + writeOPL(0xB0 + _curChannel, channel.regBx); } // This is presumably only used for some sound effects, e.g. Malcolm entering // and leaving Kallak's hut. Related functions and variables: // -// update_setupPrimaryEffect2() -// - Initialises unk32, unk33, unk34, unk35 and unk36 -// - unk32 is not further modified -// - unk33 is not further modified -// - unk34 is a countdown that gets reinitialised to unk35 on zero -// - unk35 is based on unk34 and not further modified -// - unk36 is not further modified +// update_setupPrimaryEffectVibrato() +// - Initializes vibratoTempo, vibratoStepRange, vibratoStepsCountdown, +// vibratoNumSteps, and vibratoDelay +// - vibratoTempo is not further modified +// - vibratoStepRange is not further modified +// - vibratoStepsCountdown is a countdown that gets reinitialized to +// vibratoNumSteps on zero, but is initially only half as much +// - vibratoNumSteps is not further modified +// - vibratoDelay is not further modified // // noteOn() // - Plays the current note -// - Updates unk37 with a new (lower?) frequency -// - Copies unk36 to unk38. The unk38 variable is a countdown. +// - Sets vibratoStep depending on vibratoStepRange and the note's f-number +// - Initializes vibratoDelayCountdown with vibratoDelay // -// unk32 - determines how often the notes are played -// unk33 - modifies the frequency -// unk34 - countdown, updates frequency on zero -// unk35 - initialiser for unk34 countdown -// unk36 - initialiser for unk38 countdown -// unk37 - frequency -// unk38 - countdown, begins playing on zero -// unk41 - determines how often the notes are played +// vibratoTempo - determines how often the frequency is updated +// vibratoStepRange - determines frequency step size depending on f-number +// vibratoStepsCountdown - reverses slide direction on zero +// vibratoNumSteps - initializer for vibratoStepsCountdown countdown +// vibratoDelay - initializer for vibratoDelayCountdown +// vibratoStep - amount the frequency changes each update +// vibratoDelayCountdown - effect starts when it reaches zero +// vibratoTimer - keeps track of time // -// Note that unk41 is never initialised. Not that it should matter much, but it -// is a bit sloppy. - -void AdlibDriver::primaryEffect2(Channel &channel) { - debugC(9, kDebugLevelSound, "Calling primaryEffect2 (channel: %d)", _curChannel); - if (channel.unk38) { - --channel.unk38; - return; - } - - uint8 temp = channel.unk41; - channel.unk41 += channel.unk32; - if (channel.unk41 < temp) { - uint16 unk1 = channel.unk37; - if (!(--channel.unk34)) { - unk1 ^= 0xFFFF; - ++unk1; - channel.unk37 = unk1; - channel.unk34 = channel.unk35; - } - - uint16 unk2 = (channel.regAx | (channel.regBx << 8)) & 0x3FF; - unk2 += unk1; - - channel.regAx = unk2 & 0xFF; - channel.regBx = (channel.regBx & 0xFC) | (unk2 >> 8); - - // Octave / F-Number / Key-On - writeOPL(0xA0 + _curChannel, channel.regAx); - writeOPL(0xB0 + _curChannel, channel.regBx); - } -} - -// I don't know where this is used. The same operation is performed several -// times on the current channel, using a chunk of the _soundData[] buffer for -// parameters. The parameters are used starting at the end of the chunk. +// Note that vibratoTimer is never initialized. Not that it should matter much, +// but it is a bit sloppy. Also vibratoStepsCountdown should be reset to its +// initial value in noteOn() but isn't. + +void AdLibDriver::primaryEffectVibrato(Channel &channel) { + debugC(9, kDebugLevelSound, "Calling primaryEffectVibrato (channel: %d)", _curChannel); + + if (_curChannel >= 9) + return; + + // When a new note is played the effect doesn't start immediately. + if (channel.vibratoDelayCountdown) { + --channel.vibratoDelayCountdown; + return; + } + + // Time for an update? + if (advance(channel.vibratoTimer, channel.vibratoTempo)) { + // Reverse direction every vibratoNumSteps updates + if (!(--channel.vibratoStepsCountdown)) { + channel.vibratoStep = -channel.vibratoStep; + channel.vibratoStepsCountdown = channel.vibratoNumSteps; + } + + // Update frequency. + uint16 freq = ((channel.regBx << 8) | channel.regAx) & 0x3FF; + freq += channel.vibratoStep; + + channel.regAx = freq & 0xFF; + channel.regBx = (channel.regBx & 0xFC) | (freq >> 8); + + // Octave / F-Number / Key-On + writeOPL(0xA0 + _curChannel, channel.regAx); + writeOPL(0xB0 + _curChannel, channel.regBx); + } +} + +// I don't know where this is used. An OPL register is regularly updated +// with data from a chunk of the _soundData[] buffer, i.e., one instrument +// parameter register is modulated with data from the chunk. The data is +// reused repeatedly starting from the end of the chunk. // // Since we use _curRegOffset to specify the final register, it's quite // unlikely that this function is ever used to play notes. It's probably only @@ -1247,1344 +1341,1608 @@ void AdlibDriver::primaryEffect2(Channel &channel) { // Related functions and variables: // // update_setupSecondaryEffect1() -// - Initialies unk18, unk19, unk20, unk21, unk22 and offset -// - unk19 is not further modified -// - unk20 is not further modified -// - unk22 is not further modified -// - offset is not further modified +// - Initialies secondaryEffectTimer, secondaryEffectTempo, +// secondaryEffectSize, secondaryEffectPos, secondaryEffectRegbase, +// and secondaryEffectData +// - secondaryEffectTempo is not further modified +// - secondaryEffectSize is not further modified +// - secondaryEffectRegbase is not further modified +// - secondaryEffectData is not further modified // -// unk18 - determines how often the operation is performed -// unk19 - determines how often the operation is performed -// unk20 - the start index into the data chunk -// unk21 - the current index into the data chunk -// unk22 - the operation to perform -// offset - the offset to the data chunk - -void AdlibDriver::secondaryEffect1(Channel &channel) { - debugC(9, kDebugLevelSound, "Calling secondaryEffect1 (channel: %d)", _curChannel); - uint8 temp = channel.unk18; - channel.unk18 += channel.unk19; - if (channel.unk18 < temp) { - if (--channel.unk21 < 0) { - channel.unk21 = channel.unk20; - } - writeOPL(channel.unk22 + _curRegOffset, _soundData[channel.offset + channel.unk21]); - } +// secondaryEffectTimer - keeps track of time +// secondaryEffectTempo - determines how often the operation is performed +// secondaryEffectSize - the size of the data chunk +// secondaryEffectPos - the current index into the data chunk +// secondaryEffectRegbase - the operation to perform +// secondaryEffectData - the offset of the data chunk + +void AdLibDriver::secondaryEffect1(Channel &channel) { + debugC(9, kDebugLevelSound, "Calling secondaryEffect1 (channel: %d)", _curChannel); + + if (_curChannel >= 9) + return; + + if (advance(channel.secondaryEffectTimer, channel.secondaryEffectTempo)) { + if (--channel.secondaryEffectPos < 0) + channel.secondaryEffectPos = channel.secondaryEffectSize; + writeOPL(channel.secondaryEffectRegbase + _curRegOffset, + _soundData[channel.secondaryEffectData + channel.secondaryEffectPos]); + } } -uint8 AdlibDriver::calculateOpLevel1(Channel &channel) { - int8 value = channel.opLevel1 & 0x3F; +uint8 AdLibDriver::calculateOpLevel1(Channel &channel) { + uint8 value = channel.opLevel1 & 0x3F; - if (channel.twoChan) { - value += channel.opExtraLevel1; - value += channel.opExtraLevel2; - value += channel.opExtraLevel3; - } + if (channel.twoChan) { + value += channel.opExtraLevel1; + value += channel.opExtraLevel2; - // Preserve the scaling level bits from opLevel1 + uint16 level3 = (channel.opExtraLevel3 ^ 0x3F) * channel.volumeModifier; + if (level3) { + level3 += 0x3F; + level3 >>= 8; + } - return checkValue(value) | (channel.opLevel1 & 0xC0); -} + value += level3 ^ 0x3F; + } + + // The clipping as signed instead of unsigned caused very ugly noises in LOK when the music + // was fading out in certain situations (bug #11303). The bug seems to come to surface only + // when the volume is not set to the maximum. + // I have confirmed that the noise bug also appears in LOL floppy (Westwood logo sound). It has + // been reported to be present in EOB 1 (intro music), but I haven't been able to confirm it. + // The original AdLib drivers all do the same wrong clipping. At least in the original EOB and + // LOK games this wouldn't cause issues, since the original drivers (and games) do not have + // volume settings and use a simpler calculation of the total level (just adding the three + // opExtraLevels to the opLevel). + // The later (HOF/LOL) original drivers do the same wrong clipping, too. But original LOL floppy + // doesn't have volume settings either. And with max volume the logo sound is okay... + if (value & 0x80) + debugC(3, kDebugLevelSound, "AdLibDriver::calculateOpLevel1(): WORKAROUND - total level clipping uint/int bug encountered"); + value = CLIP(value, 0, 0x3F); + + if (!channel.volumeModifier) + value = 0x3F; + + // Preserve the scaling level bits from opLevel1 + return value | (channel.opLevel1 & 0xC0); +} + +uint8 AdLibDriver::calculateOpLevel2(Channel &channel) { + uint8 value = channel.opLevel2 & 0x3F; + + value += channel.opExtraLevel1; + value += channel.opExtraLevel2; + + uint16 level3 = (channel.opExtraLevel3 ^ 0x3F) * channel.volumeModifier; + if (level3) { + level3 += 0x3F; + level3 >>= 8; + } -uint8 AdlibDriver::calculateOpLevel2(Channel &channel) { - int8 value = channel.opLevel2 & 0x3F; + value += level3 ^ 0x3F; - value += channel.opExtraLevel1; - value += channel.opExtraLevel2; - value += channel.opExtraLevel3; + // See comment in calculateOpLevel1() + if (value & 0x80) + debugC(3, kDebugLevelSound, "AdLibDriver::calculateOpLevel2(): WORKAROUND - total level clipping uint/int bug encountered"); + value = CLIP(value, 0, 0x3F); - // Preserve the scaling level bits from opLevel2 + if (!channel.volumeModifier) + value = 0x3F; - return checkValue(value) | (channel.opLevel2 & 0xC0); + // Preserve the scaling level bits from opLevel2 + return value | (channel.opLevel2 & 0xC0); } // parser opcodes -int AdlibDriver::update_setRepeat(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.repeatCounter = value; - return 0; +int AdLibDriver::update_setRepeat(Channel &channel, const uint8 *values) { + channel.repeatCounter = values[0]; + return 0; } -int AdlibDriver::update_checkRepeat(uint8 *&dataptr, Channel &channel, uint8 value) { - ++dataptr; - if (--channel.repeatCounter) { - int16 add = READ_LE_UINT16(dataptr - 2); - dataptr += add; - } - return 0; +int AdLibDriver::update_checkRepeat(Channel &channel, const uint8 *values) { + if (--channel.repeatCounter) { + int16 add = READ_LE_UINT16(values); + + // Safety check: ignore jump to invalid address + if (!checkDataOffset(channel.dataptr, add)) + warning("AdlibDriver::update_checkRepeat: Ignoring invalid offset %i", add); + else + channel.dataptr += add; + } + return 0; } -int AdlibDriver::update_setupProgram(uint8 *&dataptr, Channel &channel, uint8 value) { - if (value == 0xFF) - return 0; +int AdLibDriver::update_setupProgram(Channel &channel, const uint8 *values) { + if (values[0] == 0xFF) + return 0; + + const uint8 *ptr = getProgram(values[0]); + + // In case we encounter an invalid program we simply ignore it and do + // nothing instead. The original did not care about invalid programs and + // simply tried to play them anyway... But to avoid crashes due we ignore + // them. + // This, for example, happens in the Lands of Lore intro when Scotia gets + // the ring in the intro. + if (!checkDataOffset(ptr, 2)) { + debugC(3, kDebugLevelSound, "AdLibDriver::update_setupProgram: Invalid program %d specified", values[0]); + return 0; + } + + uint8 chan = *ptr++; + uint8 priority = *ptr++; + + // Safety check: ignore programs with invalid channel number. + if (chan > 9) { + warning("AdLibDriver::update_setupProgram: Invalid channel %d", chan); + return 0; + } + + Channel &channel2 = _channels[chan]; + + if (priority >= channel2.priority) { + // The opcode is not allowed to modify its own data pointer. + // To enforce that, we make a copy and restore it later. + // + // This fixes a subtle music bug where the wrong music would + // play when getting the quill in Kyra 1. + const uint8 *dataptrBackUp = channel.dataptr; - uint8 *ptr = getProgram(value); - if (ptr >= _soundData + soundDataSize) { - return 0; - } - uint8 chan = *ptr++; - uint8 priority = *ptr++; + // We keep new tracks from being started for two further iterations of + // the callback. This assures the correct velocity is used for this + // program. + _programStartTimeout = 2; - Channel &channel2 = _channels[chan]; + initChannel(channel2); + channel2.priority = priority; + channel2.dataptr = ptr; + channel2.tempo = 0xFF; + channel2.timer = 0xFF; + channel2.duration = 1; - if (priority >= channel2.priority) { - _flagTrigger = 1; - _flags |= 8; - initChannel(channel2); - channel2.priority = priority; - channel2.dataptr = ptr; - channel2.tempo = 0xFF; - channel2.position = 0xFF; - channel2.duration = 1; - unkOutput2(chan); - } + if (chan <= 5) + channel2.volumeModifier = _musicVolume; + else + channel2.volumeModifier = _sfxVolume; - return 0; + initAdlibChannel(chan); + + channel.dataptr = dataptrBackUp; + } + + return 0; } -int AdlibDriver::update_setNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.spacing1 = value; - return 0; +int AdLibDriver::update_setNoteSpacing(Channel &channel, const uint8 *values) { + channel.spacing1 = values[0]; + return 0; } -int AdlibDriver::update_jump(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - int16 add = READ_LE_UINT16(dataptr); dataptr += 2; - dataptr += add; - return 0; +int AdLibDriver::update_jump(Channel &channel, const uint8 *values) { + int16 add = READ_LE_UINT16(values); + // Safety check: ignore jump to invalid address + if (_version == 1) + channel.dataptr = checkDataOffset(_soundData, add - 191); + else + channel.dataptr = checkDataOffset(channel.dataptr, add); + + if (!channel.dataptr) { + warning("AdlibDriver::update_jump: Invalid offset %i, stopping channel", add); + return update_stopChannel(channel, values); + } + if (_syncJumpMask & (1 << (&channel - _channels))) + channel.lock = true; + if (add < 0) + channel.repeating = true; + return 0; } -int AdlibDriver::update_jumpToSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - int16 add = READ_LE_UINT16(dataptr); dataptr += 2; - channel.dataptrStack[channel.dataptrStackPos++] = dataptr; - dataptr += add; - return 0; +int AdLibDriver::update_jumpToSubroutine(Channel &channel, const uint8 *values) { + int16 add = READ_LE_UINT16(values); + + // Safety checks: ignore jumps when stack is full or address is invalid. + if (channel.dataptrStackPos >= ARRAYSIZE(channel.dataptrStack)) { + warning("AdLibDriver::update_jumpToSubroutine: Stack overflow"); + return 0; + } + channel.dataptrStack[channel.dataptrStackPos++] = channel.dataptr; + if (_version < 3) + channel.dataptr = checkDataOffset(_soundData, add - 191); + else + channel.dataptr = checkDataOffset(channel.dataptr, add); + + if (!channel.dataptr) + channel.dataptr = channel.dataptrStack[--channel.dataptrStackPos]; + return 0; } -int AdlibDriver::update_returnFromSubroutine(uint8 *&dataptr, Channel &channel, uint8 value) { - dataptr = channel.dataptrStack[--channel.dataptrStackPos]; - return 0; +int AdLibDriver::update_returnFromSubroutine(Channel &channel, const uint8 *values) { + // Safety check: stop track when stack is empty. + if (!channel.dataptrStackPos) { + warning("AdLibDriver::update_returnFromSubroutine: Stack underflow"); + return update_stopChannel(channel, values); + } + channel.dataptr = channel.dataptrStack[--channel.dataptrStackPos]; + return 0; } -int AdlibDriver::update_setBaseOctave(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.baseOctave = value; - return 0; +int AdLibDriver::update_setBaseOctave(Channel &channel, const uint8 *values) { + channel.baseOctave = values[0]; + return 0; } -int AdlibDriver::update_stopChannel(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.priority = 0; - if (_curChannel != 9) { - noteOff(channel); - } - dataptr = 0; - return 2; +int AdLibDriver::update_stopChannel(Channel &channel, const uint8 *values) { + channel.priority = 0; + if (_curChannel != 9) + noteOff(channel); + channel.dataptr = nullptr; + return 2; } -int AdlibDriver::update_playRest(uint8 *&dataptr, Channel &channel, uint8 value) { - setupDuration(value, channel); - noteOff(channel); - return (value != 0); +int AdLibDriver::update_playRest(Channel &channel, const uint8 *values) { + setupDuration(values[0], channel); + noteOff(channel); + return values[0] != 0; } -int AdlibDriver::update_writeAdlib(uint8 *&dataptr, Channel &channel, uint8 value) { - writeOPL(value, *dataptr++); - return 0; +int AdLibDriver::update_writeAdLib(Channel &channel, const uint8 *values) { + writeOPL(values[0], values[1]); + return 0; } -int AdlibDriver::update_setupNoteAndDuration(uint8 *&dataptr, Channel &channel, uint8 value) { - setupNote(value, channel); - value = *dataptr++; - setupDuration(value, channel); - return (value != 0); +int AdLibDriver::update_setupNoteAndDuration(Channel &channel, const uint8 *values) { + setupNote(values[0], channel); + setupDuration(values[1], channel); + return values[1] != 0; } -int AdlibDriver::update_setBaseNote(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.baseNote = value; - return 0; +int AdLibDriver::update_setBaseNote(Channel &channel, const uint8 *values) { + channel.baseNote = values[0]; + return 0; } -int AdlibDriver::update_setupSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.unk18 = value; - channel.unk19 = value; - channel.unk20 = channel.unk21 = *dataptr++; - channel.unk22 = *dataptr++; - channel.offset = READ_LE_UINT16(dataptr); dataptr += 2; - channel.secondaryEffect = &AdlibDriver::secondaryEffect1; - return 0; +int AdLibDriver::update_setupSecondaryEffect1(Channel &channel, const uint8 *values) { + channel.secondaryEffectTimer = channel.secondaryEffectTempo = values[0]; + channel.secondaryEffectSize = channel.secondaryEffectPos = values[1]; + channel.secondaryEffectRegbase = values[2]; + // WORKAROUND: The original code reads a true offset which later gets translated via xlat (in + // the current segment). This means that the outcome depends on the sound data offset. + // Unfortunately this offset is different in most implementations of the audio driver and + // probably also different from the offset assumed by the sequencer. + // It seems that the driver assumes an offset of 191 which is wrong for all the game driver + // implementations. + // This bug has probably not been noticed, since the effect is hardly used and the sounds are + // not necessarily worse. I noticed the difference between ScummVM and DOSBox for the EOB II + // teleporter sound. I also found the location of the table which is supposed to be used here + // (simple enough: it is located at the end of the track after the 0x88 ending opcode). + // Teleporters in EOB I and II now sound exactly the same which I am sure was the intended way, + // since the sound data is exactly the same. + // In DOSBox the teleporters will sound different in EOB I and II, due to different sound + // data offsets. + channel.secondaryEffectData = READ_LE_UINT16(&values[3]) - 191; + channel.secondaryEffect = &AdLibDriver::secondaryEffect1; + + // Safety check: don't enable effect when table location is invalid. + int start = channel.secondaryEffectData + channel.secondaryEffectSize; + if (start < 0 || start >= (int)_soundDataSize) { + warning("AdLibDriver::update_setupSecondaryEffect1: Ignoring due to invalid table location"); + channel.secondaryEffect = nullptr; + } + return 0; } -int AdlibDriver::update_stopOtherChannel(uint8 *&dataptr, Channel &channel, uint8 value) { - Channel &channel2 = _channels[value]; - channel2.duration = 0; - channel2.priority = 0; - channel2.dataptr = 0; - return 0; +int AdLibDriver::update_stopOtherChannel(Channel &channel, const uint8 *values) { + // Safety check + if (values[0] > 9) { + warning("AdLibDriver::update_stopOtherChannel: Ignoring invalid channel %d", values[0]); + return 0; + } + + // Don't change our own dataptr! + const uint8 *dataptrBackUp = channel.dataptr; + + Channel &channel2 = _channels[values[0]]; + channel2.duration = 0; + channel2.priority = 0; + channel2.dataptr = nullptr; + + channel.dataptr = dataptrBackUp; + return 0; } -int AdlibDriver::update_waitForEndOfProgram(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 *ptr = getProgram(value); - uint8 chan = *ptr; +int AdLibDriver::update_waitForEndOfProgram(Channel &channel, const uint8 *values) { + const uint8 *ptr = getProgram(values[0]); + + // Safety check in case an invalid program is specified. This would make + // getProgram return a nullptr and thus cause invalid memory reads. + if (!ptr) { + debugC(3, kDebugLevelSound, "AdLibDriver::update_waitForEndOfProgram: Invalid program %d specified", values[0]); + return 0; + } + + uint8 chan = *ptr; - if (!_channels[chan].dataptr) { - return 0; - } + if (chan > 9 || !_channels[chan].dataptr) + return 0; - dataptr -= 2; - return 2; + // AdPlug: Waiting on a repeating channel loops forever. + if (_channels[chan].repeating) + channel.repeating = true; + + channel.dataptr -= 2; + return 2; } -int AdlibDriver::update_setupInstrument(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 *instrument = getInstrument(value); - if (instrument >= _soundData + soundDataSize) { - return 0; - } - setupInstrument(_curRegOffset, instrument, channel); - return 0; +int AdLibDriver::update_setupInstrument(Channel &channel, const uint8 *values) { + const uint8 *instrument = getInstrument(values[0]); + + // We add a safety check to avoid setting up invalid instruments. This is + // not done in the original. However, to avoid crashes due to invalid + // memory reads we simply ignore the request. + // This happens, for example, in Hand of Fate when using the swampsnake + // potion on Zanthia to scare off the rat in the cave in the first chapter + // of the game. + if (!instrument) { + debugC(3, kDebugLevelSound, "AdLibDriver::update_setupInstrument: Invalid instrument %d specified", values[0]); + return 0; + } + + setupInstrument(_curRegOffset, instrument, channel); + return 0; } -int AdlibDriver::update_setupPrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.unk29 = value; - channel.unk30 = READ_BE_UINT16(dataptr); - dataptr += 2; - channel.primaryEffect = &AdlibDriver::primaryEffect1; - channel.unk31 = 0xFF; - return 0; +int AdLibDriver::update_setupPrimaryEffectSlide(Channel &channel, const uint8 *values) { + channel.slideTempo = values[0]; + channel.slideStep = READ_BE_UINT16(&values[1]); + channel.primaryEffect = &AdLibDriver::primaryEffectSlide; + channel.slideTimer = 0xFF; + return 0; } -int AdlibDriver::update_removePrimaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - channel.primaryEffect = 0; - channel.unk30 = 0; - return 0; +int AdLibDriver::update_removePrimaryEffectSlide(Channel &channel, const uint8 *values) { + channel.primaryEffect = nullptr; + channel.slideStep = 0; + return 0; } -int AdlibDriver::update_setBaseFreq(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.baseFreq = value; - return 0; +int AdLibDriver::update_setBaseFreq(Channel &channel, const uint8 *values) { + channel.baseFreq = values[0]; + return 0; } -int AdlibDriver::update_setupPrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.unk32 = value; - channel.unk33 = *dataptr++; - uint8 temp = *dataptr++; - channel.unk34 = temp + 1; - channel.unk35 = temp << 1; - channel.unk36 = *dataptr++; - channel.primaryEffect = &AdlibDriver::primaryEffect2; - return 0; +int AdLibDriver::update_setupPrimaryEffectVibrato(Channel &channel, const uint8 *values) { + channel.vibratoTempo = values[0]; + channel.vibratoStepRange = values[1]; + channel.vibratoStepsCountdown = values[2] + 1; + channel.vibratoNumSteps = values[2] << 1; + channel.vibratoDelay = values[3]; + channel.primaryEffect = &AdLibDriver::primaryEffectVibrato; + return 0; } -int AdlibDriver::update_setPriority(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.priority = value; - return 0; +int AdLibDriver::update_setPriority(Channel &channel, const uint8 *values) { + channel.priority = values[0]; + return 0; } -int AdlibDriver::updateCallback23(uint8 *&dataptr, Channel &channel, uint8 value) { - value >>= 1; - _unkValue1 = _unkValue2 = value; - _unkValue3 = 0xFF; - _unkValue4 = _unkValue5 = 0; - return 0; +// This provides a way to synchronize channels with a global beat: +// +// update_setBeat() +// - Initializes _beatDivider, _beatDivCnt, _beatCounter, and _beatWaiting; +// resets _callbackTimer +// - _beatDivider is not further modified +// +// callback() +// - _beatDivCnt is a countdown, gets reinitialized to _beatDivider on zero +// - _beatCounter is incremented when _beatDivCnt is reset, i.e., it's a +// counter which updates with the global _tempo divided by _beatDivider. +// +// update_waitForNextBeat() +// - _beatWaiting is updated if some bits are 0 in _beatCounter (off beat) +// - the program is stopped until some of the masked bits in _beatCounter +// become 1 and _beatWaiting is non-zero (on beat), then _beatWaiting is +// cleared +// +// _beatDivider - determines how fast _beatCounter is incremented +// _beatDivCnt - countdown for the divider +// _beatCounter - counter updated with global _tempo divided by _beatDivider +// _beatWaiting - flags that waiting started before watched counter bit got 1 +// +// Note that in theory _beatWaiting could wrap around to zero while waiting, +// then the rising edge wouldn't trigger. That's probably not a big issue +// in practice sice it can only happen for long delays (big _beatDivider and +// waiting on one of the higher bits) but could have been prevented easily. + +int AdLibDriver::update_setBeat(Channel &channel, const uint8 *values) { + _beatDivider = _beatDivCnt = values[0] >> 1; + _callbackTimer = 0xFF; + _beatCounter = _beatWaiting = 0; + return 0; } -int AdlibDriver::updateCallback24(uint8 *&dataptr, Channel &channel, uint8 value) { - if (_unkValue5) { - if (_unkValue4 & value) { - _unkValue5 = 0; - return 0; - } - } +int AdLibDriver::update_waitForNextBeat(Channel &channel, const uint8 *values) { + if ((_beatCounter & values[0]) && _beatWaiting) { + _beatWaiting = 0; + return 0; + } - if (!(value & _unkValue4)) { - ++_unkValue5; - } + if (!(_beatCounter & values[0])) + ++_beatWaiting; - dataptr -= 2; - channel.duration = 1; - return 2; + channel.dataptr -= 2; + channel.duration = 1; + return 2; } -int AdlibDriver::update_setExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.opExtraLevel1 = value; - adjustVolume(channel); - return 0; +int AdLibDriver::update_setExtraLevel1(Channel &channel, const uint8 *values) { + channel.opExtraLevel1 = values[0]; + adjustVolume(channel); + return 0; } -int AdlibDriver::update_setupDuration(uint8 *&dataptr, Channel &channel, uint8 value) { - setupDuration(value, channel); - return (value != 0); +int AdLibDriver::update_setupDuration(Channel &channel, const uint8 *values) { + setupDuration(values[0], channel); + return values[0] != 0; } -int AdlibDriver::update_playNote(uint8 *&dataptr, Channel &channel, uint8 value) { - setupDuration(value, channel); - noteOn(channel); - return (value != 0); +int AdLibDriver::update_playNote(Channel &channel, const uint8 *values) { + setupDuration(values[0], channel); + noteOn(channel); + return values[0] != 0; } -int AdlibDriver::update_setFractionalNoteSpacing(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.fractionalSpacing = value & 7; - return 0; +int AdLibDriver::update_setFractionalNoteSpacing(Channel &channel, const uint8 *values) { + channel.fractionalSpacing = values[0] & 7; + return 0; } -int AdlibDriver::update_setTempo(uint8 *&dataptr, Channel &channel, uint8 value) { - _tempo = value; - return 0; +int AdLibDriver::update_setTempo(Channel &channel, const uint8 *values) { + _tempo = values[0]; + return 0; } -int AdlibDriver::update_removeSecondaryEffect1(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - channel.secondaryEffect = 0; - return 0; +int AdLibDriver::update_removeSecondaryEffect1(Channel &channel, const uint8 *values) { + channel.secondaryEffect = nullptr; + return 0; } -int AdlibDriver::update_setChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.tempo = value; - return 0; +int AdLibDriver::update_setChannelTempo(Channel &channel, const uint8 *values) { + channel.tempo = values[0]; + return 0; } -int AdlibDriver::update_setExtraLevel3(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.opExtraLevel3 = value; - return 0; +int AdLibDriver::update_setExtraLevel3(Channel &channel, const uint8 *values) { + channel.opExtraLevel3 = values[0]; + return 0; } -int AdlibDriver::update_setExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) { - int channelBackUp = _curChannel; +int AdLibDriver::update_setExtraLevel2(Channel &channel, const uint8 *values) { + // Safety check + if (values[0] > 9) { + warning("AdLibDriver::update_setExtraLevel2: Ignore invalid channel %d", values[0]); + return 0; + } - _curChannel = value; - Channel &channel2 = _channels[value]; - channel2.opExtraLevel2 = *dataptr++; - adjustVolume(channel2); + int channelBackUp = _curChannel; - _curChannel = channelBackUp; - return 0; + _curChannel = values[0]; + Channel &channel2 = _channels[_curChannel]; + channel2.opExtraLevel2 = values[1]; + adjustVolume(channel2); + + _curChannel = channelBackUp; + return 0; } -int AdlibDriver::update_changeExtraLevel2(uint8 *&dataptr, Channel &channel, uint8 value) { - int channelBackUp = _curChannel; +int AdLibDriver::update_changeExtraLevel2(Channel &channel, const uint8 *values) { + // Safety check + if (values[0] > 9) { + warning("AdLibDriver::update_changeExtraLevel2: Ignore invalid channel %d", values[0]); + return 0; + } + + int channelBackUp = _curChannel; - _curChannel = value; - Channel &channel2 = _channels[value]; - channel2.opExtraLevel2 += *dataptr++; - adjustVolume(channel2); + _curChannel = values[0]; + Channel &channel2 = _channels[_curChannel]; + channel2.opExtraLevel2 += values[1]; + adjustVolume(channel2); - _curChannel = channelBackUp; - return 0; + _curChannel = channelBackUp; + return 0; } -// Apart from initialising to zero, these two functions are the only ones that +// Apart from initializing to zero, these two functions are the only ones that // modify _vibratoAndAMDepthBits. -int AdlibDriver::update_setAMDepth(uint8 *&dataptr, Channel &channel, uint8 value) { - if (value & 1) - _vibratoAndAMDepthBits |= 0x80; - else - _vibratoAndAMDepthBits &= 0x7F; +int AdLibDriver::update_setAMDepth(Channel &channel, const uint8 *values) { + if (values[0] & 1) + _vibratoAndAMDepthBits |= 0x80; + else + _vibratoAndAMDepthBits &= 0x7F; - writeOPL(0xBD, _vibratoAndAMDepthBits); - return 0; + writeOPL(0xBD, _vibratoAndAMDepthBits); + return 0; } -int AdlibDriver::update_setVibratoDepth(uint8 *&dataptr, Channel &channel, uint8 value) { - if (value & 1) - _vibratoAndAMDepthBits |= 0x40; - else - _vibratoAndAMDepthBits &= 0xBF; +int AdLibDriver::update_setVibratoDepth(Channel &channel, const uint8 *values) { + if (values[0] & 1) + _vibratoAndAMDepthBits |= 0x40; + else + _vibratoAndAMDepthBits &= 0xBF; - writeOPL(0xBD, _vibratoAndAMDepthBits); - return 0; + writeOPL(0xBD, _vibratoAndAMDepthBits); + return 0; } -int AdlibDriver::update_changeExtraLevel1(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.opExtraLevel1 += value; - adjustVolume(channel); - return 0; +int AdLibDriver::update_changeExtraLevel1(Channel &channel, const uint8 *values) { + channel.opExtraLevel1 += values[0]; + adjustVolume(channel); + return 0; } -int AdlibDriver::updateCallback38(uint8 *&dataptr, Channel &channel, uint8 value) { - int channelBackUp = _curChannel; +int AdLibDriver::update_clearChannel(Channel &channel, const uint8 *values) { + // Safety check + if (values[0] > 9) { + warning("AdLibDriver::update_clearChannel: Ignore invalid channel %d", values[0]); + return 0; + } - _curChannel = value; - Channel &channel2 = _channels[value]; - channel2.duration = channel2.priority = 0; - channel2.dataptr = 0; - channel2.opExtraLevel2 = 0; + int channelBackUp = _curChannel; + _curChannel = values[0]; + // Don't modify our own dataptr! + const uint8 *dataptrBackUp = channel.dataptr; - if (value != 9) { - uint8 outValue = _regOffset[value]; + // Stop channel + Channel &channel2 = _channels[_curChannel]; + channel2.duration = channel2.priority = 0; + channel2.dataptr = 0; + channel2.opExtraLevel2 = 0; - // Feedback strength / Connection type - writeOPL(0xC0 + _curChannel, 0x00); + if (_curChannel != 9) { + // Silence channel + uint8 regOff = _regOffset[_curChannel]; - // Key scaling level / Operator output level - writeOPL(0x43 + outValue, 0x3F); + // Feedback strength / Connection type + writeOPL(0xC0 + _curChannel, 0x00); - // Sustain Level / Release Rate - writeOPL(0x83 + outValue, 0xFF); + // Key scaling level / Operator output level + writeOPL(0x43 + regOff, 0x3F); - // Key On / Octave / Frequency - writeOPL(0xB0 + _curChannel, 0x00); - } + // Sustain Level / Release Rate + writeOPL(0x83 + regOff, 0xFF); - _curChannel = channelBackUp; - return 0; + // Key On / Octave / Frequency + writeOPL(0xB0 + _curChannel, 0x00); + } + + _curChannel = channelBackUp; + channel.dataptr = dataptrBackUp; + return 0; } -int AdlibDriver::updateCallback39(uint8 *&dataptr, Channel &channel, uint8 value) { - uint16 unk = *dataptr++; - unk |= value << 8; - unk &= getRandomNr(); +int AdLibDriver::update_changeNoteRandomly(Channel &channel, const uint8 *values) { + if (_curChannel >= 9) + return 0; - uint16 unk2 = ((channel.regBx & 0x1F) << 8) | channel.regAx; - unk2 += unk; - unk2 |= ((channel.regBx & 0x20) << 8); + uint16 mask = READ_BE_UINT16(values); - // Frequency - writeOPL(0xA0 + _curChannel, unk2 & 0xFF); + uint16 note = ((channel.regBx & 0x1F) << 8) | channel.regAx; - // Key On / Octave / Frequency - writeOPL(0xB0 + _curChannel, (unk2 & 0xFF00) >> 8); + note += mask & getRandomNr(); + note |= ((channel.regBx & 0x20) << 8); - return 0; -} + // Frequency + writeOPL(0xA0 + _curChannel, note & 0xFF); -int AdlibDriver::update_removePrimaryEffect2(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - channel.primaryEffect = 0; - return 0; -} + // Key On / Octave / Frequency + writeOPL(0xB0 + _curChannel, (note & 0xFF00) >> 8); -int AdlibDriver::updateCallback41(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.unk16 = value; - setupNote(channel.rawNote, channel, true); - return 0; + return 0; } -int AdlibDriver::update_resetToGlobalTempo(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - channel.tempo = _tempo; - return 0; +int AdLibDriver::update_removePrimaryEffectVibrato(Channel &channel, const uint8 *values) { + channel.primaryEffect = nullptr; + return 0; } -int AdlibDriver::update_nop1(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - return 0; +int AdLibDriver::update_pitchBend(Channel &channel, const uint8 *values) { + channel.pitchBend = (int8)values[0]; + setupNote(channel.rawNote, channel, true); + return 0; } -int AdlibDriver::update_setDurationRandomness(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.durationRandomness = value; - return 0; +int AdLibDriver::update_resetToGlobalTempo(Channel &channel, const uint8 *values) { + channel.tempo = _tempo; + return 0; } -int AdlibDriver::update_changeChannelTempo(uint8 *&dataptr, Channel &channel, uint8 value) { - int tempo = channel.tempo + (int8)value; - - if (tempo <= 0) - tempo = 1; - else if (tempo > 255) - tempo = 255; +int AdLibDriver::update_nop(Channel &channel, const uint8 *values) { + return 0; +} - channel.tempo = tempo; - return 0; +int AdLibDriver::update_setDurationRandomness(Channel &channel, const uint8 *values) { + channel.durationRandomness = values[0]; + return 0; } -int AdlibDriver::updateCallback46(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 entry = *dataptr++; - _tablePtr1 = _unkTable2[entry++]; - _tablePtr2 = _unkTable2[entry]; - if (value == 2) { - // Frequency - writeOPL(0xA0, _tablePtr2[0]); - } - return 0; +int AdLibDriver::update_changeChannelTempo(Channel &channel, const uint8 *values) { + channel.tempo = CLIP(channel.tempo + (int8)values[0], 1, 255); + return 0; } -// TODO: This is really the same as update_nop1(), so they should be combined -// into one single update_nop(). +int AdLibDriver::updateCallback46(Channel &channel, const uint8 *values) { + uint8 entry = values[1]; -int AdlibDriver::update_nop2(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - return 0; + // Safety check: prevent illegal table access + if (entry + 2 > _unkTable2Size) + return 0; + + _tablePtr1 = _unkTable2[entry]; + _tablePtr2 = _unkTable2[entry + 1]; + if (values[0] == 2) { + // Frequency + writeOPL(0xA0, _tablePtr2[0]); + } + return 0; } -int AdlibDriver::update_setupRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) { - int channelBackUp = _curChannel; - int regOffsetBackUp = _curRegOffset; +int AdLibDriver::update_setupRhythmSection(Channel &channel, const uint8 *values) { + int channelBackUp = _curChannel; + int regOffsetBackUp = _curRegOffset; - _curChannel = 6; - _curRegOffset = _regOffset[6]; + _curChannel = 6; + _curRegOffset = _regOffset[6]; - setupInstrument(_curRegOffset, getInstrument(value), channel); - _unkValue6 = channel.opLevel2; + const uint8 *instrument; + instrument = getInstrument(values[0]); + if (instrument) { + setupInstrument(_curRegOffset, instrument, channel); + } else { + debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 6 specified", values[0]); + } + _opLevelBD = channel.opLevel2; - _curChannel = 7; - _curRegOffset = _regOffset[7]; + _curChannel = 7; + _curRegOffset = _regOffset[7]; - setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel); - _unkValue7 = channel.opLevel1; - _unkValue8 = channel.opLevel2; + instrument = getInstrument(values[1]); + if (instrument) { + setupInstrument(_curRegOffset, instrument, channel); + } else { + debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 7 specified", values[1]); + } + _opLevelHH = channel.opLevel1; + _opLevelSD = channel.opLevel2; - _curChannel = 8; - _curRegOffset = _regOffset[8]; + _curChannel = 8; + _curRegOffset = _regOffset[8]; - setupInstrument(_curRegOffset, getInstrument(*dataptr++), channel); - _unkValue9 = channel.opLevel1; - _unkValue10 = channel.opLevel2; + instrument = getInstrument(values[2]); + if (instrument) { + setupInstrument(_curRegOffset, instrument, channel); + } else { + debugC(3, kDebugLevelSound, "AdLibDriver::update_setupRhythmSection: Invalid instrument %d for channel 8 specified", values[2]); + } + _opLevelTT = channel.opLevel1; + _opLevelCY = channel.opLevel2; - // Octave / F-Number / Key-On for channels 6, 7 and 8 + // Octave / F-Number / Key-On for channels 6, 7 and 8 - _channels[6].regBx = *dataptr++ & 0x2F; - writeOPL(0xB6, _channels[6].regBx); - writeOPL(0xA6, *dataptr++); + _channels[6].regBx = values[3] & 0x2F; + writeOPL(0xB6, _channels[6].regBx); + writeOPL(0xA6, values[4]); - _channels[7].regBx = *dataptr++ & 0x2F; - writeOPL(0xB7, _channels[7].regBx); - writeOPL(0xA7, *dataptr++); + _channels[7].regBx = values[5] & 0x2F; + writeOPL(0xB7, _channels[7].regBx); + writeOPL(0xA7, values[6]); - _channels[8].regBx = *dataptr++ & 0x2F; - writeOPL(0xB8, _channels[8].regBx); - writeOPL(0xA8, *dataptr++); + _channels[8].regBx = values[7] & 0x2F; + writeOPL(0xB8, _channels[8].regBx); + writeOPL(0xA8, values[8]); - _rhythmSectionBits = 0x20; + _rhythmSectionBits = 0x20; - _curRegOffset = regOffsetBackUp; - _curChannel = channelBackUp; - return 0; + _curRegOffset = regOffsetBackUp; + _curChannel = channelBackUp; + return 0; } -int AdlibDriver::update_playRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) { - // Any instrument that we want to play, and which was already playing, - // is temporarily keyed off. Instruments that were off already, or - // which we don't want to play, retain their old on/off status. This is - // probably so that the instrument's envelope is played from its - // beginning again... +int AdLibDriver::update_playRhythmSection(Channel &channel, const uint8 *values) { + // Any instrument that we want to play, and which was already playing, + // is temporarily keyed off. Instruments that were off already, or + // which we don't want to play, retain their old on/off status. This is + // probably so that the instrument's envelope is played from its + // beginning again... - writeOPL(0xBD, (_rhythmSectionBits & ~(value & 0x1F)) | 0x20); + writeOPL(0xBD, (_rhythmSectionBits & ~(values[0] & 0x1F)) | 0x20); - // ...but since we only set the rhythm instrument bits, and never clear - // them (until the entire rhythm section is disabled), I'm not sure how - // useful the cleverness above is. We could perhaps simply turn off all - // the rhythm instruments instead. + // ...but since we only set the rhythm instrument bits, and never clear + // them (until the entire rhythm section is disabled), I'm not sure how + // useful the cleverness above is. We could perhaps simply turn off all + // the rhythm instruments instead. - _rhythmSectionBits |= value; + _rhythmSectionBits |= values[0]; - writeOPL(0xBD, _vibratoAndAMDepthBits | 0x20 | _rhythmSectionBits); - return 0; + writeOPL(0xBD, _vibratoAndAMDepthBits | 0x20 | _rhythmSectionBits); + return 0; } -int AdlibDriver::update_removeRhythmSection(uint8 *&dataptr, Channel &channel, uint8 value) { - --dataptr; - _rhythmSectionBits = 0; +int AdLibDriver::update_removeRhythmSection(Channel &channel, const uint8 *values) { + _rhythmSectionBits = 0; - // All the rhythm bits are cleared. The AM and Vibrato depth bits - // remain unchanged. + // All the rhythm bits are cleared. The AM and Vibrato depth bits + // remain unchanged. - writeOPL(0xBD, _vibratoAndAMDepthBits); - return 0; + writeOPL(0xBD, _vibratoAndAMDepthBits); + return 0; } -int AdlibDriver::updateCallback51(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 value2 = *dataptr++; +int AdLibDriver::update_setRhythmLevel2(Channel &channel, const uint8 *values) { + uint8 ops = values[0], v = values[1]; - if (value & 1) { - _unkValue12 = value2; + if (ops & 1) { + _opExtraLevel2HH = v; - // Channel 7, op1: Level Key Scaling / Total Level - writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12)); - } + // Channel 7, op1: Level Key Scaling / Total Level + writeOPL(0x51, checkValue(v + _opLevelHH + _opExtraLevel1HH + _opExtraLevel2HH)); + } - if (value & 2) { - _unkValue14 = value2; + if (ops & 2) { + _opExtraLevel2CY = v; - // Channel 8, op2: Level Key Scaling / Total Level - writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14)); - } + // Channel 8, op2: Level Key Scaling / Total Level + writeOPL(0x55, checkValue(v + _opLevelCY + _opExtraLevel1CY + _opExtraLevel2CY)); + } - if (value & 4) { - _unkValue15 = value2; + if (ops & 4) { + _opExtraLevel2TT = v; - // Channel 8, op1: Level Key Scaling / Total Level - writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15)); - } + // Channel 8, op1: Level Key Scaling / Total Level + writeOPL(0x52, checkValue(v + _opLevelTT + _opExtraLevel1TT + _opExtraLevel2TT)); + } - if (value & 8) { - _unkValue18 = value2; + if (ops & 8) { + _opExtraLevel2SD = v; - // Channel 7, op2: Level Key Scaling / Total Level - writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18)); - } + // Channel 7, op2: Level Key Scaling / Total Level + writeOPL(0x54, checkValue(v + _opLevelSD + _opExtraLevel1SD + _opExtraLevel2SD)); + } - if (value & 16) { - _unkValue20 = value2; + if (ops & 16) { + _opExtraLevel2BD = v; - // Channel 6, op2: Level Key Scaling / Total Level - writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20)); - } + // Channel 6, op2: Level Key Scaling / Total Level + writeOPL(0x53, checkValue(v + _opLevelBD + _opExtraLevel1BD + _opExtraLevel2BD)); + } - return 0; + return 0; } -int AdlibDriver::updateCallback52(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 value2 = *dataptr++; +int AdLibDriver::update_changeRhythmLevel1(Channel &channel, const uint8 *values) { + uint8 ops = values[0], v = values[1]; - if (value & 1) { - _unkValue11 = checkValue(value2 + _unkValue7 + _unkValue11 + _unkValue12); + if (ops & 1) { + _opExtraLevel1HH = checkValue(v + _opLevelHH + _opExtraLevel1HH + _opExtraLevel2HH); - // Channel 7, op1: Level Key Scaling / Total Level - writeOPL(0x51, _unkValue11); - } + // Channel 7, op1: Level Key Scaling / Total Level + writeOPL(0x51, _opExtraLevel1HH); + } - if (value & 2) { - _unkValue13 = checkValue(value2 + _unkValue10 + _unkValue13 + _unkValue14); + if (ops & 2) { + _opExtraLevel1CY = checkValue(v + _opLevelCY + _opExtraLevel1CY + _opExtraLevel2CY); - // Channel 8, op2: Level Key Scaling / Total Level - writeOPL(0x55, _unkValue13); - } + // Channel 8, op2: Level Key Scaling / Total Level + writeOPL(0x55, _opExtraLevel1CY); + } - if (value & 4) { - _unkValue16 = checkValue(value2 + _unkValue9 + _unkValue16 + _unkValue15); + if (ops & 4) { + _opExtraLevel1TT = checkValue(v + _opLevelTT + _opExtraLevel1TT + _opExtraLevel2TT); - // Channel 8, op1: Level Key Scaling / Total Level - writeOPL(0x52, _unkValue16); - } + // Channel 8, op1: Level Key Scaling / Total Level + writeOPL(0x52, _opExtraLevel1TT); + } - if (value & 8) { - _unkValue17 = checkValue(value2 + _unkValue8 + _unkValue17 + _unkValue18); + if (ops & 8) { + _opExtraLevel1SD = checkValue(v + _opLevelSD + _opExtraLevel1SD + _opExtraLevel2SD); - // Channel 7, op2: Level Key Scaling / Total Level - writeOPL(0x54, _unkValue17); - } + // Channel 7, op2: Level Key Scaling / Total Level + writeOPL(0x54, _opExtraLevel1SD); + } - if (value & 16) { - _unkValue19 = checkValue(value2 + _unkValue6 + _unkValue19 + _unkValue20); + if (ops & 16) { + _opExtraLevel1BD = checkValue(v + _opLevelBD + _opExtraLevel1BD + _opExtraLevel2BD); - // Channel 6, op2: Level Key Scaling / Total Level - writeOPL(0x53, _unkValue19); - } + // Channel 6, op2: Level Key Scaling / Total Level + writeOPL(0x53, _opExtraLevel1BD); + } - return 0; + return 0; } -int AdlibDriver::updateCallback53(uint8 *&dataptr, Channel &channel, uint8 value) { - uint8 value2 = *dataptr++; +int AdLibDriver::update_setRhythmLevel1(Channel &channel, const uint8 *values) { + uint8 ops = values[0], v = values[1]; - if (value & 1) { - _unkValue11 = value2; + if (ops & 1) { + _opExtraLevel1HH = v; - // Channel 7, op1: Level Key Scaling / Total Level - writeOPL(0x51, checkValue(value2 + _unkValue7 + _unkValue12)); - } + // Channel 7, op1: Level Key Scaling / Total Level + writeOPL(0x51, checkValue(v + _opLevelHH + _opExtraLevel2HH)); + } - if (value & 2) { - _unkValue13 = value2; + if (ops & 2) { + _opExtraLevel1CY = v; - // Channel 8, op2: Level Key Scaling / Total Level - writeOPL(0x55, checkValue(value2 + _unkValue10 + _unkValue14)); - } + // Channel 8, op2: Level Key Scaling / Total Level + writeOPL(0x55, checkValue(v + _opLevelCY + _opExtraLevel2CY)); + } - if (value & 4) { - _unkValue16 = value2; + if (ops & 4) { + _opExtraLevel1TT = v; - // Channel 8, op1: Level Key Scaling / Total Level - writeOPL(0x52, checkValue(value2 + _unkValue9 + _unkValue15)); - } + // Channel 8, op1: Level Key Scaling / Total Level + writeOPL(0x52, checkValue(v + _opLevelTT + _opExtraLevel2TT)); + } - if (value & 8) { - _unkValue17 = value2; + if (ops & 8) { + _opExtraLevel1SD = v; - // Channel 7, op2: Level Key Scaling / Total Level - writeOPL(0x54, checkValue(value2 + _unkValue8 + _unkValue18)); - } + // Channel 7, op2: Level Key Scaling / Total Level + writeOPL(0x54, checkValue(v + _opLevelSD + _opExtraLevel2SD)); + } - if (value & 16) { - _unkValue19 = value2; + if (ops & 16) { + _opExtraLevel1BD = v; - // Channel 6, op2: Level Key Scaling / Total Level - writeOPL(0x53, checkValue(value2 + _unkValue6 + _unkValue20)); - } + // Channel 6, op2: Level Key Scaling / Total Level + writeOPL(0x53, checkValue(v + _opLevelBD + _opExtraLevel2BD)); + } - return 0; + return 0; } -int AdlibDriver::update_setSoundTrigger(uint8 *&dataptr, Channel &channel, uint8 value) { - _soundTrigger = value; - return 0; +int AdLibDriver::update_setSoundTrigger(Channel &channel, const uint8 *values) { + _soundTrigger = values[0]; + return 0; } -int AdlibDriver::update_setTempoReset(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.tempoReset = value; - return 0; +int AdLibDriver::update_setTempoReset(Channel &channel, const uint8 *values) { + channel.tempoReset = values[0]; + return 0; } -int AdlibDriver::updateCallback56(uint8 *&dataptr, Channel &channel, uint8 value) { - channel.unk39 = value; - channel.unk40 = *dataptr++; - return 0; +int AdLibDriver::updateCallback56(Channel &channel, const uint8 *values) { + channel.unk39 = values[0]; + channel.unk40 = values[1]; + return 0; } // static res -#define COMMAND(x) { &AdlibDriver::x, #x } - -void AdlibDriver::setupOpcodeList() { - static const OpcodeEntry opcodeList[] = { - COMMAND(snd_ret0x100), - COMMAND(snd_ret0x1983), - COMMAND(snd_initDriver), - COMMAND(snd_deinitDriver), - COMMAND(snd_setSoundData), - COMMAND(snd_unkOpcode1), - COMMAND(snd_startSong), - COMMAND(snd_unkOpcode2), - COMMAND(snd_unkOpcode3), - COMMAND(snd_readByte), - COMMAND(snd_writeByte), - COMMAND(snd_getSoundTrigger), - COMMAND(snd_unkOpcode4), - COMMAND(snd_dummy), - COMMAND(snd_getNullvar4), - COMMAND(snd_setNullvar3), - COMMAND(snd_setFlag), - COMMAND(snd_clearFlag) - }; - - _opcodeList = opcodeList; - _opcodesEntries = ARRAYSIZE(opcodeList); -} - -void AdlibDriver::setupParserOpcodeTable() { - static const ParserOpcode parserOpcodeTable[] = { - // 0 - COMMAND(update_setRepeat), - COMMAND(update_checkRepeat), - COMMAND(update_setupProgram), - COMMAND(update_setNoteSpacing), - - // 4 - COMMAND(update_jump), - COMMAND(update_jumpToSubroutine), - COMMAND(update_returnFromSubroutine), - COMMAND(update_setBaseOctave), - - // 8 - COMMAND(update_stopChannel), - COMMAND(update_playRest), - COMMAND(update_writeAdlib), - COMMAND(update_setupNoteAndDuration), - - // 12 - COMMAND(update_setBaseNote), - COMMAND(update_setupSecondaryEffect1), - COMMAND(update_stopOtherChannel), - COMMAND(update_waitForEndOfProgram), - - // 16 - COMMAND(update_setupInstrument), - COMMAND(update_setupPrimaryEffect1), - COMMAND(update_removePrimaryEffect1), - COMMAND(update_setBaseFreq), - - // 20 - COMMAND(update_stopChannel), - COMMAND(update_setupPrimaryEffect2), - COMMAND(update_stopChannel), - COMMAND(update_stopChannel), - - // 24 - COMMAND(update_stopChannel), - COMMAND(update_stopChannel), - COMMAND(update_setPriority), - COMMAND(update_stopChannel), - - // 28 - COMMAND(updateCallback23), - COMMAND(updateCallback24), - COMMAND(update_setExtraLevel1), - COMMAND(update_stopChannel), - - // 32 - COMMAND(update_setupDuration), - COMMAND(update_playNote), - COMMAND(update_stopChannel), - COMMAND(update_stopChannel), - - // 36 - COMMAND(update_setFractionalNoteSpacing), - COMMAND(update_stopChannel), - COMMAND(update_setTempo), - COMMAND(update_removeSecondaryEffect1), - - // 40 - COMMAND(update_stopChannel), - COMMAND(update_setChannelTempo), - COMMAND(update_stopChannel), - COMMAND(update_setExtraLevel3), - - // 44 - COMMAND(update_setExtraLevel2), - COMMAND(update_changeExtraLevel2), - COMMAND(update_setAMDepth), - COMMAND(update_setVibratoDepth), - - // 48 - COMMAND(update_changeExtraLevel1), - COMMAND(update_stopChannel), - COMMAND(update_stopChannel), - COMMAND(updateCallback38), - - // 52 - COMMAND(update_stopChannel), - COMMAND(updateCallback39), - COMMAND(update_removePrimaryEffect2), - COMMAND(update_stopChannel), - - // 56 - COMMAND(update_stopChannel), - COMMAND(updateCallback41), - COMMAND(update_resetToGlobalTempo), - COMMAND(update_nop1), - - // 60 - COMMAND(update_setDurationRandomness), - COMMAND(update_changeChannelTempo), - COMMAND(update_stopChannel), - COMMAND(updateCallback46), - - // 64 - COMMAND(update_nop2), - COMMAND(update_setupRhythmSection), - COMMAND(update_playRhythmSection), - COMMAND(update_removeRhythmSection), - - // 68 - COMMAND(updateCallback51), - COMMAND(updateCallback52), - COMMAND(updateCallback53), - COMMAND(update_setSoundTrigger), - - // 72 - COMMAND(update_setTempoReset), - COMMAND(updateCallback56), - COMMAND(update_stopChannel) - }; - - _parserOpcodeTable = parserOpcodeTable; - _parserOpcodeTableSize = ARRAYSIZE(parserOpcodeTable); -} +#define COMMAND(x, n) { &AdLibDriver::x, #x, n } + +const AdLibDriver::ParserOpcode AdLibDriver::_parserOpcodeTable[] = { + // 0 + COMMAND(update_setRepeat, 1), + COMMAND(update_checkRepeat, 2), + COMMAND(update_setupProgram, 1), + COMMAND(update_setNoteSpacing, 1), + + // 4 + COMMAND(update_jump, 2), + COMMAND(update_jumpToSubroutine, 2), + COMMAND(update_returnFromSubroutine, 0), + COMMAND(update_setBaseOctave, 1), + + // 8 + COMMAND(update_stopChannel, 0), + COMMAND(update_playRest, 1), + COMMAND(update_writeAdLib, 2), + COMMAND(update_setupNoteAndDuration, 2), + + // 12 + COMMAND(update_setBaseNote, 1), + COMMAND(update_setupSecondaryEffect1, 5), + COMMAND(update_stopOtherChannel, 1), + COMMAND(update_waitForEndOfProgram, 1), + + // 16 + COMMAND(update_setupInstrument, 1), + COMMAND(update_setupPrimaryEffectSlide, 3), + COMMAND(update_removePrimaryEffectSlide, 0), + COMMAND(update_setBaseFreq, 1), + + // 20 + COMMAND(update_stopChannel, 0), + COMMAND(update_setupPrimaryEffectVibrato, 4), + COMMAND(update_stopChannel, 0), + COMMAND(update_stopChannel, 0), + + // 24 + COMMAND(update_stopChannel, 0), + COMMAND(update_stopChannel, 0), + COMMAND(update_setPriority, 1), + COMMAND(update_stopChannel, 0), + + // 28 + COMMAND(update_setBeat, 1), + COMMAND(update_waitForNextBeat, 1), + COMMAND(update_setExtraLevel1, 1), + COMMAND(update_stopChannel, 0), + + // 32 + COMMAND(update_setupDuration, 1), + COMMAND(update_playNote, 1), + COMMAND(update_stopChannel, 0), + COMMAND(update_stopChannel, 0), + + // 36 + COMMAND(update_setFractionalNoteSpacing, 1), + COMMAND(update_stopChannel, 0), + COMMAND(update_setTempo, 1), + COMMAND(update_removeSecondaryEffect1, 0), + + // 40 + COMMAND(update_stopChannel, 0), + COMMAND(update_setChannelTempo, 1), + COMMAND(update_stopChannel, 0), + COMMAND(update_setExtraLevel3, 1), + + // 44 + COMMAND(update_setExtraLevel2, 2), + COMMAND(update_changeExtraLevel2, 2), + COMMAND(update_setAMDepth, 1), + COMMAND(update_setVibratoDepth, 1), + + // 48 + COMMAND(update_changeExtraLevel1, 1), + COMMAND(update_stopChannel, 0), + COMMAND(update_stopChannel, 0), + COMMAND(update_clearChannel, 1), + + // 52 + COMMAND(update_stopChannel, 0), + COMMAND(update_changeNoteRandomly, 2), + COMMAND(update_removePrimaryEffectVibrato, 0), + COMMAND(update_stopChannel, 0), + + // 56 + COMMAND(update_stopChannel, 0), + COMMAND(update_pitchBend, 1), + COMMAND(update_resetToGlobalTempo, 0), + COMMAND(update_nop, 0), + + // 60 + COMMAND(update_setDurationRandomness, 1), + COMMAND(update_changeChannelTempo, 1), + COMMAND(update_stopChannel, 0), + COMMAND(updateCallback46, 2), + + // 64 + COMMAND(update_nop, 0), + COMMAND(update_setupRhythmSection, 9), + COMMAND(update_playRhythmSection, 1), + COMMAND(update_removeRhythmSection, 0), + + // 68 + COMMAND(update_setRhythmLevel2, 2), + COMMAND(update_changeRhythmLevel1, 2), + COMMAND(update_setRhythmLevel1, 2), + COMMAND(update_setSoundTrigger, 1), + + // 72 + COMMAND(update_setTempoReset, 1), + COMMAND(updateCallback56, 2), + COMMAND(update_stopChannel, 0) +}; + #undef COMMAND +const int AdLibDriver::_parserOpcodeTableSize = ARRAYSIZE(AdLibDriver::_parserOpcodeTable); + // This table holds the register offset for operator 1 for each of the nine // channels. To get the register offset for operator 2, simply add 3. -const uint8 AdlibDriver::_regOffset[] = { - 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, - 0x12 +const uint8 AdLibDriver::_regOffset[] = { + 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, + 0x12 }; -// Given the size of this table, and the range of its values, it's probably the -// F-Numbers (10 bits) for the notes of the 12-tone scale. However, it does not -// match the table in the Adlib documentation I've seen. +// These are the F-Numbers (10 bits) for the notes of the 12-tone scale. +// However, it does not match the table in the AdLib documentation I've seen. -const uint16 AdlibDriver::_unkTable[] = { - 0x0134, 0x0147, 0x015A, 0x016F, 0x0184, 0x019C, 0x01B4, 0x01CE, 0x01E9, - 0x0207, 0x0225, 0x0246 +const uint16 AdLibDriver::_freqTable[] = { + 0x0134, 0x0147, 0x015A, 0x016F, 0x0184, 0x019C, 0x01B4, 0x01CE, 0x01E9, + 0x0207, 0x0225, 0x0246 }; // These tables are currently only used by updateCallback46(), which only ever // uses the first element of one of the sub-tables. -const uint8 *AdlibDriver::_unkTable2[] = { - AdlibDriver::_unkTable2_1, - AdlibDriver::_unkTable2_2, - AdlibDriver::_unkTable2_1, - AdlibDriver::_unkTable2_2, - AdlibDriver::_unkTable2_3, - AdlibDriver::_unkTable2_2 +const uint8 *const AdLibDriver::_unkTable2[] = { + AdLibDriver::_unkTable2_1, + AdLibDriver::_unkTable2_2, + AdLibDriver::_unkTable2_1, + AdLibDriver::_unkTable2_2, + AdLibDriver::_unkTable2_3, + AdLibDriver::_unkTable2_2 }; -const uint8 AdlibDriver::_unkTable2_1[] = { - 0x50, 0x50, 0x4F, 0x4F, 0x4E, 0x4E, 0x4D, 0x4D, - 0x4C, 0x4C, 0x4B, 0x4B, 0x4A, 0x4A, 0x49, 0x49, - 0x48, 0x48, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45, - 0x44, 0x44, 0x43, 0x43, 0x42, 0x42, 0x41, 0x41, - 0x40, 0x40, 0x3F, 0x3F, 0x3E, 0x3E, 0x3D, 0x3D, - 0x3C, 0x3C, 0x3B, 0x3B, 0x3A, 0x3A, 0x39, 0x39, - 0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, 0x35, - 0x34, 0x34, 0x33, 0x33, 0x32, 0x32, 0x31, 0x31, - 0x30, 0x30, 0x2F, 0x2F, 0x2E, 0x2E, 0x2D, 0x2D, - 0x2C, 0x2C, 0x2B, 0x2B, 0x2A, 0x2A, 0x29, 0x29, - 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25, - 0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21, - 0x20, 0x20, 0x1F, 0x1F, 0x1E, 0x1E, 0x1D, 0x1D, - 0x1C, 0x1C, 0x1B, 0x1B, 0x1A, 0x1A, 0x19, 0x19, - 0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, - 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, - 0x10, 0x10 +const int AdLibDriver::_unkTable2Size = ARRAYSIZE(AdLibDriver::_unkTable2); + +const uint8 AdLibDriver::_unkTable2_1[] = { + 0x50, 0x50, 0x4F, 0x4F, 0x4E, 0x4E, 0x4D, 0x4D, + 0x4C, 0x4C, 0x4B, 0x4B, 0x4A, 0x4A, 0x49, 0x49, + 0x48, 0x48, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45, + 0x44, 0x44, 0x43, 0x43, 0x42, 0x42, 0x41, 0x41, + 0x40, 0x40, 0x3F, 0x3F, 0x3E, 0x3E, 0x3D, 0x3D, + 0x3C, 0x3C, 0x3B, 0x3B, 0x3A, 0x3A, 0x39, 0x39, + 0x38, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, 0x35, + 0x34, 0x34, 0x33, 0x33, 0x32, 0x32, 0x31, 0x31, + 0x30, 0x30, 0x2F, 0x2F, 0x2E, 0x2E, 0x2D, 0x2D, + 0x2C, 0x2C, 0x2B, 0x2B, 0x2A, 0x2A, 0x29, 0x29, + 0x28, 0x28, 0x27, 0x27, 0x26, 0x26, 0x25, 0x25, + 0x24, 0x24, 0x23, 0x23, 0x22, 0x22, 0x21, 0x21, + 0x20, 0x20, 0x1F, 0x1F, 0x1E, 0x1E, 0x1D, 0x1D, + 0x1C, 0x1C, 0x1B, 0x1B, 0x1A, 0x1A, 0x19, 0x19, + 0x18, 0x18, 0x17, 0x17, 0x16, 0x16, 0x15, 0x15, + 0x14, 0x14, 0x13, 0x13, 0x12, 0x12, 0x11, 0x11, + 0x10, 0x10 }; -// no don't ask me WHY this table exsits! -const uint8 AdlibDriver::_unkTable2_2[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x6F, - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F +// no don't ask me WHY this table exists! +const uint8 AdLibDriver::_unkTable2_2[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x6F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F }; -const uint8 AdlibDriver::_unkTable2_3[] = { - 0x40, 0x40, 0x40, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E, - 0x3E, 0x3D, 0x3D, 0x3D, 0x3C, 0x3C, 0x3C, 0x3B, - 0x3B, 0x3B, 0x3A, 0x3A, 0x3A, 0x39, 0x39, 0x39, - 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36, - 0x36, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x33, - 0x33, 0x33, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31, - 0x30, 0x30, 0x30, 0x2F, 0x2F, 0x2F, 0x2E, 0x2E, - 0x2E, 0x2D, 0x2D, 0x2D, 0x2C, 0x2C, 0x2C, 0x2B, - 0x2B, 0x2B, 0x2A, 0x2A, 0x2A, 0x29, 0x29, 0x29, - 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26, - 0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23, - 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, - 0x20, 0x20, 0x20, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E, - 0x1E, 0x1D, 0x1D, 0x1D, 0x1C, 0x1C, 0x1C, 0x1B, - 0x1B, 0x1B, 0x1A, 0x1A, 0x1A, 0x19, 0x19, 0x19, - 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16, - 0x16, 0x15 +const uint8 AdLibDriver::_unkTable2_3[] = { + 0x40, 0x40, 0x40, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E, + 0x3E, 0x3D, 0x3D, 0x3D, 0x3C, 0x3C, 0x3C, 0x3B, + 0x3B, 0x3B, 0x3A, 0x3A, 0x3A, 0x39, 0x39, 0x39, + 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36, + 0x36, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x33, + 0x33, 0x33, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31, + 0x30, 0x30, 0x30, 0x2F, 0x2F, 0x2F, 0x2E, 0x2E, + 0x2E, 0x2D, 0x2D, 0x2D, 0x2C, 0x2C, 0x2C, 0x2B, + 0x2B, 0x2B, 0x2A, 0x2A, 0x2A, 0x29, 0x29, 0x29, + 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26, + 0x26, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23, + 0x23, 0x23, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, + 0x20, 0x20, 0x20, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E, + 0x1E, 0x1D, 0x1D, 0x1D, 0x1C, 0x1C, 0x1C, 0x1B, + 0x1B, 0x1B, 0x1A, 0x1A, 0x1A, 0x19, 0x19, 0x19, + 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x16, 0x16, + 0x16, 0x15 }; // This table is used to modify the frequency of the notes, depending on the -// note value and unk16. In theory, we could very well try to access memory -// outside this table, but in reality that probably won't happen. +// note value and the pitch bend value. In theory, we could very well try to +// access memory outside this table, but in reality that probably won't happen. // -// This could be some sort of pitch bend, but I have yet to see it used for -// anything so it's hard to say. - -const uint8 AdlibDriver::_unkTables[][32] = { - // 0 - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, - 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19, - 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21 }, - // 1 - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x09, - 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, - 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24 }, - // 2 - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x09, - 0x0A, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x19, 0x1A, 0x1C, 0x1D, - 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26 }, - // 3 - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A, - 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1D, - 0x1E, 0x1F, 0x20, 0x21, 0x23, 0x25, 0x27, 0x28 }, - // 4 - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A, - 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x13, 0x15, - 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x28, 0x2A }, - // 5 - { 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B, - 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, - 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20, - 0x21, 0x22, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D }, - // 6 - { 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B, - 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, - 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1E, 0x21, 0x24, - 0x25, 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30 }, - // 7 - { 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, - 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x18, - 0x19, 0x1A, 0x1C, 0x1D, 0x1F, 0x21, 0x23, 0x25, - 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30, 0x32 }, - // 8 - { 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0D, - 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x14, 0x17, 0x1A, - 0x19, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x25, 0x28, - 0x29, 0x2A, 0x2B, 0x2D, 0x2F, 0x31, 0x33, 0x35 }, - // 9 - { 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E, - 0x0F, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1B, - 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x29, - 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x39 }, - // 10 - { 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E, - 0x0F, 0x10, 0x12, 0x14, 0x16, 0x19, 0x1B, 0x1E, - 0x1F, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D, - 0x2E, 0x2F, 0x31, 0x32, 0x34, 0x36, 0x39, 0x3C }, - // 11 - { 0x00, 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F, - 0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1E, - 0x1F, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2B, 0x2E, - 0x2F, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3C, 0x3F }, - // 12 - { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x10, - 0x11, 0x12, 0x14, 0x16, 0x18, 0x1B, 0x1E, 0x21, - 0x22, 0x23, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, - 0x33, 0x34, 0x36, 0x38, 0x3B, 0x34, 0x41, 0x44 }, - // 13 - { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x11, - 0x12, 0x13, 0x15, 0x17, 0x1A, 0x1D, 0x20, 0x23, - 0x24, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, 0x35, - 0x36, 0x37, 0x39, 0x3B, 0x3E, 0x41, 0x44, 0x47 } + +const uint8 AdLibDriver::_pitchBendTables[][32] = { + // 0 + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19, + 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21 }, + // 1 + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x24 }, + // 2 + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x09, + 0x0A, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x19, 0x1A, 0x1C, 0x1D, + 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26 }, + // 3 + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1D, + 0x1E, 0x1F, 0x20, 0x21, 0x23, 0x25, 0x27, 0x28 }, + // 4 + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x11, 0x13, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x28, 0x2A }, + // 5 + { 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1B, 0x1D, 0x1F, 0x20, + 0x21, 0x22, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D }, + // 6 + { 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, + 0x16, 0x17, 0x18, 0x1A, 0x1C, 0x1E, 0x21, 0x24, + 0x25, 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30 }, + // 7 + { 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x18, + 0x19, 0x1A, 0x1C, 0x1D, 0x1F, 0x21, 0x23, 0x25, + 0x26, 0x27, 0x29, 0x2B, 0x2D, 0x2F, 0x30, 0x32 }, + // 8 + { 0x00, 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x14, 0x17, 0x1A, + 0x19, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x25, 0x28, + 0x29, 0x2A, 0x2B, 0x2D, 0x2F, 0x31, 0x33, 0x35 }, + // 9 + { 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E, + 0x0F, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x20, 0x22, 0x24, 0x26, 0x29, + 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x39 }, + // 10 + { 0x00, 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0E, + 0x0F, 0x10, 0x12, 0x14, 0x16, 0x19, 0x1B, 0x1E, + 0x1F, 0x21, 0x23, 0x25, 0x27, 0x29, 0x2B, 0x2D, + 0x2E, 0x2F, 0x31, 0x32, 0x34, 0x36, 0x39, 0x3C }, + // 11 + { 0x00, 0x01, 0x03, 0x05, 0x07, 0x0A, 0x0C, 0x0F, + 0x10, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1E, + 0x1F, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2B, 0x2E, + 0x2F, 0x30, 0x32, 0x34, 0x36, 0x39, 0x3C, 0x3F }, + // 12 + { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x10, + 0x11, 0x12, 0x14, 0x16, 0x18, 0x1B, 0x1E, 0x21, + 0x22, 0x23, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, + 0x33, 0x34, 0x36, 0x38, 0x3B, 0x34, 0x41, 0x44 }, + // 13 + { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x11, + 0x12, 0x13, 0x15, 0x17, 0x1A, 0x1D, 0x20, 0x23, + 0x24, 0x25, 0x27, 0x29, 0x2C, 0x2F, 0x32, 0x35, + 0x36, 0x37, 0x39, 0x3B, 0x3E, 0x41, 0x44, 0x47 } }; -// #pragma mark - +/* +PCSoundDriver *PCSoundDriver::createAdLib(Audio::Mixer *mixer, int version) { + return new AdLibDriver(mixer, version); +} -// At the time of writing, the only known case where Kyra 1 uses sound triggers -// is in the castle, to cycle between three different songs. +} // End of namespace Kyra +*/ -const int CadlPlayer::_kyra1SoundTriggers[] = { - 0, 4, 5, 3 -}; +#undef CALLBACKS_PER_SECOND -const int CadlPlayer::_kyra1NumSoundTriggers = ARRAYSIZE(CadlPlayer::_kyra1SoundTriggers); +// ### End of current scummvm code ### -CadlPlayer::CadlPlayer(Copl *newopl) - : CPlayer(newopl), numsubsongs(0), _trackEntries(), _trackEntries16(), _soundDataPtr(0) -{ - _version = 0; - memset(_trackEntries, 0, sizeof(_trackEntries)); - memset(_trackEntries16, 0, sizeof(_trackEntries16)); - _driver = new AdlibDriver(newopl); - assert(_driver); +// The driver in scummvm the code below is based on has been moved +// to a different file (engines/kyra/sound/sound_pc_v1.cpp) which +// is GPL only, so we're stuck with the code from before that change. - _sfxPlayingSound = -1; - // _soundFileLoaded = ""; +/* +// Kyra 1 sound triggers. Most noticeably, these are used towards the end of +// the game, in the castle, to cycle between different songs. The same music is +// used in other places throughout the game, but the player is less likely to +// spend enough time there to notice. - _soundTriggers = _kyra1SoundTriggers; - _numSoundTriggers = _kyra1NumSoundTriggers; +const int SoundAdLibPC::_kyra1SoundTriggers[] = { + 0, 4, 5, 3 +}; + +const int SoundAdLibPC::_kyra1NumSoundTriggers = ARRAYSIZE(SoundAdLibPC::_kyra1SoundTriggers); + +SoundAdLibPC::SoundAdLibPC(KyraEngine_v1 *vm, Audio::Mixer *mixer) + : Sound(vm, mixer), _driver(0), _trackEntries(), _soundDataPtr(0) { +*/ +CadlPlayer::CadlPlayer(Copl *newopl) + : CPlayer(newopl), numsubsongs(0), _soundDataPtr(0) { + memset(_trackEntries, 0, sizeof(_trackEntries)); + + _version = 0; + /* + _soundTriggers = 0; + _numSoundTriggers = 0; + _sfxPlayingSound = -1; + _soundFileLoaded.clear(); + _currentResourceSet = 0; + memset(&_resInfo, 0, sizeof(_resInfo)); + + switch (vm->game()) { + case GI_LOL: + _version = _vm->gameFlags().isDemo ? 3 : 4; + break; + case GI_KYRA2: + _version = 4; + break; + case GI_KYRA1: + _version = 3; + _soundTriggers = _kyra1SoundTriggers; + _numSoundTriggers = _kyra1NumSoundTriggers; + break; + case GI_EOB2: + _version = 2; + break; + case GI_EOB1: + _version = 1; + break; + default: + break; + } - init(); + _driver = new AdLibDriver(mixer, _version); + */ + _driver = new AdLibDriver(newopl); + assert(_driver); } CadlPlayer::~CadlPlayer() { - delete [] _soundDataPtr; - delete _driver; + delete _driver; + delete[] _soundDataPtr; + // for (int i = 0; i < 3; i++) + // initAudioResourceInfo(i, 0); } bool CadlPlayer::init() { - _driver->callback(2); - _driver->callback(16, int(4)); - return true; + _driver->initDriver(); + return true; } -void CadlPlayer::process() { - uint8 trigger = _driver->callback(11); +/* +void SoundAdLibPC::process() { + int trigger = _driver->getSoundTrigger(); - if (trigger < _numSoundTriggers) { - int soundId = _soundTriggers[trigger]; + if (trigger < _numSoundTriggers) { + int soundId = _soundTriggers[trigger]; - if (soundId) { - playTrack(soundId); - } - } else { - warning("Unknown sound trigger %d", trigger); - // TODO: At this point, we really want to clear the trigger... - } + if (soundId) + playTrack(soundId); + } else { + warning("Unknown sound trigger %d", trigger); + // TODO: At this point, we really want to clear the trigger... + } } -// void CadlPlayer::setVolume(int volume) { -// } +void SoundAdLibPC::updateVolumeSettings() { + bool mute = false; + if (ConfMan.hasKey("mute")) + mute = ConfMan.getBool("mute"); -// int CadlPlayer::getVolume() { -// return 0; -// } + int newMusicVolume = mute ? 0 : ConfMan.getInt("music_volume"); + //newMusicVolume = (newMusicVolume * 145) / Audio::Mixer::kMaxMixerVolume + 110; + newMusicVolume = CLIP(newMusicVolume, 0, 255); -// void CadlPlayer::loadMusicFile(const char *file) { -// loadSoundFile(file); -// } + int newSfxVolume = mute ? 0 : ConfMan.getInt("sfx_volume"); + //newSfxVolume = (newSfxVolume * 200) / Audio::Mixer::kMaxMixerVolume + 55; + newSfxVolume = CLIP(newSfxVolume, 0, 255); + + _driver->setMusicVolume(newMusicVolume); + _driver->setSfxVolume(newSfxVolume); +} +*/ void CadlPlayer::playTrack(uint16_t track) { - play(track); -} - -// void CadlPlayer::haltTrack() { -// unk1(); -// unk2(); -// //_engine->_system->delayMillis(3 * 60); -// } - -void CadlPlayer::playSoundEffect(uint16_t track) { - play(track); -} - -void CadlPlayer::play(uint16_t track) { - uint16 soundId = 0; - if (_version < 3) - { - soundId = _trackEntries[track]; - if ((int8)soundId == -1 || !_soundDataPtr) - return; - soundId &= 0xFF; - } - else - { - soundId = _trackEntries16[track]; - if ((int16)soundId == -1 || !_soundDataPtr) - return; - soundId &= 0xFFFF; - } - _driver->ADLVer = _version; - _driver->callback(16, 0); - // while ((_driver->callback(16, 0) & 8)) { - // We call the system delay and not the game delay to avoid concurrency issues. - // _engine->_system->delayMillis(10); - // } - if (_sfxPlayingSound != -1) { - // Restore the sounds's normal values. - _driver->callback(10, _sfxPlayingSound, int(1), int(_sfxPriority)); - _driver->callback(10, _sfxPlayingSound, int(3), int(_sfxFourthByteOfSong)); - _sfxPlayingSound = -1; - } - - int chan = _driver->callback(9, soundId, int(0)); - - if (chan != 9) { - _sfxPlayingSound = soundId; - _sfxPriority = _driver->callback(9, soundId, int(1)); - _sfxFourthByteOfSong = _driver->callback(9, soundId, int(3)); - - // In the cases I've seen, the mysterious fourth byte has been - // the parameter for the update_setExtraLevel3() callback. - // - // The extra level is part of the channels "total level", which - // is a six-bit value where larger values means softer volume. - // - // So what seems to be happening here is that sounds which are - // started by this function are given a slightly lower priority - // and a slightly higher (i.e. softer) extra level 3 than they - // would have if they were started from anywhere else. Strange. - - int newVal = ((((-_sfxFourthByteOfSong) + 63) * 0xFF) >> 8) & 0xFF; - newVal = -newVal + 63; - _driver->callback(10, soundId, int(3), newVal); - newVal = ((_sfxPriority * 0xFF) >> 8) & 0xFF; - _driver->callback(10, soundId, int(1), newVal); - } - - _driver->callback(6, soundId); -} - -// void CadlPlayer::beginFadeOut() { -// playSoundEffect(1); -// } + if (_musicEnabled) { + /* + // WORKAROUND: There is a bug in the Kyra 1 "Pool of Sorrow" + // music which causes the channels to get progressively out of + // sync for each loop. To avoid that, we declare that all four + // of the song channels have to jump "in sync". + + if (track == 4 && _soundFileLoaded.equalsIgnoreCase("KYRA1B.ADL")) + _driver->setSyncJumpMask(0x000F); + else + _driver->setSyncJumpMask(0); + */ + play(track, 0xFF); + } +} -bool CadlPlayer::load(const std::string &filename, const CFileProvider &fp) -{ - binistream *f = fp.open(filename); - - // file validation section - if(!f || !fp.extension(filename, ".adl")) { - fp.close(f); - return false; - } - if (fp.filesize(f) < 720) - { // minimum file size of v1 - fp.close(f); - return false; - } - - // if (_soundFileLoaded == file) - // return; - - // if (_soundDataPtr) { - // haltTrack(); - // } - - uint8 *file_data = 0; uint32 file_size = 0; - - // char filename[25]; - // sprintf(filename, "%s.ADL", file); - - // file_data = _engine->resource()->fileData(filename, &file_size); - // if (!file_data) { - // warning("Couldn't find music file: '%s'", filename); - // return; - // } - - unk2(); - unk1(); - - // detect format version - _version = 3; // assuming we have v3 - for (int i = 0; i < 120; i += 2) - { - uint16_t w = f->readInt(2); - // all entries should be in range 0..500-1 or 0xFFFF - if (w >= 500 && w < 0xffff) - { - _version = 1; // actually 1 or 2 - break; - } - } - if (_version == 1) - { // detect whether v1 or v2 - f->seek(120); - _version = 2; // assuming we have v2 - for (int i = 0; i < 150; i += 2) - { - uint16_t w = f->readInt(2); - if (w > 0 && w < 600) - { // minimum track offset for v1 is 600 - fp.close(f); - return false; - } - // minimum track offset for v2 is 1000 - if (w > 0 && w < 1000) - _version = 1; - } - } - if (_version == 2 && fp.filesize(f) < 1120) - { // minimum file size of v2 - fp.close(f); - return false; - } - if (_version == 3 && fp.filesize(f) < 2500) - { // minimum file size of v3 - fp.close(f); - return false; - } - - f->seek(0); - file_size = fp.filesize(f); - file_data = new uint8 [file_size]; - f->readString((char *)file_data, file_size); - - _driver->callback(8, int(-1)); - _soundDataPtr = 0; - - uint8 *p = file_data; - uint16 _EntriesSize; - if (_version < 3) - { - _EntriesSize = 120 * sizeof(uint8); - memcpy(_trackEntries, p, _EntriesSize); - } - else - { - _EntriesSize = 250 * sizeof(uint16); - memcpy(_trackEntries16, p, _EntriesSize); - } - p += _EntriesSize; - - int soundDataSize = file_size - _EntriesSize; - - _soundDataPtr = new uint8[soundDataSize]; - assert(_soundDataPtr); - - memcpy(_soundDataPtr, p, soundDataSize*sizeof(uint8)); - - delete [] file_data; - file_data = p = 0; - file_size = 0; - - _driver->soundDataSize = soundDataSize; - _driver->callback(4, _soundDataPtr); - - // _soundFileLoaded = file; - - // find last subsong - uint16_t maxEntry = 0xffff; - switch (_version) - { - case 1: - maxEntry = 150 - 1; - break; - case 2: - maxEntry = 250 - 1; - break; - case 3: - maxEntry = 500 - 1; - break; - } - if (_version < 3) - { - for (int i = 120-1; i >= 0; i--) - if (_trackEntries[i] <= maxEntry) { - numsubsongs = i + 1; - break; - } - } - else - { - for (int i = 250-1; i >= 0; i--) - if (_trackEntries16[i] <= maxEntry) { - numsubsongs = i + 1; - break; - } - } - - fp.close(f); - cursubsong = -1; - return true; -} - -void CadlPlayer::rewind(int subsong) -{ - if(subsong == -1) subsong = cursubsong; - opl->init(); - opl->write(1,32); - playSoundEffect(subsong); - cursubsong = subsong; - update(); +/* +void SoundAdLibPC::haltTrack() { + play(0, 0); + play(0, 0); + //_vm->_system->delayMillis(3 * 60); } -unsigned int CadlPlayer::getsubsongs() -{ - return numsubsongs; +bool SoundAdLibPC::isPlaying() const { + return _driver->isChannelPlaying(0); } +*/ -bool CadlPlayer::update() -{ - if (cursubsong == -1) - rewind(2); +void CadlPlayer::playSoundEffect(uint16_t track, uint8_t volume) { + if (_sfxEnabled) + play(track, volume); +} - bool songend = true; +void CadlPlayer::play(uint16_t track, uint8_t volume) { + uint16 soundId = 0; -// if(_trackEntries[cursubsong] == 0xff) -// return false; + if (track >= numsubsongs) // Check added in AdPlug + return; - _driver->callback(); + if (_version == 4) + soundId = READ_LE_UINT16(&_trackEntries[track<<1]); + else + soundId = _trackEntries[track]; - for(int i = 0; i < 10; i++) - if(_driver->_channels[i].dataptr != NULL) - songend = false; + if ((soundId == 0xFFFF && _version == 4) || (soundId == 0xFF && _version < 4) || !_soundDataPtr) + return; - return !songend; + _driver->startSound(soundId, volume); } -void CadlPlayer::unk1() { - playSoundEffect(0); - //_engine->_system->delayMillis(5 * 60); +/* +void SoundAdLibPC::beginFadeOut() { + play(_version > 2 ? 1 : 15, 0xFF); } -void CadlPlayer::unk2() { - playSoundEffect(0); +int SoundAdLibPC::checkTrigger() { + return _driver->getSoundTrigger(); } -std::string CadlPlayer::gettype() -{ - char tmpstr[25]; +void SoundAdLibPC::resetTrigger() { + _driver->resetSoundTrigger(); +} - sprintf(tmpstr, "Westwood ADL (version %d)", _version); - return std::string(tmpstr); +void SoundAdLibPC::initAudioResourceInfo(int set, void *info) { + if (set >= kMusicIntro && set <= kMusicFinale) { + delete _resInfo[set]; + _resInfo[set] = info ? new SoundResourceInfo_PC(*(SoundResourceInfo_PC*)info) : 0; + } +} + +void SoundAdLibPC::selectAudioResourceSet(int set) { + if (set >= kMusicIntro && set <= kMusicFinale) { + if (_resInfo[set]) + _currentResourceSet = set; + } +} + +bool SoundAdLibPC::hasSoundFile(uint file) const { + if (file < res()->fileListSize) + return (res()->fileList[file] != 0); + return false; +} + +void SoundAdLibPC::loadSoundFile(uint file) { + if (file < res()->fileListSize) + internalLoadFile(res()->fileList[file]); } -CPlayer *CadlPlayer::factory(Copl *newopl) +void SoundAdLibPC::internalLoadFile(Common::String file) { + file += ((_version == 1) ? ".DAT" : ".ADL"); + if (_soundFileLoaded == file) + return; + + if (_soundDataPtr) + haltTrack(); + + uint8 *fileData = 0; uint32 fileSize = 0; + + fileData = _vm->resource()->fileData(file.c_str(), &fileSize); + if (!fileData) { + warning("Couldn't find music file: '%s'", file.c_str()); + return; + } + + playSoundEffect(0); + playSoundEffect(0); + + _driver->stopAllChannels(); + + int soundDataSize = fileSize; + uint8 *p = fileData; + + if (_version == 4) { + memcpy(_trackEntries, p, 500); + p += 500; + soundDataSize -= 500; + } else { + memcpy(_trackEntries, p, 120); + p += 120; + soundDataSize -= 120; + } + + uint8 *oldData = _soundDataPtr; + _soundDataPtr = new uint8[soundDataSize]; + assert(_soundDataPtr); + + memcpy(_soundDataPtr, p, soundDataSize); + _driver->setSoundData(_soundDataPtr, soundDataSize); + + delete[] fileData; + delete[] oldData; + + _soundFileLoaded = file; +} +*/ + +bool CadlPlayer::load(const std::string &filename, const CFileProvider &fp) { - return new CadlPlayer(newopl); + // file validation section + + // The format has no magic number. It starts with a few tables and + // it would be possible to check whether the entries are plausible, + // but that may be a bit weak to determine the file format reliably. + if (!fp.extension(filename, ".adl")) + return false; + + binistream *f = fp.open(filename); + if (!f) return false; + + const unsigned long fileSize = fp.filesize(f); + if (fileSize < 720) { // minimum file size of v1 + fp.close(f); + return false; + } + + /* + Here's the rough structure of ADL files. The player needs only the + first part, the driver uses only the second part (all offsets in the + programs/instruments tables are relative to the start of _soundData). + + _trackEntries[] _soundData[] + +------------------+ +-----------------+--------------------+------//-+ + | subsong->prog.ID | | Program offsets | Instrument offsets | Data... | + +------------------+ +-----------------+--------------------+------//-+ + v1: 120 bytes 150 words 150 words @720 + v2/3: 120 bytes 250 words 250 words @1120 + v4: 250 words 500 words 500 words @2500 + + The versions can be distinguished by inspecting the table entries. + */ + + // read track entries (maximum length) + f->readString((char *)_trackEntries, sizeof(_trackEntries)); + + // detect format version v4 vs v1/2/3 + int ofs = 500; + _version = 4; // assuming we have v4 + for (int i = 0; i < 500; i += 2) { + uint16_t w = READ_LE_UINT16(&_trackEntries[i]); + // For v4 all entries should be below 500 or 0xFFFF. For other + // versions this will check program offsets (at least 600). + if (w >= 500 && w < 0xFFFF) { + _version = 3; // actually 1, 2, or 3 + ofs = 120; + break; + } + } + + // read remainder of file + int soundDataSize = fileSize - ofs; + delete[] _soundDataPtr; + _soundDataPtr = new uint8[soundDataSize]; + const int surplus = sizeof(_trackEntries) - ofs; + if (surplus) { + memcpy(_soundDataPtr, &_trackEntries[ofs], surplus); + memset(&_trackEntries[ofs], 0xFF, surplus); + } + f->readString((char *)&_soundDataPtr[surplus], soundDataSize - surplus); + fp.close(f); + + int numProgs; + if (_version < 4) { + // Note: Could check instrument offsets, too. Or fail when an + // offset is above soundDataSize. That's not really necessary + // now, but should be added if the extension check is removed. + + // detect whether v1 or v2/v3 + numProgs = 150; // for v1 + for (int i = 0; i < numProgs * 2; i += 2) { + uint16_t w = READ_LE_UINT16(&_soundDataPtr[i]); + + // minimum program/instrument offset for v1 is 600 + if (w > 0 && w < 600) + goto bad_data; + + // minimum offset for v2/v3 is 1000 + if (w > 0 && w < 1000) + _version = 1; + } + + // TODO: Detect whether v2 (EOB2) or v3 (KYRA1). The only + // difference seems to be how offsets are encoded for the + // update_jumpToSubroutine opcode in the sound programs. + // Assume v3 for now. + + // more sanity checks + if (_version > 1) { + if (fileSize < 1120) // minimum size of v2/v3 + goto bad_data; + + numProgs = 250; + for (int i = 150 * 2; i < numProgs * 2; i += 2) { + uint16_t w = READ_LE_UINT16(&_soundDataPtr[i]); + if (w > 0 && w < 1000) + goto bad_data; + } + } + } else { + // sanity checks for v4 + if (fileSize < 2500) { // minimum file size of v4 + bad_data: + delete[] _soundDataPtr; + _soundDataPtr = nullptr; + return false; + } + numProgs = 500; + for (int i = 0; i < numProgs * 2; i += 2) { + uint16_t w = READ_LE_UINT16(&_soundDataPtr[i]); + // minimum program offset for v4 is 2000 + if (w > 0 && w < 2000) + goto bad_data; + } + } + + _driver->setVersion(_version); + _driver->setSoundData(_soundDataPtr, soundDataSize); + + // find last subsong + if (_version == 4) { + for (int i = 2 * 250; i > 0; i -= 2) + if (READ_LE_UINT16(&_trackEntries[i - 2]) < numProgs) { + numsubsongs = i / 2; + break; + } + } else { + for (int i = 120; i > 0; i--) + if (_trackEntries[i - 1] < numProgs) { + numsubsongs = i; + break; + } + } + + rewind(2); // subsong 2 is selected by default + return true; +} + +void CadlPlayer::rewind(int subsong) { + // stop current song and re-initialize the opl. + init(); + _driver->stopAllChannels(); + opl->init(); + opl->write(1, 32); + + // prevent setting invalid subsong + if (subsong >= numsubsongs) + subsong = 0; + // rewind(-1) rewinds current subsong + if (subsong >= 0) + cursubsong = subsong; + + // enqueue new subsong + playSoundEffect(cursubsong); +} + +unsigned int CadlPlayer::getsubsongs() { + return numsubsongs; +} + +bool CadlPlayer::update() { + _driver->callback(); + + for (int i = 0; i < 10; i++) + if (_driver->isChannelPlaying(i) && !_driver->isChannelRepeating(i)) + return true; + + return false; +} + +std::string CadlPlayer::gettype() { + char tmpstr[27]; + + snprintf(tmpstr, sizeof(tmpstr), "Westwood ADL (version %d)", _version); + return std::string(tmpstr); +} + +CPlayer *CadlPlayer::factory(Copl *newopl) { + return new CadlPlayer(newopl); } diff --git a/plugins/adplug/adplug/adl.h b/plugins/adplug/adplug/adl.h index 05392d0992..402bb77844 100644 --- a/plugins/adplug/adplug/adl.h +++ b/plugins/adplug/adplug/adl.h @@ -26,7 +26,7 @@ #include "player.h" -class AdlibDriver; +class AdLibDriver; class CadlPlayer: public CPlayer { @@ -50,33 +50,22 @@ class CadlPlayer: public CPlayer unsigned int getsubsong() { return cursubsong; } std::string gettype(); + void playSoundEffect(uint16_t track, uint8_t volume = 0xFF); + private: + enum { _musicEnabled = 1, _sfxEnabled = 1 }; + int numsubsongs, cursubsong; - AdlibDriver *_driver; + AdLibDriver *_driver; uint8_t _version; - uint8_t _trackEntries[120]; - uint16_t _trackEntries16[250]; + uint8_t _trackEntries[500]; uint8_t *_soundDataPtr; - int _sfxPlayingSound; - - uint8_t _sfxPriority; - uint8_t _sfxFourthByteOfSong; - - int _numSoundTriggers; - const int *_soundTriggers; - - static const int _kyra1NumSoundTriggers; - static const int _kyra1SoundTriggers[]; bool init(); - void process(); void playTrack(uint16_t track); - void playSoundEffect(uint16_t track); - void play(uint16_t track); - void unk1(); - void unk2(); + void play(uint16_t track, uint8_t volume); }; #endif diff --git a/plugins/adplug/adplug/adlib.cpp b/plugins/adplug/adplug/adlib.cpp deleted file mode 100644 index 8031594d42..0000000000 --- a/plugins/adplug/adplug/adlib.cpp +++ /dev/null @@ -1,708 +0,0 @@ -/* - * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2008 Simon Peter , et al. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * adlib.cpp - AdLib Sound Driver by Stas'M and Jepael - * - * Based on ADLIB.C by Marc Savary and Dale Glowinski, Ad Lib Inc. - */ - -#include "adlib.h" - -const uint8_t CadlibDriver::percMasks[5] = - {0x10, 0x08, 0x04, 0x02, 0x01}; - -/* definition of the ELECTRIC-PIANO voice (opr0 & opr1) */ -uint8_t CadlibDriver::pianoParamsOp0[nbLocParam] = - { 1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0 }; -uint8_t CadlibDriver::pianoParamsOp1[nbLocParam] = - { 0, 1, 1, 15, 7, 0, 2, 4, 0, 0, 0, 1, 0, 0 }; - -/* definition of default percussive voices: */ -uint8_t CadlibDriver::bdOpr0[nbLocParam] = - { 0, 0, 0, 10, 4, 0, 8, 12, 11, 0, 0, 0, 1, 0 }; -uint8_t CadlibDriver::bdOpr1[nbLocParam] = - { 0, 0, 0, 13, 4, 0, 6, 15, 0, 0, 0, 0, 1, 0 }; -uint8_t CadlibDriver::sdOpr[nbLocParam] = - { 0, 12, 0, 15, 11, 0, 8, 5, 0, 0, 0, 0, 0, 0 }; -uint8_t CadlibDriver::tomOpr[nbLocParam] = - { 0, 4, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 }; -uint8_t CadlibDriver::cymbOpr[nbLocParam] = - { 0, 1, 0, 15, 11, 0, 5, 5, 0, 0, 0, 0, 0, 0 }; -uint8_t CadlibDriver::hhOpr[nbLocParam] = - { 0, 1, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 }; - -/* Slot numbers as a function of the voice and the operator. - ( melodic only) -*/ -uint8_t CadlibDriver::slotVoice[9][2] = { - { 0, 3 }, /* voix 0 */ - { 1, 4 }, /* 1 */ - { 2, 5 }, /* 2 */ - { 6, 9 }, /* 3 */ - { 7, 10 }, /* 4 */ - { 8, 11 }, /* 5 */ - { 12, 15 }, /* 6 */ - { 13, 16 }, /* 7 */ - { 14, 17 } /* 8 */ -}; - -/* Slot numbers for the percussive voices. - 0 indicates that there is only one slot. -*/ -uint8_t CadlibDriver::slotPerc[5][2] = { - { 12, 15 }, /* Bass Drum: slot 12 et 15 */ - { 16, 0 }, /* SD: slot 16 */ - { 14, 0 }, /* TOM: slot 14 */ - { 17, 0 }, /* TOP-CYM: slot 17 */ - { 13, 0 } /* HH: slot 13 */ -}; - -/* - This table gives the offset of each slot within the chip. - offset = fn( slot) -*/ -uint8_t CadlibDriver::offsetSlot[18] = { - 0, 1, 2, 3, 4, 5, - 8, 9, 10, 11, 12, 13, - 16, 17, 18, 19, 20, 21 -}; - -/* This table indicates if the slot is a modulator (0) or a carrier (1). - opr = fn( slot) -*/ -uint8_t CadlibDriver::operSlot[18] = { - 0, 0, 0, /* 1 2 3 */ - 1, 1, 1, /* 4 5 6 */ - 0, 0, 0, /* 7 8 9 */ - 1, 1, 1, /* 10 11 12 */ - 0, 0, 0, /* 13 14 15 */ - 1, 1, 1, /* 16 17 18 */ -}; - -/* This table gives the voice number associated with each slot. - (melodic mode only) - voice = fn( slot) -*/ -uint8_t CadlibDriver::voiceSlot[18] = { - 0, 1, 2, - 0, 1, 2, - 3, 4, 5, - 3, 4, 5, - 6, 7, 8, - 6, 7, 8, -}; - -/*** public methods *************************************/ - -void CadlibDriver::SoundWarmInit() -{ - /* init variables */ - for (int i = 0; i < MAX_VOICES; i++) - { - fNumFreqPtr[i] = 0; - voiceKeyOn[i] = 0; - notePitch[i] = 0; - } - amDepth = 0; - vibDepth = 0; - noteSel = 0; - InitSlotVolume(); - InitFNums(); - SetMode(0); /* melodic mode */ - SetGParam(0, 0, 0); /* init global parameters */ - for (int i = 0; i < 9; i++) - SoundChut(i); - SetPitchRange(1); /* default pitch range is 1 half-tone */ - SetWaveSel(1); -} - -/* ---------------------------------------------- - Put the chip in melodic mode (mode == 0), - or in percussive mode ( mode != 0). - - If the melodic mode is chosen, all voices are - set to electric-piano, else the first 5 are set - to electric-piano, and the percussion voices - to their default timbres. ---------------------------------------------- -*/ -void CadlibDriver::SetMode(int mode) -{ - if (mode) - { - SoundChut(BD); - SoundChut(SD); - SoundChut(TOM); - - /* set the frequency for the last 4 percussion voices: */ - SetFreq(TOM, TOM_PITCH, 0); - SetFreq(SD, SD_PITCH, 0); - } - percussion = mode; - percBits = 0; - - InitSlotParams(); - SndSAmVibRhythm(); -} - -/* - Enable (state != 0) / disable (state == 0) - the wave-select parameters. - - If you do not want to use the wave-select parameters, call - this function with a value of 0 AFTER calling SoundColdInit() - or SoundWarmInit(). -*/ -void CadlibDriver::SetWaveSel(int state) -{ - modeWaveSel = state ? 0x20 : 0; - for (int i = 0; i < 18; i++) - opl->write(0xE0 + offsetSlot[i], 0); - opl->write(1, modeWaveSel); -} - -/* - Routine to change the pitch bend range. The value can be from - 1 to 12 (in half-tones). - - For example, the value 12 means that the pitch bend will - range from -12 (pitchBend == 0, see function 'SetVoicePitch()') - to +12 (pitchBend == 0x3fff) half-tones. - - The change will be effective as of the next call to - 'SetVoicePitch()'. -*/ -void CadlibDriver::SetPitchRange(uint8_t pR) -{ - if (pR > 12) - pR = 12; - if (pR < 1) - pR = 1; - pitchRange = pR; - pitchRangeStep = pitchRange * NR_STEP_PITCH; -} - -/* ----------------------------------------------- - Set the 3 global parameters AmDepth, - VibDepth & NoteSel - - The change takes place immediately. ----------------------------------------------- -*/ -void CadlibDriver::SetGParam(int amD, int vibD, int nSel) -{ - amDepth = amD; - vibDepth = vibD; - noteSel = nSel; - - SndSAmVibRhythm(); - SndSNoteSel(); -} - -/* -------------------------------------------------- - Set the parameters of the voice 'voice'. - - In melodic mode, 'voice' varies from 0 to 8. - In percussive mode, voices 0 to 5 are melodic - and 6 to 10 are percussive. - - A timbre (melodic or percussive) is defined as follows: - the 13 first parameters of operator 0 ( ksl, multi, feedBack, - attack, sustain, eg-typem decay, release, level, am, vib, ksr, fm) - followed by the 13 parameters of operator 1 (if a percussive voice, all - the parameters are zero), followed by the wave-select parameter for - the operators 0 and 1. - - 'paramArray' is structured as follows: - struct { - int opr0Prm[ 13]; first 13 parameters - int opr1Prm[ 13]; must be 0 if percussive timbre - int opr0WaveSel; last parameter - int opr1WaveSel; must be 0 if percussive timbre - } TimbreDef; - - The old timbre files (*.INS) do not contain the parameters - 'opr0WaveSel' and 'opr1WaveSel'. - Set these two parameters to zero if you are using the old file - format. -------------------------------------------------- -*/ -void CadlibDriver::SetVoiceTimbre(uint8_t voice, int16_t * paramArray) -{ - int16_t wave0, wave1; - int16_t * prm1, * wavePtr; - - wavePtr = paramArray + 2 * (nbLocParam - 1); - wave0 = *wavePtr++; - wave1 = *wavePtr; - prm1 = paramArray + nbLocParam - 1; - - if (!percussion || voice < BD) { /* melodic only */ - SetSlotParam(slotVoice[voice][0], paramArray, wave0); - SetSlotParam(slotVoice[voice][1], prm1, wave1); - } - else if (voice == BD) { /* Bass Drum */ - SetSlotParam(slotPerc[0][0], paramArray, wave0); - SetSlotParam(slotPerc[0][1], prm1, wave1); - } - else /* percussion, 1 slot */ - SetSlotParam(slotPerc[voice - BD][0], paramArray, wave0); -} - -/* --------------------------------------------------- - Set the volume of the voice 'voice' to 'volume'. - - The resulting output level is (timbreVolume * volume / 127). - The change takes place immediately. - - 0 <= volume <= 127 --------------------------------------------------- -*/ -void CadlibDriver::SetVoiceVolume(uint8_t voice, uint8_t volume) -{ - uint8_t slot; - -#if 1 - if (!percussion || voice < BD) - slot = slotVoice[voice][1]; - else - slot = slotPerc[voice - BD][voice == BD ? 1 : 0]; - - if (volume > MAX_VOLUME) - volume = MAX_VOLUME; - slotRelVolume[slot] = volume; - SndSKslLevel(slot); -#else /* code that modify the two oper. volume of an additive sound: */ - if (volume > MAX_VOLUME) - volume = MAX_VOLUME; - - if (!percussion || voice <= BD) { - slots = slotVoice[voice]; - slotRelVolume[slots[1]] = volume; - SndSKslLevel(slots[1]); - if (!GetLocPrm(slots[0], prmFm)) { - /* additive syntesis: set volume of first slot too */ - slotRelVolume[slots[0]] = volume; - SndSKslLevel(slots[0]); - } - } - else { - slot = slotPerc[voice - BD][0]; - slotRelVolume[slot] = volume; - SndSKslLevel(slot); - } -#endif -} - -/* -------------------------------------------------- - Change the pitch value of a voice. - - The variation in pitch is a function of the previous call - to 'SetPitchRange()' and the value of 'pitchBend'. - A value of 0 means -half-tone * pitchRange, - 0x2000 means no variation (exact pitch) and - 0x3fff means +half-tone * pitchRange. - - Does not affect the percussive voices, except for the bass drum. - - The change takes place immediately. - - 0 <= pitchBend <= 0x3fff, 0x2000 == exact tuning -------------------------------------------------- -*/ -void CadlibDriver::SetVoicePitch(uint8_t voice, uint16_t pitchBend) -{ - if (!percussion || voice <= BD) { - /* melodic + bass-drum */ - if (pitchBend > MAX_PITCH) - pitchBend = MAX_PITCH; - ChangePitch(voice, pitchBend); - SetFreq(voice, notePitch[voice], voiceKeyOn[voice]); - } -} - -/* ------------------------------------------------------------ - Routine to start a note playing. - - 0 <= voice <= 8 in melodic mode, - 0 <= voice <= 10 in percussive mode; - 0 <= pitch <= 127, 60 == MID_C ( the card can play between 12 and 107 ) ------------------------------------------------------------ -*/ -void CadlibDriver::NoteOn(uint8_t voice, int pitch) -{ - pitch -= (MID_C - CHIP_MID_C); - if (pitch < 0) - pitch = 0; - if (pitch > 127) - pitch = 127; - - if (voice < BD || !percussion) - /* this is a melodic voice */ - SetFreq(voice, pitch, 1); - else { - /* this is a percussive voice */ - if (voice == BD) - SetFreq(BD, pitch, 0); - else if (voice == TOM) { - /* for the last 4 percussions, only the TOM may change in frequency, - modifying the three others: */ - SetFreq(TOM, pitch, 0); - SetFreq(SD, pitch + TOM_TO_SD, 0); /* f7 = 3 * f8 */ - } - - percBits |= percMasks[voice - BD]; - SndSAmVibRhythm(); - } -} - -/* - Routine to stop playing the note which was started in 'NoteOn()'. - - 0 <= voice <= 8 in melodic mode, - 0 <= voice <= 10 in percussive mode; -*/ -void CadlibDriver::NoteOff(uint8_t voice) -{ - if (!percussion || voice < BD) - SetFreq(voice, notePitch[voice], 0); /* shut off */ - else { - percBits &= ~percMasks[voice - BD]; - SndSAmVibRhythm(); - } -} - -/* - Set the volume of all slots. -*/ -void CadlibDriver::InitSlotVolume() -{ - for (int i = 0; i < 18; i++) - slotRelVolume[i] = MAX_VOLUME; -} - -/* - Initialize all lines of the frequency table. Each line represents - 12 half-tones shifted by (n/NR_STEP_PITCH), where 'n' is the line number - and ranges from 1 to NR_STEP_PITCH. -*/ -void CadlibDriver::InitFNums() -{ - uint8_t i, j, k, num, numStep, pas; - - numStep = 100 / NR_STEP_PITCH; - for (num = pas = 0; pas < NR_STEP_PITCH; pas++, num += numStep) - SetFNum(fNumNotes[pas], num, 100); - for (i = 0; i < MAX_VOICES; i++) { - fNumFreqPtr[i] = (uint16_t *)fNumNotes[0]; - halfToneOffset[i] = 0; - } - - k = 0; - for (i = 0; i < 8; i++) - for (j = 0; j < 12; j++, k++) { - noteDIV12[k] = i; - noteMOD12[k] = j; - } -} - -/* - Set the frequency of voice 'voice' to 0 hertz. -*/ -void CadlibDriver::SoundChut(int voice) -{ - opl->write(0xA0 + voice, 0); - opl->write(0xB0 + voice, 0); -} - -/* - Change pitch of voices 0 to 8, for melodic or percussive mode. -*/ -void CadlibDriver::SetFreq(uint8_t voice, int pitch, uint8_t keyOn) -{ - uint16_t fNbr, t1; - - voiceKeyOn[voice] = keyOn; - notePitch[voice] = pitch; - pitch += halfToneOffset[voice]; - if (pitch > 95) - pitch = 95; - if (pitch < 0) - pitch = 0; - fNbr = *(fNumFreqPtr[voice] + noteMOD12[pitch]); - opl->write(0xA0 + voice, fNbr & 0xFF); - t1 = keyOn ? 32 : 0; - t1 += (noteDIV12[pitch] << 2) + (0x3 & (fNbr >> 8)); - opl->write(0xB0 + voice, t1); -} - -/* - Set the values: AM Depth, VIB depth & Rhythm -*/ -void CadlibDriver::SndSAmVibRhythm() -{ - uint8_t t1; - - t1 = amDepth ? 0x80 : 0; - t1 |= vibDepth ? 0x40 : 0; - t1 |= percussion ? 0x20 : 0; - t1 |= percBits; - opl->write(0xBD, t1); -} - -/* -------------------------------------------- - Note sel -*/ -void CadlibDriver::SndSNoteSel() -{ - opl->write(0x08, noteSel ? 64 : 0); -} - -/* - KSL, LEVEL -*/ -void CadlibDriver::SndSKslLevel(uint8_t slot) -{ - unsigned t1; - - t1 = 63 - (GetLocPrm(slot, prmLevel) & 0x3f); /* amplitude */ - t1 = slotRelVolume[slot] * t1; - t1 += t1 + MAX_VOLUME; /* round off to 0.5 */ - t1 = 63 - t1 / (2 * MAX_VOLUME); - - t1 |= GetLocPrm(slot, prmKsl) << 6; - opl->write(0x40 + offsetSlot[slot], t1 & 0xFF); -} - -/* -------------------------------------------- - FEED-BACK and FM (connection). - Applicable only to operator 0 in melodic mode. -*/ -void CadlibDriver::SndSFeedFm(uint8_t slot) -{ - uint8_t t1; - - if (operSlot[slot]) - return; - t1 = GetLocPrm(slot, prmFeedBack) << 1; - t1 |= GetLocPrm(slot, prmFm) ? 0 : 1; - opl->write(0xC0 + (int)voiceSlot[slot], t1); -} - -/* - ATTACK, DECAY -*/ -void CadlibDriver::SndSAttDecay(uint8_t slot) -{ - uint8_t t1; - - t1 = GetLocPrm(slot, prmAttack) << 4; - t1 |= GetLocPrm(slot, prmDecay) & 0xf; - opl->write(0x60 + (int)offsetSlot[slot], t1); -} - -/* - SUSTAIN, RELEASE -*/ -void CadlibDriver::SndSSusRelease(uint8_t slot) -{ - uint8_t t1; - - t1 = GetLocPrm(slot, prmSustain) << 4; - t1 |= GetLocPrm(slot, prmRelease) & 0xf; - opl->write(0x80 + (int)offsetSlot[slot], t1); -} - -/* - AM, VIB, EG-TYP (Sustaining), KSR, MULTI -*/ -void CadlibDriver::SndSAVEK(uint8_t slot) -{ - uint8_t t1; - - t1 = GetLocPrm(slot, prmAm) ? 0x80 : 0; - t1 += GetLocPrm(slot, prmVib) ? 0x40 : 0; - t1 += GetLocPrm(slot, prmStaining) ? 0x20 : 0; - t1 += GetLocPrm(slot, prmKsr) ? 0x10 : 0; - t1 += GetLocPrm(slot, prmMulti) & 0xf; - opl->write(0x20 + (int)offsetSlot[slot], t1); -} - -/* - Set the wave-select parameter. -*/ -void CadlibDriver::SndWaveSelect(uint8_t slot) -{ - uint8_t wave; - - if (modeWaveSel) - wave = GetLocPrm(slot, prmWaveSel) & 0x03; - else - wave = 0; - opl->write(0xE0 + offsetSlot[slot], wave); -} - -/*------------------------------------------------- - Transfer all the parameters from slot 'slot' to - the chip. -*/ -void CadlibDriver::SndSetAllPrm(uint8_t slot) -{ - SndSAmVibRhythm(); - SndSNoteSel(); - SndSKslLevel(slot); - SndSFeedFm(slot); - SndSAttDecay(slot); - SndSSusRelease(slot); - SndSAVEK(slot); - SndWaveSelect(slot); -} - -/* ------------------------------------------------------- - Set the 14 parameters ( 13 in 'param', 1 in 'waveSel') - of slot 'slot'. Update the parameter array and the chip. ------------------------------------------------------- -*/ -void CadlibDriver::SetSlotParam(uint8_t slot, int16_t * param, uint8_t waveSel) -{ - for (int i = 0; i < nbLocParam - 1; i++) - paramSlot[slot][i] = *param++; - paramSlot[slot][nbLocParam - 1] = waveSel &= 0x3; - SndSetAllPrm(slot); -} - -void CadlibDriver::SetCharSlotParam(uint8_t slot, uint8_t * cParam, uint8_t waveSel) -{ - int16_t param[nbLocParam]; - - for (int i = 0; i < nbLocParam - 1; i++) - param[i] = *cParam++; - SetSlotParam(slot, param, waveSel); -} - -/* - In melodic mode, initialize all voices to electric-pianos. - - In percussive mode, initialize the first 6 voices to electric-pianos - and the percussive voices to their default timbres. -*/ -void CadlibDriver::InitSlotParams() -{ - for (int i = 0; i < 18; i++) - if (operSlot[i]) - SetCharSlotParam(i, pianoParamsOp1, 0); - else - SetCharSlotParam(i, pianoParamsOp0, 0); - - if (percussion) - { - SetCharSlotParam(12, bdOpr0, 0); - SetCharSlotParam(15, bdOpr1, 0); - SetCharSlotParam(16, sdOpr, 0); - SetCharSlotParam(14, tomOpr, 0); - SetCharSlotParam(17, cymbOpr, 0); - SetCharSlotParam(13, hhOpr, 0); - } -} - -/* - Initialize a line in the frequency table with shifted frequency values. - The values are shifted a fraction (num/den) of a half-tone. - See following routine. -*/ -void CadlibDriver::SetFNum(uint16_t * fNumVec, int num, int den) -{ - int i; - long val; - - *fNumVec++ = (uint16_t)(4 + (val = CalcPremFNum(num, den))) >> 3; - for (i = 1; i < 12; i++) { - val *= 106; - *fNumVec++ = (uint16_t)(4 + (val /= 100)) >> 3; - } -} - -/* - Routine to set 'halfToneOffset[]' & 'fNumFreqPtr[]'. - These two global variables are used to determine the frequency - variation to use when a note is played. - - 0 <= pitchBend <= 3fffH -*/ -void CadlibDriver::ChangePitch(int voice, int pitchBend) -{ - int l, t1, t2, delta; - static int oldL = -1; - static int oldHt; - static uint16_t * oldPtr; - - l = (int)(pitchBend - MID_PITCH) * pitchRangeStep; - if (oldL == l) { /* optimisation ... */ - fNumFreqPtr[voice] = oldPtr; - halfToneOffset[voice] = oldHt; - } - else { - t1 = l / MID_PITCH; - if (t1 < 0) { - t2 = NR_STEP_PITCH - 1 - t1; - oldHt = halfToneOffset[voice] = -(t2 / NR_STEP_PITCH); - delta = (t2 - NR_STEP_PITCH + 1) % NR_STEP_PITCH; - if (delta) - delta = NR_STEP_PITCH - delta; - } - else { - oldHt = halfToneOffset[voice] = t1 / NR_STEP_PITCH; - delta = t1 % NR_STEP_PITCH; - } - oldPtr = fNumFreqPtr[voice] = (uint16_t *)fNumNotes[delta]; - oldL = l; - } -} - -/* - Return binary value of the frequency 260.44 (C) - shifted by +/- numdeltaDemiTon/denDeltaDemiTon multiplied by 8. - - If the numerator (numDeltaDemiTon) is positive, the frequency is - increased; if negative, it is decreased. - - Fo = Fb( 1 + 0.06 num /den) - Fnum8 = Fo * 65536 * 72 / 3.58e6 - - -100 <= numDeltaDemiTon <= +100 - 1 <= denDeltaDemiTon <= 100 -*/ -long CadlibDriver::CalcPremFNum(int numDeltaDemiTon, int denDeltaDemiTon) -{ - long f8, fNum8, d100; - - d100 = denDeltaDemiTon * 100; - f8 = (d100 + 6 * numDeltaDemiTon) * (26044L * 2L); /* 260.44 * 100 * 2 */ - f8 /= d100 * 25; - fNum8 = f8 * 16384; /*( 16384L * 9L); */ - fNum8 *= 9L; - fNum8 /= 179L * 625L; - return fNum8; -} diff --git a/plugins/adplug/adplug/adlib.h b/plugins/adplug/adplug/adlib.h deleted file mode 100644 index 85b3de730a..0000000000 --- a/plugins/adplug/adplug/adlib.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2005 Simon Peter , et al. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * adlib.h - AdLib Sound Driver by Stas'M and Jepael - * - * Based on ADLIB.H by Marc Savary and Dale Glowinski, Ad Lib Inc. - */ - -#ifndef H_ADPLUG_ADLIBDRV -#define H_ADPLUG_ADLIBDRV - -#include "stdint.h" -#include "player.h" - -/* Parameters of each voice: */ -#define nbLocParam 14 - -#define prmKsl 0 -#define prmMulti 1 -#define prmFeedBack 2 /* use for opr. 0 only */ -#define prmAttack 3 -#define prmSustain 4 -#define prmStaining 5 /* Sustaining ... */ -#define prmDecay 6 -#define prmRelease 7 -#define prmLevel 8 -#define prmAm 9 -#define prmVib 10 -#define prmKsr 11 -#define prmFm 12 /* use for opr. 0 only */ -#define prmWaveSel 13 /* wave select */ - - /* globals parameters: */ -#define prmAmDepth 14 -#define prmVibDepth 15 -#define prmNoteSel 16 -#define prmPercussion 17 - - /* melodic voice numbers: */ -#define vMelo0 0 -#define vMelo1 1 -#define vMelo2 2 -#define vMelo3 3 -#define vMelo4 4 -#define vMelo5 5 -#define vMelo6 6 -#define vMelo7 7 -#define vMelo8 8 - - /* percussive voice numbers: */ -#define BD 6 -#define SD 7 -#define TOM 8 -#define CYMB 9 -#define HIHAT 10 - -#define MAX_VOICES 11 -#define MAX_VOLUME 0x7f -#define MAX_PITCH 0x3fff -#define MID_PITCH 0x2000 - -#define MID_C 60 /* MIDI standard mid C */ -#define CHIP_MID_C 48 /* sound chip mid C */ -#define NR_NOTES 96 /* # of notes we can play on chip */ - -#define TOM_PITCH 24 /* best frequency, in range of 0 to 95 */ -#define TOM_TO_SD 7 /* 7 half-tones between voice 7 & 8 */ -#define SD_PITCH (TOM_PITCH + TOM_TO_SD) - -#define NR_STEP_PITCH 25 /* 25 steps within a half-tone for pitch bend */ -#define ADLIB_OPER_LEN 13 /* operator length */ -#define ADLIB_INST_LEN (ADLIB_OPER_LEN * 2 + 2) /* modulator, carrier, mod/car wave select */ - -#define GetLocPrm(slot, prm) ( (uint8_t)paramSlot[slot][prm] ) - -class CadlibDriver -{ -public: - CadlibDriver(Copl *newopl) - : opl(newopl) - {}; - ~CadlibDriver() - {}; - - void SoundWarmInit(); - void SetMode(int mode); - void SetWaveSel(int state); - void SetPitchRange(uint8_t pR); - void SetGParam(int amD, int vibD, int nSel); - void SetVoiceTimbre(uint8_t voice, int16_t * paramArray); - void SetVoiceVolume(uint8_t voice, uint8_t volume); - void SetVoicePitch(uint8_t voice, uint16_t pitchBend); - void NoteOn(uint8_t voice, int pitch); - void NoteOff(uint8_t voice); - -private: - Copl *opl; - - void InitSlotVolume(); - void InitFNums(); - void SoundChut(int voice); - void SetFreq(uint8_t voice, int pitch, uint8_t keyOn); - void SndSAmVibRhythm(); - void SndSNoteSel(); - void SndSKslLevel(uint8_t slot); - void SndSFeedFm(uint8_t slot); - void SndSAttDecay(uint8_t slot); - void SndSSusRelease(uint8_t slot); - void SndSAVEK(uint8_t slot); - void SndWaveSelect(uint8_t slot); - void SndSetAllPrm(uint8_t slot); - void SetSlotParam(uint8_t slot, int16_t * param, uint8_t waveSel); - void SetCharSlotParam(uint8_t slot, uint8_t * cParam, uint8_t waveSel); - void InitSlotParams(); - void SetFNum(uint16_t * fNumVec, int num, int den); - void ChangePitch(int voice, int pitchBend); - long CalcPremFNum(int numDeltaDemiTon, int denDeltaDemiTon); - - uint16_t fNumNotes[NR_STEP_PITCH][12]; - int halfToneOffset[MAX_VOICES]; - uint16_t * fNumFreqPtr[MAX_VOICES]; - int pitchRange; /* pitch variation, half-tone [+1,+12] */ - int pitchRangeStep; /* == pitchRange * NR_STEP_PITCH */ - int modeWaveSel; /* != 0 if used with the 'wave-select' parameters */ - - uint8_t percBits; /* control bits of percussive voices */ - uint8_t notePitch[MAX_VOICES]; /* pitch of last note-on of each voice */ - uint8_t voiceKeyOn[MAX_VOICES]; /* state of keyOn bit of each voice */ - - uint8_t noteDIV12[96]; /* table of (0..95) DIV 12 */ - uint8_t noteMOD12[96]; /* table of (0..95) MOD 12 */ - - uint8_t slotRelVolume[18]; /* relative volume of slots */ - - typedef uint8_t SLOT_PARAM; - SLOT_PARAM paramSlot[18][nbLocParam]; /* all the parameters of slots... */ - - uint8_t amDepth; /* chip global parameters .. */ - uint8_t vibDepth; /* ... */ - uint8_t noteSel; /* ... */ - uint8_t percussion; /* percussion mode parameter */ - -protected: - static const uint8_t percMasks[5]; - static uint8_t pianoParamsOp0[nbLocParam]; - static uint8_t pianoParamsOp1[nbLocParam]; - static uint8_t bdOpr0[nbLocParam]; - static uint8_t bdOpr1[nbLocParam]; - static uint8_t sdOpr[nbLocParam]; - static uint8_t tomOpr[nbLocParam]; - static uint8_t cymbOpr[nbLocParam]; - static uint8_t hhOpr[nbLocParam]; - static uint8_t slotVoice[9][2]; - static uint8_t slotPerc[5][2]; - static uint8_t offsetSlot[18]; - static uint8_t operSlot[18]; - static uint8_t voiceSlot[18]; -}; - -#endif diff --git a/plugins/adplug/adplug/adlibemu.c b/plugins/adplug/adplug/adlibemu.c index eecedbb83f..4f1277a330 100644 --- a/plugins/adplug/adplug/adlibemu.c +++ b/plugins/adplug/adplug/adlibemu.c @@ -50,6 +50,30 @@ I'm not sure about a few things in my code: #include #include +#include "adlibemu.h" + +#define nlvol ctx->nlvol +#define nrvol ctx->nrvol +#define nlplc ctx->nlplc +#define nrplc ctx->nrplc +#define rend ctx->rend + +#define AMPSCALE ctx->AMPSCALE +#define numspeakers ctx->numspeakers +#define bytespersample ctx->bytespersample +#define recipsamp ctx->recipsamp +#define cell ctx->cell +#define wavtable ctx->wavtable +#define nfrqmul ctx->nfrqmul +#define adlibreg ctx->adlibreg +#define ksl ctx->ksl +#define odrumstat ctx->odrumstat +#define rptr ctx->rptr +#define nrptr ctx->nrptr +#define rbuf ctx->rbuf +#define snd ctx->snd +#define initfirstime ctx->initfirstime + #if !defined(max) && !defined(__cplusplus) #define max(a,b) (((a) > (b)) ? (a) : (b)) #endif @@ -58,10 +82,7 @@ I'm not sure about a few things in my code: #endif #define PI 3.141592653589793 -#define MAXCELLS 18 -#define WAVPREC 2048 -static float AMPSCALE=(8192.0); #define FRQSCALE (49716/512.0) //Constants for Ken's Awe32, on a PII-266 (Ken says: Use these for KSM's!) @@ -74,39 +95,21 @@ static float AMPSCALE=(8192.0); //#define MFBFACTOR 0.5 //How much feedback goes back into modulator //#define ADJUSTSPEED 0.85 //0<=x<=1 Simulate finite rate of change of state -typedef struct -{ - float val, t, tinc, vol, sustain, amp, mfb; - float a0, a1, a2, a3, decaymul, releasemul; - short *waveform; - long wavemask; - void (*cellfunc)(void *, float); - unsigned char flags, dum0, dum1, dum2; -} celltype; - -static long numspeakers, bytespersample; -static float recipsamp; -static celltype cell[MAXCELLS]; -static signed short wavtable[WAVPREC*3]; -static float kslmul[4] = {0.0,0.5,0.25,1.0}; -static float frqmul[16] = {.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15}, nfrqmul[16]; -static unsigned char adlibreg[256], ksl[8][16]; -static unsigned char modulatorbase[9] = {0,1,2,8,9,10,16,17,18}; -static unsigned char odrumstat = 0; -static unsigned char base2cell[22] = {0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8}; - -float lvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on left speaker -float rvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on right speaker -long lplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on left speaker -long rplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on right speaker - -long nlvol[9], nrvol[9]; -long nlplc[9], nrplc[9]; -long rend = 0; -#define FIFOSIZ 256 -static float *rptr[9], *nrptr[9]; -static float rbuf[9][FIFOSIZ*2]; -static float snd[FIFOSIZ*2]; +static const float kslmul[4] = {0.0,0.5,0.25,1.0}; +static const float frqmul[16] = {.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15}; +static const unsigned char modulatorbase[9] = {0,1,2,8,9,10,16,17,18}; +static const unsigned char base2cell[22] = {0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8}; + +static const float lvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on left speaker -- never modified by adplug +static const float rvol[9] = {1,1,1,1,1,1,1,1,1}; //Volume multiplier on right speaker -- never modified by adplug +static const long lplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on left speaker -- never modified by adplug +static const long rplc[9] = {0,0,0,0,0,0,0,0,0}; //Samples to delay on right speaker -- never modified by adplug + +static const long waveform[8] = {WAVPREC,WAVPREC>>1,WAVPREC,(WAVPREC*3)>>2,0,0,(WAVPREC*5)>>2,WAVPREC<<1}; +static const long wavemask[8] = {WAVPREC-1,WAVPREC-1,(WAVPREC>>1)-1,(WAVPREC>>1)-1,WAVPREC-1,((WAVPREC*3)>>2)-1,WAVPREC>>1,WAVPREC-1}; +static const long wavestart[8] = {0,WAVPREC>>1,0,WAVPREC>>2,0,0,0,WAVPREC>>3}; +static const float attackconst[4] = {1/2.82624,1/2.25280,1/1.88416,1/1.59744}; +static const float decrelconst[4] = {1/39.28064,1/31.41608,1/26.17344,1/22.44608}; #ifndef USING_ASM #define _inline @@ -129,8 +132,8 @@ static void ftol(float f, long *a) { #endif #define ctc ((celltype *)c) //A rare attempt to make code easier to read! -void docell4 (void *c, float modulator) { (void)c; (void)modulator; } -void docell3 (void *c, float modulator) +static void docell4 (void *c, float modulator) { (void)c; (void)modulator; } +static void docell3 (void *c, float modulator) { long i; @@ -138,13 +141,13 @@ void docell3 (void *c, float modulator) ctc->t += ctc->tinc; ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED; } -void docell2 (void *c, float modulator) +static void docell2 (void *c, float modulator) { long i; ftol(ctc->t+modulator,&i); - if (*(long *)&ctc->amp <= 0x37800000) + if (ctc->amp <= 0.000015258789062f) { ctc->amp = 0; ctc->cellfunc = docell4; @@ -154,13 +157,13 @@ void docell2 (void *c, float modulator) ctc->t += ctc->tinc; ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED; } -void docell1 (void *c, float modulator) +static void docell1 (void *c, float modulator) { long i; ftol(ctc->t+modulator,&i); - if ((*(long *)&ctc->amp) <= (*(long *)&ctc->sustain)) + if (ctc->amp <= ctc->sustain) { if (ctc->flags&32) { @@ -176,14 +179,14 @@ void docell1 (void *c, float modulator) ctc->t += ctc->tinc; ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED; } -void docell0 (void *c, float modulator) +static void docell0 (void *c, float modulator) { long i; ftol(ctc->t+modulator,&i); ctc->amp = ((ctc->a3*ctc->amp + ctc->a2)*ctc->amp + ctc->a1)*ctc->amp + ctc->a0; - if ((*(long *)&ctc->amp) > 0x3f800000) + if (ctc->amp > 1.0f) { ctc->amp = 1; ctc->cellfunc = docell1; @@ -193,13 +196,8 @@ void docell0 (void *c, float modulator) ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED; } - -static long waveform[8] = {WAVPREC,WAVPREC>>1,WAVPREC,(WAVPREC*3)>>2,0,0,(WAVPREC*5)>>2,WAVPREC<<1}; -static long wavemask[8] = {WAVPREC-1,WAVPREC-1,(WAVPREC>>1)-1,(WAVPREC>>1)-1,WAVPREC-1,((WAVPREC*3)>>2)-1,WAVPREC>>1,WAVPREC-1}; -static long wavestart[8] = {0,WAVPREC>>1,0,WAVPREC>>2,0,0,0,WAVPREC>>3}; -static float attackconst[4] = {1/2.82624,1/2.25280,1/1.88416,1/1.59744}; -static float decrelconst[4] = {1/39.28064,1/31.41608,1/26.17344,1/22.44608}; -void cellon (long i, long j, celltype *c, unsigned char iscarrier) +static void cellon (adlibemu_context *ctx, long i, long j, celltype *c, unsigned char iscarrier) +#define cellon(i,j,c,iscarrier) cellon(ctx,i,j,c,iscarrier) { long frn, oct, toff; float f; @@ -231,7 +229,8 @@ void cellon (long i, long j, celltype *c, unsigned char iscarrier) } //This function (and bug fix) written by Chris Moeller -void cellfreq (signed long i, signed long j, celltype *c) +static void cellfreq (adlibemu_context *ctx, signed long i, signed long j, celltype *c) +#define cellfreq(i,j,c) cellfreq(ctx,i,j,c) { long frn, oct; @@ -243,11 +242,12 @@ void cellfreq (signed long i, signed long j, celltype *c) (float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14); } -static long initfirstime = 0; -void adlibinit (long dasamplerate, long danumspeakers, long dabytespersample) +void adlibinit (adlibemu_context *ctx, long dasamplerate, long danumspeakers, long dabytespersample) { long i, j, frn, oct; + AMPSCALE=(8192.0); + memset((void *)adlibreg,0,sizeof(adlibreg)); memset((void *)cell,0,sizeof(celltype)*MAXCELLS); memset((void *)rbuf,0,sizeof(rbuf)); @@ -308,7 +308,7 @@ void adlibinit (long dasamplerate, long danumspeakers, long dabytespersample) } } -void adlib0 (long i, long v) +void adlib0 (adlibemu_context *ctx, long i, long v) { unsigned char tmp = adlibreg[i]; adlibreg[i] = v; @@ -436,11 +436,11 @@ static void clipit16(float f,short *a) { } #endif -void adlibsetvolume(int i) { +void adlibsetvolume(adlibemu_context *ctx,int i) { AMPSCALE=i; } -void adlibgetsample (unsigned char *sndptr, long numbytes) +void adlibgetsample (adlibemu_context *ctx, unsigned char *sndptr, long numbytes) { long i, j, k=0, ns, endsamples, rptrs, numsamples; celltype *cptr; diff --git a/plugins/adplug/adplug/adlibemu.h b/plugins/adplug/adplug/adlibemu.h index 576e57f66c..8ea4227fb4 100644 --- a/plugins/adplug/adplug/adlibemu.h +++ b/plugins/adplug/adplug/adlibemu.h @@ -18,9 +18,51 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -void adlibinit(long dasamplerate,long danumspeakers,long dabytespersample); -void adlib0(long i,long v); -void adlibgetsample(void *sndptr,long numbytes); -void adlibsetvolume(int i); -void randoinsts(); -extern float lvol[9],rvol[9],lplc[9],rplc[9]; +#ifndef _ADLIBEMU_H +#define _ADLIBEMU_H + +#define MAXCELLS 18 +#define WAVPREC 2048 +#define FIFOSIZ 256 + +typedef struct +{ + float val, t, tinc, vol, sustain, amp, mfb; + float a0, a1, a2, a3, decaymul, releasemul; + short *waveform; + long wavemask; + void (*cellfunc)(void *, float); + unsigned char flags, dum0, dum1, dum2; +} celltype; + +typedef struct +{ + long nlvol[9], nrvol[9]; + long nlplc[9], nrplc[9]; + long rend; + + // private + float AMPSCALE; + long numspeakers, bytespersample; + float recipsamp; + celltype cell[MAXCELLS]; + signed short wavtable[WAVPREC*3]; + float nfrqmul[16]; + unsigned char adlibreg[256], ksl[8][16]; + unsigned char odrumstat; + float *rptr[9], *nrptr[9]; + float rbuf[9][FIFOSIZ*2]; + float snd[FIFOSIZ*2]; + int initfirstime; +} adlibemu_context; + +#ifdef USING_ASM +# error USING_ASM is broken in adplug (-fPIC, context) +#endif + +void adlibinit(adlibemu_context *ctx, long dasamplerate,long danumspeakers,long dabytespersample); +void adlib0(adlibemu_context *ctx,long i,long v); +void adlibgetsample(adlibemu_context *ctx,unsigned char *sndptr,long numbytes); +void adlibsetvolume(adlibemu_context *ctx,int i); + +#endif // _ADLIBEMU_H diff --git a/plugins/adplug/adplug/adplug.cpp b/plugins/adplug/adplug/adplug.cpp index cc258db288..91445da3e2 100644 --- a/plugins/adplug/adplug/adplug.cpp +++ b/plugins/adplug/adplug/adplug.cpp @@ -38,6 +38,7 @@ #include "hsc.h" #include "amd.h" #include "a2m.h" +#include "a2m-v2.h" #include "imf.h" #include "sng.h" #include "adtrack.h" @@ -56,7 +57,7 @@ #include "dtm.h" #include "fmc.h" #include "mtk.h" -#include "rad.h" +#include "rad2.h" #include "raw.h" #include "sa2.h" #include "bmf.h" @@ -82,6 +83,9 @@ #include "vgm.h" #include "sop.h" #include "herad.h" +#include "coktel.h" +#include "pis.h" +#include "mtr.h" /***** CAdPlug *****/ @@ -91,16 +95,19 @@ const CPlayerDesc CAdPlug::allplayers[] = { CPlayerDesc(CsngPlayer::factory, "SNGPlay", ".sng\0"), CPlayerDesc(CimfPlayer::factory, "Apogee IMF", ".imf\0.wlf\0.adlib\0"), CPlayerDesc(Ca2mLoader::factory, "Adlib Tracker 2", ".a2m\0"), + CPlayerDesc(Ca2mv2Player::factory, "Adlib Tracker 2", ".a2m\0.a2t\0"), CPlayerDesc(CadtrackLoader::factory, "Adlib Tracker", ".sng\0"), CPlayerDesc(CamdLoader::factory, "AMUSIC", ".amd\0"), + CPlayerDesc(CamdLoader::factory, "XMS-Tracker", ".xms\0"), CPlayerDesc(CbamPlayer::factory, "Bob's Adlib Music", ".bam\0"), CPlayerDesc(CcmfPlayer::factory, "Creative Music File", ".cmf\0"), + CPlayerDesc(CcoktelPlayer::factory, "Coktel Vision Adlib Music", ".adl\0"), CPlayerDesc(Cd00Player::factory, "Packed EdLib", ".d00\0"), CPlayerDesc(CdfmLoader::factory, "Digital-FM", ".dfm\0"), CPlayerDesc(ChspLoader::factory, "HSC Packed", ".hsp\0"), CPlayerDesc(CksmPlayer::factory, "Ken Silverman Music", ".ksm\0"), CPlayerDesc(CmadLoader::factory, "Mlat Adlib Tracker", ".mad\0"), - CPlayerDesc(CmusPlayer::factory, "AdLib MIDI/IMS Format", ".mus\0.ims\0"), + CPlayerDesc(CmusPlayer::factory, "AdLib MIDI/IMS Format", ".mus\0.mdy\0.ims\0"), CPlayerDesc(CmdiPlayer::factory, "AdLib MIDIPlay File", ".mdi\0"), CPlayerDesc(CmidPlayer::factory, "MIDI", ".mid\0.sci\0.laa\0"), CPlayerDesc(CmkjPlayer::factory, "MKJamz", ".mkj\0"), @@ -110,7 +117,8 @@ const CPlayerDesc CAdPlug::allplayers[] = { CPlayerDesc(CdtmLoader::factory, "DeFy Adlib Tracker", ".dtm\0"), CPlayerDesc(CfmcLoader::factory, "Faust Music Creator", ".sng\0"), CPlayerDesc(CmtkLoader::factory, "MPU-401 Trakker", ".mtk\0"), - CPlayerDesc(CradLoader::factory, "Reality Adlib Tracker", ".rad\0"), + CPlayerDesc(CmtrLoader::factory, "Master Tracker", ".mtr\0"), + CPlayerDesc(Crad2Player::factory, "Reality Adlib Tracker", ".rad\0"), CPlayerDesc(CrawPlayer::factory, "Raw AdLib Capture", ".rac\0.raw\0"), CPlayerDesc(Csa2Loader::factory, "Surprise! Adlib Tracker", ".sat\0.sa2\0"), CPlayerDesc(CxadbmfPlayer::factory, "BMF Adlib Tracker", ".xad\0.bmf\0"), @@ -125,8 +133,9 @@ const CPlayerDesc CAdPlug::allplayers[] = { CPlayerDesc(CxsmPlayer::factory, "eXtra Simple Music", ".xsm\0"), CPlayerDesc(CdroPlayer::factory, "DOSBox Raw OPL v0.1", ".dro\0"), CPlayerDesc(Cdro2Player::factory, "DOSBox Raw OPL v2.0", ".dro\0"), + CPlayerDesc(CpisPlayer::factory, "Beni Tracker PIS Player", ".pis\0"), CPlayerDesc(CmscPlayer::factory, "Adlib MSC Player", ".msc\0"), - CPlayerDesc(CrixPlayer::factory, "Softstar RIX OPL Music", ".rix\0"), + CPlayerDesc(CrixPlayer::factory, "Softstar RIX OPL Music", ".rix\0.mkf\0"), CPlayerDesc(CadlPlayer::factory, "Westwood ADL", ".adl\0"), CPlayerDesc(CjbmPlayer::factory, "JBM Adlib Music", ".jbm\0"), CPlayerDesc(CgotPlayer::factory, "God of Thunder Music", ".got\0"), diff --git a/plugins/adplug/adplug/amd.cpp b/plugins/adplug/adplug/amd.cpp index 0a689f8c6a..d535646fe4 100644 --- a/plugins/adplug/adplug/amd.cpp +++ b/plugins/adplug/adplug/amd.cpp @@ -19,7 +19,8 @@ * amd.cpp - AMD Loader by Simon Peter */ -#include +#include +#include #include "amd.h" #include "debug.h" @@ -31,154 +32,183 @@ CPlayer *CamdLoader::factory(Copl *newopl) bool CamdLoader::load(const std::string &filename, const CFileProvider &fp) { - binistream *f = fp.open(filename); if(!f) return false; + binistream *f = fp.open(filename); + if (!f) return false; + + // file validation section struct { char id[9]; unsigned char version; + enum {offset = 1062}; } header; - int i, j, k, t, numtrax, maxi = 0; - unsigned char buf, buf2, buf3; - const unsigned char convfx[10] = {0,1,2,9,17,11,13,18,3,14}; - const unsigned char convvol[64] = { - 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 0xa, 0xa, 0xb, - 0xc, 0xc, 0xd, 0xe, 0xe, 0xf, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x14, - 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x21, - 0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2b, 0x2d, 0x2e, 0x30, 0x32, 0x35, - 0x37, 0x3a, 0x3c, 0x3f - }; - // file validation section - if(fp.filesize(f) < 1072) { fp.close(f); return false; } - f->seek(1062); f->readString(header.id, 9); + if (fp.filesize(f) < header.offset + sizeof(header)) { + fp.close(f); + return false; + } + f->seek(header.offset); + f->readString(header.id, sizeof(header.id)); + if (memcmp(header.id, "readInt(1); - if(strncmp(header.id, "seek(0); + // title + author f->readString(songname, sizeof(songname)); f->readString(author, sizeof(author)); - for(i = 0; i < 26; i++) { - f->readString(instname[i], 23); - for(j = 0; j < 11; j++) inst[i].data[j] = f->readInt(1); + + // instruments + for (size_t i = 0; i < 26; i++) { + f->readString(instname[i], sizeof(instname[i])); // load name + for (size_t j = 0; j < sizeof(instname[i]); j++) // convert name + if (instname[i][j] == '\xff') + instname[i][j] = '\x20'; + + inst[i].data[1] = f->readInt(1); // load & reorder data + inst[i].data[9] = f->readInt(1); + inst[i].data[3] = f->readInt(1); + inst[i].data[5] = f->readInt(1); + inst[i].data[7] = f->readInt(1); + inst[i].data[2] = f->readInt(1); + inst[i].data[10] = f->readInt(1); + inst[i].data[4] = f->readInt(1); + inst[i].data[6] = f->readInt(1); + inst[i].data[8] = f->readInt(1); + inst[i].data[0] = f->readInt(1); } - length = f->readInt(1); nop = f->readInt(1) + 1; - for(i=0;i<128;i++) order[i] = f->readInt(1); - f->seek(10, binio::Add); - if(header.version == 0x10) { // unpacked module + + // order length + # of patterns + length = f->readInt(1); + nop = f->readInt(1) + 1; + if (length > 128 || nop > 64) { + fp.close(f); + return false; + } + + // order list + f->readString((char *)order, 128); + // invalid pattern number in order list? + for (size_t i = 0; i < length; i++) + if ((order[i] & 0x7f) >= 64) { // should be < nop + fp.close(f); + return false; + } + + f->ignore(10); + + // track data + int maxi = 0; + if (header.version == 0x10) { // unpacked module + init_trackord(); maxi = nop * 9; - for(i=0;i<64*9;i++) - trackord[i/9][i%9] = i+1; - t = 0; - while(!f->ateof()) { - for(j=0;j<64;j++) - for(i=t;ireadInt(1); - tracks[i][j].param2 = (buf&127) % 10; - tracks[i][j].param1 = (buf&127) / 10; + + for (int t = 0; t < maxi && !f->ateof(); t += 9) { + for (int j = 0; j < 64; j++) + for (int i = t; i < t + 9; i++) { + unsigned char buf = f->readInt(1) & 0x7f; + tracks[i][j].param1 = buf / 10; + tracks[i][j].param2 = buf % 10; + buf = f->readInt(1); - tracks[i][j].inst = buf >> 4; tracks[i][j].command = buf & 0x0f; + tracks[i][j].inst = buf >> 4; + buf = f->readInt(1); + tracks[i][j].inst += (buf & 1) << 4; if(buf >> 4) // fix bug in AMD save routine - tracks[i][j].note = ((buf & 14) >> 1) * 12 + (buf >> 4); + tracks[i][j].note = ((buf & 0x0e) >> 1) * 12 + (buf >> 4); else tracks[i][j].note = 0; - tracks[i][j].inst += (buf & 1) << 4; } - t += 9; } } else { // packed module - for(i=0;ireadInt(2) + 1; - numtrax = f->readInt(2); - for(k=0;kreadInt(2); - if(i > 575) i = 575; // fix corrupted modules - maxi = (i + 1 > maxi ? i + 1 : maxi); - j = 0; - do { - buf = f->readInt(1); - if(buf & 128) { - for(t = j; t < j + (buf & 127) && t < 64; t++) { - tracks[i][t].command = 0; - tracks[i][t].inst = 0; - tracks[i][t].note = 0; - tracks[i][t].param1 = 0; - tracks[i][t].param2 = 0; - } - j += buf & 127; + if (trackord[i][j] > 64 * 9) trackord[i][j] = 0; // or fail? + } + + int numtrax = f->readInt(2); + for (int k = 0; k < numtrax; k++) { + int i = std::min((int)f->readInt(2), 64 * 9 - 1); // fix corrupted modules + maxi = std::max(i + 1, maxi); + + for (int j = 0; j < 64; j++) { + unsigned char buf = f->readInt(1); + + if (buf & 0x80) { // packed block of empty events + int len = std::min(buf & 0x7f, 64 - j); + memset(&tracks[i][j], 0, len * sizeof(tracks[i][j])); + j += len - 1; // adjust for increment in loop header continue; } - tracks[i][j].param2 = buf % 10; + + // normal data, same as above tracks[i][j].param1 = buf / 10; + tracks[i][j].param2 = buf % 10; + buf = f->readInt(1); - tracks[i][j].inst = buf >> 4; tracks[i][j].command = buf & 0x0f; + tracks[i][j].inst = buf >> 4; + buf = f->readInt(1); + tracks[i][j].inst += (buf & 1) << 4; if(buf >> 4) // fix bug in AMD save routine - tracks[i][j].note = ((buf & 14) >> 1) * 12 + (buf >> 4); + tracks[i][j].note = ((buf & 0x0e) >> 1) * 12 + (buf >> 4); else tracks[i][j].note = 0; - tracks[i][j].inst += (buf & 1) << 4; - j++; - } while(j<64); + } } } fp.close(f); - // convert to protracker replay data - bpm = 50; restartpos = 0; flags = Decimal; - for(i=0;i<26;i++) { // convert instruments - buf = inst[i].data[0]; - buf2 = inst[i].data[1]; - inst[i].data[0] = inst[i].data[10]; - inst[i].data[1] = buf; - buf = inst[i].data[2]; - inst[i].data[2] = inst[i].data[5]; - buf3 = inst[i].data[3]; - inst[i].data[3] = buf; - buf = inst[i].data[4]; - inst[i].data[4] = inst[i].data[7]; - inst[i].data[5] = buf3; - buf3 = inst[i].data[6]; - inst[i].data[6] = inst[i].data[8]; - inst[i].data[7] = buf; - inst[i].data[8] = inst[i].data[9]; - inst[i].data[9] = buf2; - inst[i].data[10] = buf3; - for(j=0;j<23;j++) // convert names - if(instname[i][j] == '\xff') - instname[i][j] = '\x20'; - } - for(i=0;i 63) { - index = 63; - } - int vol = convvol[index]; - - if(vol > 63) vol = 63; + if (tracks[i][j].command == 17) { + unsigned int vol = tracks[i][j].param1 * 10 + tracks[i][j].param2; + static const unsigned char convvol[64] = { + 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, + 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06, 0x07, + 0x07, 0x08, 0x09, 0x09, 0x0a, 0x0a, 0x0b, 0x0c, + 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x21, + 0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2b, 0x2d, + 0x2e, 0x30, 0x32, 0x35, 0x37, 0x3a, 0x3c, 0x3f + }; + + if (vol < sizeof(convvol)) vol = convvol[vol]; + else vol = 63; + tracks[i][j].param1 = vol / 10; tracks[i][j].param2 = vol % 10; } @@ -190,8 +220,8 @@ bool CamdLoader::load(const std::string &filename, const CFileProvider &fp) float CamdLoader::getrefresh() { - if(tempo) - return (float) (tempo); + if (tempo) + return (float)tempo; else return 18.2f; } diff --git a/plugins/adplug/adplug/amd.h b/plugins/adplug/adplug/amd.h index e320b002b8..f984524232 100644 --- a/plugins/adplug/adplug/amd.h +++ b/plugins/adplug/adplug/amd.h @@ -19,6 +19,7 @@ * amd.h - AMD Loader by Simon Peter */ +#include "strnlen.h" #include "protrack.h" class CamdLoader: public CmodPlayer @@ -34,16 +35,16 @@ class CamdLoader: public CmodPlayer float getrefresh(); std::string gettype() - { return std::string("AMUSIC Adlib Tracker"); }; + { return std::string("AMUSIC Adlib Tracker"); } std::string gettitle() - { return std::string(songname,0,24); }; + { return std::string(songname, strnlen(songname, sizeof(songname))); } std::string getauthor() - { return std::string(author,0,24); }; + { return std::string(author, strnlen(author, sizeof(author))); } unsigned int getinstruments() - { return 26; }; + { return 26; } std::string getinstrument(unsigned int n) - { return std::string(instname[n],0,23); }; + { return n < getinstruments() ? std::string(instname[n], strnlen(instname[n], sizeof(instname[n]))) : std::string(); } private: - char songname[24],author[24],instname[26][23]; + char songname[24], author[24], instname[26][23]; }; diff --git a/plugins/adplug/adplug/bam.cpp b/plugins/adplug/adplug/bam.cpp index 6fd505bf3d..eca4d94863 100644 --- a/plugins/adplug/adplug/bam.cpp +++ b/plugins/adplug/adplug/bam.cpp @@ -44,7 +44,7 @@ * support the multiple loop situation you describe, and * neither do any BAM-creation programs. Then both loops point * to the same label, the inner loop's counter is just allowed - * to clobber the outer loop's counter. No stack is neccisary. + * to clobber the outer loop's counter. No stack is necessary. */ #include diff --git a/plugins/adplug/adplug/bmf.cpp b/plugins/adplug/adplug/bmf.cpp index 5fcd1718df..55e823eca7 100644 --- a/plugins/adplug/adplug/bmf.cpp +++ b/plugins/adplug/adplug/bmf.cpp @@ -31,21 +31,22 @@ type : Ford Knox BBStro tune : by The Brain [Razor 1911] player : ver.1.1 by ? - comment : in original player at 9th channel the feedback adlib register is not C8 but C6. + comment : in original player at 9th channel the feedback adlib register is + not C8 but C6. file(s) : DATURA.COM type : Datura BBStro tune : by The Brain [Razor 1911] player : ver.1.2 by ? - comment : inaccurate replaying, because constant outport; in original player it can be 380 or 382. + comment : inaccurate replaying, because constant outport; in original player + it can be 380 or 382. */ -#include +#include "strnlen.h" #include "bmf.h" #include "debug.h" -const unsigned char CxadbmfPlayer::bmf_adlib_registers[117] = -{ +const unsigned char CxadbmfPlayer::bmf_adlib_registers[117] = { 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xA0, 0xB0, 0xC0, 0xE0, 0xE3, 0x21, 0x24, 0x41, 0x44, 0x61, 0x64, 0x81, 0x84, 0xA1, 0xB1, 0xC1, 0xE1, 0xE4, 0x22, 0x25, 0x42, 0x45, 0x62, 0x65, 0x82, 0x85, 0xA2, 0xB2, 0xC2, 0xE2, 0xE5, @@ -57,19 +58,18 @@ const unsigned char CxadbmfPlayer::bmf_adlib_registers[117] = 0x32, 0x35, 0x52, 0x55, 0x72, 0x75, 0x92, 0x95, 0xA8, 0xB8, 0xC8, 0xF2, 0xF5 }; -const unsigned short CxadbmfPlayer::bmf_notes[12] = -{ - 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287 +const unsigned short CxadbmfPlayer::bmf_notes[12] = { + 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, + 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287 }; /* for 1.1 */ -const unsigned short CxadbmfPlayer::bmf_notes_2[12] = -{ - 0x159, 0x16D, 0x183, 0x19A, 0x1B2, 0x1CC, 0x1E8, 0x205, 0x223, 0x244, 0x267, 0x28B +const unsigned short CxadbmfPlayer::bmf_notes_2[12] = { + 0x159, 0x16D, 0x183, 0x19A, 0x1B2, 0x1CC, + 0x1E8, 0x205, 0x223, 0x244, 0x267, 0x28B }; -const unsigned char CxadbmfPlayer::bmf_default_instrument[13] = -{ +const unsigned char CxadbmfPlayer::bmf_default_instrument[13] = { 0x01, 0x01, 0x3F, 0x3F, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -80,115 +80,153 @@ CPlayer *CxadbmfPlayer::factory(Copl *newopl) bool CxadbmfPlayer::xadplayer_load() { - unsigned short ptr = 0; + size_t ptr = 0; int i; - if(xad.fmt != BMF) + if (xad.fmt != BMF) return false; #ifdef DEBUG AdPlug_LogWrite("\nbmf_load():\n\n"); #endif - if (!strncmp((char *)&tune[0],"BMF1.2",6)) - { + if (tune_size < 6) + return false; + if (!memcmp(&tune[0], "BMF1.2", 6)) { bmf.version = BMF1_2; bmf.timer = 70.0f; - } - else if (!strncmp((char *)&tune[0],"BMF1.1",6)) - { + ptr = 6; + } else if (!memcmp(&tune[0], "BMF1.1", 6)) { bmf.version = BMF1_1; bmf.timer = 68.5f; - } - else - { + ptr = 6; + } else { bmf.version = BMF0_9B; bmf.timer = 18.2f; } // copy title & author - if (bmf.version > BMF0_9B) - { - ptr = 6; - - strncpy(bmf.title,(char *)&tune[ptr],36); - bmf.title[35] = 0; - - while (tune[ptr]) { ptr++; } - ptr++; - - strncpy(bmf.author,(char *)&tune[ptr],36); - bmf.author[35] = 0; - - while (tune[ptr]) { ptr++; } - ptr++; - } - else - { - strncpy(bmf.title,xad.title,36); - strncpy(bmf.author,xad.author,36); + if (bmf.version > BMF0_9B) { + // It's unclear whether title/author can be longer than 35 chars. The + // implementation reads an arbitrarily long NUL-terminated string and + // truncates it to fit in a fixed 36 byte buffer. Doesn't make sense! + size_t len = strnlen((char *)&tune[ptr], tune_size - ptr); + if (ptr + len == tune_size) + return false; + if (len >= sizeof(bmf.title)) { + memcpy(bmf.title, &tune[ptr], sizeof(bmf.title) - 1); + bmf.title[sizeof(bmf.title) - 1] = 0; + } else { + memcpy(bmf.title, &tune[ptr], len + 1); + } + ptr += len + 1; + + len = strnlen((char *)&tune[ptr], tune_size - ptr); + if (ptr + len == tune_size) + return false; + if (len >= sizeof(bmf.author)) { + memcpy(bmf.author, &tune[ptr], sizeof(bmf.author) - 1); + bmf.author[sizeof(bmf.author) - 1] = 0; + } else { + memcpy(bmf.author, &tune[ptr], len + 1); + } + ptr += len + 1; + } else { + // bmf.version == BMF0_9B + strncpy(bmf.title, xad.title, sizeof(bmf.title)); + bmf.title[sizeof(bmf.title) - 1] = 0; + strncpy(bmf.author, xad.author, sizeof(bmf.author)); + bmf.author[sizeof(bmf.author) - 1] = 0; } // speed - if (bmf.version > BMF0_9B) - bmf.speed = tune[ptr++]; - else - bmf.speed = ((tune[ptr++] << 8) / 3) >> 8; // strange, yeh ? + if (tune_size - ptr < 1) + return false; + bmf.speed = tune[ptr++]; + if (bmf.version == BMF0_9B) + bmf.speed /= 3; // removed pointless shifts from original code // load instruments - if (bmf.version > BMF0_9B) - { - unsigned long iflags = (tune[ptr] << 24) | (tune[ptr+1] << 16) | (tune[ptr+2] << 8) | tune[ptr+3]; - ptr+=4; - - for(i=0;i<32;i++) - if (iflags & (1 << (31-i))) - { - strcpy(bmf.instruments[i].name, (char *)&tune[ptr]); - memcpy(bmf.instruments[i].data, &tune[ptr+11], 13); - ptr += 24; - } - else - { - bmf.instruments[i].name[0] = 0; - - if (bmf.version == BMF1_1) - for(int j=0;j<13;j++) - bmf.instruments[i].data[j] = bmf_default_instrument[j]; - else - for(int j=0;j<13;j++) - bmf.instruments[i].data[j] = 0; - } - } - else - { + if (bmf.version > BMF0_9B) { + if (tune_size - ptr < 4) + return false; + unsigned long iflags = (((unsigned long)tune[ptr] ) << 24) | + (((unsigned long)tune[ptr+1]) << 16) | + (((unsigned long)tune[ptr+2]) << 8) | + ((unsigned long)tune[ptr+3]); + ptr += 4; + + for (i = 0; i < 32; i++) { + if (iflags & (1 << (31-i))) { + if (tune_size - ptr < sizeof(bmf.instruments[i])) + return false; + memcpy(bmf.instruments[i].name, &tune[ptr], + sizeof(bmf.instruments[i].name) - 1); + // last char ignored since name must be 0-terminated + bmf.instruments[i].name[sizeof(bmf.instruments[i].name) - 1] = 0; + memcpy(bmf.instruments[i].data, + &tune[ptr + sizeof(bmf.instruments[i].name)], + sizeof(bmf.instruments[i].data)); + ptr += sizeof(bmf.instruments[i]); + } else if (bmf.version == BMF1_1) { + memset(bmf.instruments[i].name, 0, sizeof(bmf.instruments[i].name)); + memcpy(bmf.instruments[i].data, bmf_default_instrument, + sizeof(bmf.instruments[i].data)); + } else { + memset(&bmf.instruments[i], 0, sizeof(bmf.instruments[i])); + } + } + } else { + // bmf.version == BMF0_9B ptr = 6; - for(i=0;i<32;i++) - { - bmf.instruments[i].name[0] = 0; - memcpy(bmf.instruments[tune[ptr]].data, &tune[ptr+2],13); // bug no.1 (no instrument-table-end detection) - ptr+=15; + if (tune_size - ptr < 32 * 15) + return false; + // Should unset entries be zero or set to bmf_default_instrument? + memset(bmf.instruments, 0, sizeof(bmf.instruments)); + for (i = 0; i < 32; i++) { + size_t ip = ptr + 15 * i; + // Original code lacked check and had a comment: "bug no.1 (no + // instrument-table-end detection)" - what does that mean? + if (tune[ip] < 32) + memcpy(bmf.instruments[tune[ip]].data, &tune[ip + 2], + sizeof(bmf.instruments[tune[ip]].data)); + else // what? continue, return false, or skip rest of the table? + break; // Old comment (see above) suggests it's a table end marker. } + ptr += 32 * 15; } - + // load streams - if (bmf.version > BMF0_9B) - { - unsigned long sflags = (tune[ptr] << 24) | (tune[ptr+1] << 16) | (tune[ptr+2] << 8) | tune[ptr+3]; - ptr+=4; - - for(i=0;i<9;i++) - if (sflags & (1 << (31-i))) - ptr+=__bmf_convert_stream(&tune[ptr],i); - else + if (bmf.version > BMF0_9B) { + if (tune_size - ptr < 4) + return false; + unsigned long sflags = (((unsigned long)tune[ptr ]) << 24) | + (((unsigned long)tune[ptr+1]) << 16) | + (((unsigned long)tune[ptr+2]) << 8) | + ((unsigned long)tune[ptr+3]); + ptr += 4; + + for (i = 0; i < 9; i++) + if (sflags & (1 << (31-i))) { + long len = __bmf_convert_stream(tune + ptr, i, tune_size - ptr); + if (len < 0) + return false; + ptr += len; + } else { bmf.streams[i][0].cmd = 0xFF; - } - else - { - for(i=0;i 9) + return false; + for (i = 0; i < tune[5]; i++) { + long len = __bmf_convert_stream(tune + ptr, i, tune_size - ptr); + if (len < 0) + return false; + ptr += len; + } - for(i=tune[5];i<9;i++) + for (i = tune[5]; i < 9; i++) bmf.streams[i][0].cmd = 0xFF; } @@ -197,183 +235,159 @@ bool CxadbmfPlayer::xadplayer_load() void CxadbmfPlayer::xadplayer_rewind(int subsong) { - int i,j; - - for(i=0; i<9; i++) - { - bmf.channel[i].stream_position = 0; - bmf.channel[i].delay = 0; - bmf.channel[i].loop_position = 0; - bmf.channel[i].loop_counter = 0; - } + memset(bmf.channel, 0, sizeof(bmf.channel)); plr.speed = bmf.speed; #ifdef DEBUG - AdPlug_LogWrite("speed: %x\n",plr.speed); + AdPlug_LogWrite("speed: %x\n", plr.speed); #endif bmf.active_streams = 9; // OPL initialization - if (bmf.version > BMF0_9B) - { + if (bmf.version > BMF0_9B) { opl_write(0x01, 0x20); - + /* 1.1 */ + int i, j; if (bmf.version == BMF1_1) - for(i=0;i<9;i++) - for(j=0;j<13;j++) - opl_write(bmf_adlib_registers[13*i+j], bmf_default_instrument[j]); + for (i = 0; i < 9; i++) + for (j = 0; j < 13; j++) + opl_write(bmf_adlib_registers[13 * i + j], bmf_default_instrument[j]); /* 1.2 */ else if (bmf.version == BMF1_2) - for(i=0x20; i<0x100; i++) - opl_write(i,0xFF); // very interesting, really! + for (i = 0x20; i < 0x100; i++) + opl_write(i, 0xFF); // very interesting, really! } /* ALL */ - opl_write(0x08, 0x00); opl_write(0xBD, 0xC0); } void CxadbmfPlayer::xadplayer_update() { - for(int i=0;i<9;i++) - if (bmf.channel[i].stream_position != 0xFFFF) - if (bmf.channel[i].delay) + for (int i = 0; i < 9; i++) { + unsigned short &pos = bmf.channel[i].stream_position; + if (pos == 0xFFFF) + continue; + + if (bmf.channel[i].delay) { bmf.channel[i].delay--; - else - { + continue; + } + #ifdef DEBUG - AdPlug_LogWrite("channel %02X:\n", i); + AdPlug_LogWrite("channel %02X:\n", i); #endif - bmf_event event; - // process so-called cross-events - while (true) - { - memcpy(&event, &bmf.streams[i][bmf.channel[i].stream_position], sizeof(bmf_event)); +again: + const bmf_event &event = bmf.streams[i][pos]; #ifdef DEBUG - AdPlug_LogWrite("%02X %02X %02X %02X %02X %02X\n", - event.note,event.delay,event.volume,event.instrument, - event.cmd,event.cmd_data); + AdPlug_LogWrite("%02X %02X %02X %02X %02X %02X\n", + event.note, event.delay, event.volume, + event.instrument, event.cmd, event.cmd_data); #endif - if (event.cmd == 0xFF) - { - bmf.channel[i].stream_position = 0xFFFF; - bmf.active_streams--; - break; - } - else if (event.cmd == 0xFE) - { - bmf.channel[i].loop_position = bmf.channel[i].stream_position+1; - bmf.channel[i].loop_counter = event.cmd_data; - } - else if (event.cmd == 0xFD) - { - if (bmf.channel[i].loop_counter) - { - bmf.channel[i].stream_position = bmf.channel[i].loop_position-1; - bmf.channel[i].loop_counter--; - } - } - else - break; - - bmf.channel[i].stream_position++; - } // while (true) - - // process normal event - unsigned short pos = bmf.channel[i].stream_position; - - if (pos != 0xFFFF) + switch (event.cmd) { + // Process so-called Cross-Events + + case 0xFF: // End of Stream + pos = 0xFFFF; + bmf.active_streams--; + continue; + + case 0xFE: // Save Loop Position + bmf.channel[i].loop_position = ++pos; + bmf.channel[i].loop_counter = event.cmd_data; + goto again; + + case 0xFD: // Loop to Saved Position + if (bmf.channel[i].loop_counter) { + pos = bmf.channel[i].loop_position; + bmf.channel[i].loop_counter--; + } else { + pos++; + } + goto again; + + // Process normal event + + case 0x01: // Set Modulator Volume { - bmf.channel[i].delay = bmf.streams[i][pos].delay; - - // command ? - if (bmf.streams[i][pos].cmd) - { - unsigned char cmd = bmf.streams[i][pos].cmd; - - // 0x01: Set Modulator Volume - if (cmd == 0x01) - { - unsigned char reg = bmf_adlib_registers[13*i+2]; - - opl_write(reg, (adlib[reg] | 0x3F) - bmf.streams[i][pos].cmd_data); - } - // 0x10: Set Speed - else if (cmd == 0x10) - { - plr.speed = bmf.streams[i][pos].cmd_data; - plr.speed_counter = plr.speed; - } - } // if (bmf.streams[i][pos].cmd) - - // instrument ? - if (bmf.streams[i][pos].instrument) - { - unsigned char ins = bmf.streams[i][pos].instrument-1; - - if (bmf.version != BMF1_1) - opl_write(0xB0+i, adlib[0xB0+i] & 0xDF); - - for(int j=0;j<13;j++) - opl_write(bmf_adlib_registers[i*13+j], bmf.instruments[ins].data[j]); - } // if (bmf.streams[i][pos].instrument) - - // volume ? - if (bmf.streams[i][pos].volume) - { - unsigned char vol = bmf.streams[i][pos].volume-1; - unsigned char reg = bmf_adlib_registers[13*i+3]; - - opl_write(reg, (adlib[reg] | 0x3F) - vol); - } // if (bmf.streams[i][pos].volume) - - // note ? - if (bmf.streams[i][pos].note) - { - unsigned short note = bmf.streams[i][pos].note; - unsigned short freq = 0; - - // mute channel - opl_write(0xB0+i, adlib[0xB0+i] & 0xDF); - - // get frequency - if (bmf.version == BMF1_1) - { - if (note <= 0x60) - freq = bmf_notes_2[--note % 12]; - } - else - { - if (note != 0x7F) - freq = bmf_notes[--note % 12]; - } - - // play note - if (freq) - { - opl_write(0xB0+i, (freq >> 8) | ((note / 12) << 2) | 0x20); - opl_write(0xA0+i, freq & 0xFF); - } - } // if (bmf.streams[i][pos].note) - - bmf.channel[i].stream_position++; - } // if (pos != 0xFFFF) - - } // if (!bmf.channel[i].delay) + unsigned char reg = bmf_adlib_registers[13 * i + 2]; + // Masking cmd_data to avoid out of range writes might be a good idea, + // or do it while parsing the file. + opl_write(reg, (adlib[reg] | 0x3F) - event.cmd_data); + } + break; + + case 0x10: // Set Speed + plr.speed = event.cmd_data; + plr.speed_counter = plr.speed; + break; + } + + // Got normal event, pos != 0xFFFF + + // Process delay + bmf.channel[i].delay = event.delay; + + // Process instrument + if (event.instrument) { + unsigned char ins = event.instrument - 1; + + if (bmf.version != BMF1_1) + opl_write(0xB0 + i, adlib[0xB0 + i] & 0xDF); + + for (int j = 0; j < 13; j++) + opl_write(bmf_adlib_registers[13 * i + j], + bmf.instruments[ins].data[j]); + } + + // Process volume + if (event.volume) { + unsigned char vol = event.volume - 1; + unsigned char reg = bmf_adlib_registers[13 * i + 3]; + + // Masking cmd_data to avoid out of range writes might be a good idea, + // or do it while parsing the file. + opl_write(reg, (adlib[reg] | 0x3F) - vol); + } + + // Process note + if (event.note) { + unsigned short note = event.note - 1; + unsigned short freq = 0; + + // mute channel + opl_write(0xB0 + i, adlib[0xB0 + i] & 0xDF); + + // get frequency + if (bmf.version == BMF1_1) { + if (note < 0x60) + freq = bmf_notes_2[note % 12]; + } else { + if (note != 0x7E) // really? + freq = bmf_notes[note % 12]; + } + + // play note + if (freq) { + opl_write(0xB0 + i, (freq >> 8) | ((note / 12) << 2) | 0x20); + opl_write(0xA0 + i, freq & 0xFF); + } + } // if (event.note) + + pos++; + } // for (i) // is module loop ? - if (!bmf.active_streams) - { - for(int j=0;j<9;j++) + if (!bmf.active_streams) { + for (int j = 0; j < 9; j++) bmf.channel[j].stream_position = 0; - bmf.active_streams = 9; - + bmf.active_streams = 9; plr.looping = 1; } } @@ -415,190 +429,144 @@ unsigned int CxadbmfPlayer::xadplayer_getspeed() /* -------- Internal Functions ---------------------------- */ -int CxadbmfPlayer::__bmf_convert_stream(unsigned char *stream, int channel) +long int CxadbmfPlayer::__bmf_convert_stream(const unsigned char *stream, + int channel, unsigned long stream_size) { #ifdef DEBUG - AdPlug_LogWrite("channel %02X (note,delay,volume,instrument,command,command_data):\n",channel); - unsigned char *last = stream; + AdPlug_LogWrite("channel %02X (note, delay, volume, instrument, " + "command, command_data):\n", channel); + const unsigned char *last = stream; #endif - unsigned char *stream_start = stream; - - int pos = 0; - - while (true) - { - memset(&bmf.streams[channel][pos], 0, sizeof(bmf_event)); - - bool is_cmd = false; - - if (*stream == 0xFE) - { - // 0xFE -> 0xFF: End of Stream - bmf.streams[channel][pos].cmd = 0xFF; - - stream++; - + const unsigned char *const stream_start = stream; + const unsigned char *const stream_end = stream + stream_size; + const int last_pos = sizeof(bmf.streams[0]) / sizeof(bmf.streams[0][0]) - 1; + + // The loop exits when End of Stream (0xFE) is found (or on error). + // If the stream is too long to store, truncate it, but keep reading + // until the end marker is found and stored. + for (int pos = 0; pos <= last_pos; pos < last_pos && pos++) { + bmf_event &event = bmf.streams[channel][pos]; + memset(&event, 0, sizeof(bmf_event)); + + if (stream_end - stream < 1) + return -1; + switch (*stream) { + /* Cross-Events */ + case 0xFE: // 0xFE -> 0xFF: End of Stream + event.cmd = 0xFF; + stream++; + pos = last_pos + 1; // end loop break; - } - else if (*stream == 0xFC) - { - // 0xFC -> 0xFE xx: Save Loop Position - bmf.streams[channel][pos].cmd = 0xFE; - bmf.streams[channel][pos].cmd_data = (*(stream+1) & ((bmf.version == BMF0_9B) ? 0x7F : 0x3F)) - 1; - - stream+=2; - } - else if (*stream == 0x7D) - { - // 0x7D -> 0xFD: Loop Saved Position - bmf.streams[channel][pos].cmd = 0xFD; - - stream++; - } - else - { - if (*stream & 0x80) - { - if (*(stream+1) & 0x80) - { - if (*(stream+1) & 0x40) - { - // byte0: 1aaaaaaa = NOTE - bmf.streams[channel][pos].note = *stream & 0x7F; - // byte1: 11bbbbbb = DELAY - bmf.streams[channel][pos].delay = *(stream+1) & 0x3F; - // byte2: cccccccc = COMMAND - - stream+=2; - - is_cmd = true; - } - else - { - // byte0: 1aaaaaaa = NOTE - bmf.streams[channel][pos].note = *stream & 0x7F; - // byte1: 11bbbbbb = DELAY - bmf.streams[channel][pos].delay = *(stream+1) & 0x3F; - - stream+=2; - } // if (*(stream+1) & 0x40) - } - else - { - // byte0: 1aaaaaaa = NOTE - bmf.streams[channel][pos].note = *stream & 0x7F; - // byte1: 0bbbbbbb = COMMAND - - stream++; - - is_cmd = true; - } // if (*(stream+1) & 0x80) - } - else - { - // byte0: 0aaaaaaa = NOTE - bmf.streams[channel][pos].note = *stream & 0x7F; - - stream++; - } // if (*stream & 0x80) - } // if (*stream == 0xFE) - - // is command ? - if (is_cmd) - { - /* ALL */ - - if ((0x20 <= *stream) && (*stream <= 0x3F)) - { - // 0x20 or higher; 0x3F or lower: Set Instrument - bmf.streams[channel][pos].instrument = *stream - 0x20 + 1; + case 0xFC: // 0xFC -> 0xFE xx: Save Loop Position + event.cmd = 0xFE; + if (stream_end - stream < 2) + return -1; + event.cmd_data = (stream[1] & (bmf.version == BMF0_9B ? 0x7F : 0x3F)) - 1; + stream += 2; + break; - stream++; - } - else if (0x40 <= *stream) - { - // 0x40 or higher: Set Volume - bmf.streams[channel][pos].volume = *stream - 0x40 + 1; + case 0x7D: // 0x7D -> 0xFD: Loop to Saved Position + event.cmd = 0xFD; + stream++; + break; - stream++; + /* Normal Events */ + default: + // The event is defined by the next 1 to 4 bytes and consists of + // a note, an optional delay, and an optional command (which may + // take another data byte as argument). + // Bit 0x80 of the first byte indicates the presence of an optional + // part; if present, bits 0x80 and 0x40 of the second byte determine + // whether we have delay, command, or both. The possible forms are: + // 0NNNNNNN - 1 byte: note + // 1NNNNNNN 0CCCCCCC - 2+ bytes: note, command [data] + // 1NNNNNNN 10DDDDDD - 2 bytes: note, delay + // 1NNNNNNN 11DDDDDD CCCCCCCC - 3+ bytes: note, delay, command [data] + event.note = *stream & 0x7F; + + if (!(*stream++ & 0x80)) + break; // 1st byte is 0NNNNNNN: no delay or command; done. + + if (stream_end - stream < 1) + return -1; + if (*stream & 0x80) { + // 2nd byte is 1xDDDDDD: delay byte present + event.delay = *stream & 0x3F; + + if (!(*stream++ & 0x40)) + break; // 2nd byte is 10DDDDDD: no command; done. } - else - { - /* 0.9b */ - - if (bmf.version == BMF0_9B) - if (*stream < 0x20) - { - // 0x1F or lower: ? - stream++; - } - - /* 1.2 */ - - if (bmf.version == BMF1_2) - if (*stream == 0x01) - { - // 0x01: Set Modulator Volume -> 0x01 - bmf.streams[channel][pos].cmd = 0x01; - bmf.streams[channel][pos].cmd_data = *(stream+1); - - stream+=2; - } - else if (*stream == 0x02) - { - // 0x02: ? - stream+=2; - } - else if (*stream == 0x03) - { - // 0x03: ? - stream+=2; - } - else if (*stream == 0x04) - { - // 0x04: Set Speed -> 0x10 - bmf.streams[channel][pos].cmd = 0x10; - bmf.streams[channel][pos].cmd_data = *(stream+1); - - stream+=2; - } - else if (*stream == 0x05) - { - // 0x05: Set Carrier Volume (port 380) - bmf.streams[channel][pos].volume = *(stream+1) + 1; - - stream+=2; - } - else if (*stream == 0x06) - { - // 0x06: Set Carrier Volume (port 382) - bmf.streams[channel][pos].volume = *(stream+1) + 1; - - stream+=2; - } // if (bmf.version == BMF1_2) - - } // if ((0x20 <= *stream) && (*stream <= 0x3F)) - - } // if (is_cmd) + // If we reach this point, a command byte follows. + if (stream_end - stream < 1) + return -1; + + if (0x40 <= *stream) { + // command 0x40 or higher: Set Volume + event.volume = *stream - 0x40 + 1; // masking needed? + stream++; + } else if ((0x20 <= *stream) && (*stream <= 0x3F)) { + // command 0x20 to 0x3F: Set Instrument + event.instrument = *stream - 0x20 + 1; + stream++; + } else { + // command 0x1F or lower + if (bmf.version == BMF0_9B) { + // version 0.9b, command 0x1F or lower: ? + stream++; + } else if (bmf.version == BMF1_2) { + /* 1.2 */ + if (0x01 <= *stream && *stream <= 0x06) { + if (stream_end - stream < 2) + return -1; + switch (*stream) { + case 0x01: // Set Modulator Volume -> 0x01 + event.cmd = 0x01; + event.cmd_data = stream[1]; + break; + + case 0x02: // ? + case 0x03: // ? + break; + + case 0x04: // Set Speed -> 0x10 + event.cmd = 0x10; + event.cmd_data = stream[1]; + break; + + case 0x05: // Set Carrier Volume (port 380) + event.volume = stream[1] + 1; // masking needed? + break; + + case 0x06: // Set Carrier Volume (port 382) + event.volume = stream[1] + 1; // masking needed? + break; + } + stream += 2; + } // if (0x01 <= *stream && *stream <= 0x06) + + } // if (bmf.version == BMF1_2) + + // The following cases are not handled and don't even + // increment the stream pointer (that's probably wrong): + // * version 1.1, commands 0x1F or lower + // * version 1.2, commands 0x00 and 0x07 to 0x1F + + } // ! if (0x40 <= *stream) + + } // switch #ifdef DEBUG - AdPlug_LogWrite("%02X %02X %02X %02X %02X %02X <---- ", - bmf.streams[channel][pos].note, - bmf.streams[channel][pos].delay, - bmf.streams[channel][pos].volume, - bmf.streams[channel][pos].instrument, - bmf.streams[channel][pos].cmd, - bmf.streams[channel][pos].cmd_data - ); - for(int zz=0;zz<(stream-last);zz++) - AdPlug_LogWrite("%02X ",last[zz]); - AdPlug_LogWrite("\n"); - last=stream; + AdPlug_LogWrite("%02X %02X %02X %02X %02X %02X <---- ", + event.note, event.delay, event.volume, + event.instrument, event.cmd, event.cmd_data); + for (int zz = 0; zz < (stream - last); zz++) + AdPlug_LogWrite(" %02X", last[zz]); + AdPlug_LogWrite("\n"); + last = stream; #endif - pos++; - } // while (true) + } // for - return (stream - stream_start); + return stream - stream_start; } diff --git a/plugins/adplug/adplug/bmf.h b/plugins/adplug/adplug/bmf.h index cc36c0e0c3..0f73831b0f 100644 --- a/plugins/adplug/adplug/bmf.h +++ b/plugins/adplug/adplug/bmf.h @@ -88,5 +88,6 @@ class CxadbmfPlayer: public CxadPlayer static const unsigned short bmf_notes_2[12]; static const unsigned char bmf_default_instrument[13]; - int __bmf_convert_stream(unsigned char *stream, int channel); + long int __bmf_convert_stream(const unsigned char *stream, int channel, + unsigned long stream_size); }; diff --git a/plugins/adplug/adplug/cff.cpp b/plugins/adplug/adplug/cff.cpp index e37646a4a5..e1009b381a 100644 --- a/plugins/adplug/adplug/cff.cpp +++ b/plugins/adplug/adplug/cff.cpp @@ -25,8 +25,6 @@ */ #include -#include -#include #include "cff.h" @@ -39,122 +37,116 @@ CPlayer *CcffLoader::factory(Copl *newopl) bool CcffLoader::load(const std::string &filename, const CFileProvider &fp) { - binistream *f = fp.open(filename); if(!f) return false; - const unsigned char conv_inst[11] = { 2,1,10,9,4,3,6,5,0,8,7 }; - const unsigned short conv_note[12] = { 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE }; + static const unsigned char conv_inst[11] = { + 2, 1, 10, 9, 4, 3, 6, 5, 0, 8, 7 + }; + static const unsigned short conv_note[12] = { + 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, + 0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE + }; - int i,j,k,t=0; + binistream *f = fp.open(filename); + if (!f) return false; // '' - signed ? - f->readString(header.id, 16); - header.version = f->readInt(1); header.size = f->readInt(2); - header.packed = f->readInt(1); f->readString((char *)header.reserved, 12); - if (memcmp(header.id,"""\x1A\xDE\xE0",16)) - { fp.close(f); return false; } - - unsigned char *module = new unsigned char [0x10000]; - - // packed ? - if (header.packed) - { - cff_unpacker *unpacker = new cff_unpacker; - - unsigned char *packed_module = new unsigned char [header.size + 4]; - - memset(packed_module,0,header.size + 4); + f->readString(header.id, sizeof(header.id)); + header.version = f->readInt(1); + header.size = f->readInt(2); + header.packed = f->readInt(1); + f->ignore(sizeof(header.reserved)); + + if (memcmp(header.id, "\x1A\xDE\xE0", sizeof(header.id)) || + header.size < 16 /* for unpacker's id; real minimum is bigger */) { + fp.close(f); + return false; + } - f->readString((char *)packed_module, header.size); - fp.close(f); + // always allocate padding needed for unpacker + unsigned char *module = new unsigned char[header.size + 8]; + size_t module_size = header.size; + f->readString((char *)module, header.size); + fp.close(f); - if (!unpacker->unpack(packed_module,module)) - { - delete unpacker; - delete [] packed_module; - delete [] module; - return false; - } + // unpack + if (header.packed) { + unsigned char *packed_module = module; + memset(packed_module + header.size, 0, 8); + module = new unsigned char[0x10000]; + cff_unpacker *unpacker = new cff_unpacker; + module_size = unpacker->unpack(packed_module,module); delete unpacker; delete [] packed_module; + } - if (memcmp(&module[0x5E1],"CUD-FM-File - SEND A POSTCARD -",31)) - { - delete [] module; - return false; - } - } - else - { - f->readString((char *)module, header.size); - fp.close(f); - } + if (module_size < 0x669 + 64 * 9 * 3 || + memcmp(&module[0x5E1], "CUD-FM-File - SEND A POSTCARD -", 31)) { + delete [] module; + return false; + } // init CmodPlayer realloc_instruments(47); realloc_order(64); - realloc_patterns(36,64,9); + realloc_patterns(36, 64, 9); init_notetable(conv_note); init_trackord(); // load instruments - for (i=0;i<47;i++) - { - memcpy(&instruments[i],&module[i*32],sizeof(cff_instrument)); - - for (j=0;j<11;j++) - inst[i].data[conv_inst[j]] = instruments[i].data[j]; + for (int i = 0; i < 47; i++) { + memcpy(&instruments[i], &module[i * 32], sizeof(cff_instrument) - 1); + instruments[i].name[20] = 0; - instruments[i].name[20] = 0; - } + for (int j = 0; j < 11; j++) + inst[i].data[conv_inst[j]] = instruments[i].data[j]; + } // number of patterns nop = module[0x5E0]; + if (nop < 1 || nop > 36 || (size_t)0x669 + nop * 64 * 9 * 3 > module_size) { + delete [] module; + return false; + } // load title & author - memcpy(song_title,&module[0x614],20); - memcpy(song_author,&module[0x600],20); + memcpy(song_title, &module[0x614], sizeof(song_title)); + memcpy(song_author, &module[0x600], sizeof(song_author)); // load order - memcpy(order,&module[0x628],64); + memcpy(order, &module[0x628], 64); // load tracks - for (i=0;ibyte0 == 0x6D) tracks[t][k].note = 127; else - if (event->byte0) - tracks[t][k].note = event->byte0; + tracks[t][k].note = event->byte0; if (event->byte2) old_event_byte2[j] = event->byte2; // convert effect - switch (event->byte1) - { + switch (event->byte1) { case 'I': // set instrument - tracks[t][k].inst = event->byte2 + 1; + if (event->byte2 < 47) // ignore invalid instruments + tracks[t][k].inst = event->byte2 + 1; tracks[t][k].param1 = tracks[t][k].param2 = 0; break; case 'H': // set tempo tracks[t][k].command = 7; - if (event->byte2 < 16) - { + if (event->byte2 < 16) { tracks[t][k].param1 = 0x07; tracks[t][k].param2 = 0x0D; - } + } break; case 'A': // set speed @@ -213,30 +205,25 @@ bool CcffLoader::load(const std::string &filename, const CFileProvider &fp) case 'D': // fine volume slide tracks[t][k].command = 14; - if (old_event_byte2[j] & 15) - { + if (old_event_byte2[j] & 15) { // slide down tracks[t][k].param1 = 5; tracks[t][k].param2 = old_event_byte2[j] & 15; - } - else - { + } else { // slide up tracks[t][k].param1 = 4; tracks[t][k].param2 = old_event_byte2[j] >> 4; - } + } break; case 'J': // arpeggio tracks[t][k].param1 = old_event_byte2[j] >> 4; tracks[t][k].param2 = old_event_byte2[j] & 15; break; - } - } - - t++; - } - } + } + } + } + } delete [] module; @@ -244,14 +231,14 @@ bool CcffLoader::load(const std::string &filename, const CFileProvider &fp) restartpos = 0; // order length - for (i=0;i<64;i++) - { - if (order[i] >= 0x80) - { - length = i; - break; - } - } + if (order[0] >= 0x36) // empty order list or invalid pattern + return false; + for (length = 1; length < 64; length++) { + if (order[length] & 0x80) // end marker, keep length + break; + if (order[length] >= 36) // invalid pattern number + return false; + } // default tempo bpm = 0x7D; @@ -266,13 +253,12 @@ void CcffLoader::rewind(int subsong) CmodPlayer::rewind(subsong); // default instruments - for (int i=0;i<9;i++) - { + for (int i = 0; i < 9; i++) { channel[i].inst = i; channel[i].vol1 = 63 - (inst[i].data[10] & 63); channel[i].vol2 = 63 - (inst[i].data[9] & 63); - } + } } std::string CcffLoader::gettype() @@ -285,17 +271,20 @@ std::string CcffLoader::gettype() std::string CcffLoader::gettitle() { - return std::string(song_title,20); + return std::string(song_title, sizeof(song_title)); } std::string CcffLoader::getauthor() { - return std::string(song_author,20); + return std::string(song_author, sizeof(song_author)); } std::string CcffLoader::getinstrument(unsigned int n) { - return std::string(instruments[n].name); + if (n < getinstruments()) + return std::string(instruments[n].name); + else + return std::string(); } unsigned int CcffLoader::getinstruments() @@ -303,9 +292,7 @@ unsigned int CcffLoader::getinstruments() return 47; } -/* -------- Private Methods ------------------------------- */ - -#ifdef _WIN32 +#if defined(_WIN32) && defined(_MSC_VER) #pragma warning(disable:4244) #pragma warning(disable:4018) #endif @@ -313,9 +300,9 @@ unsigned int CcffLoader::getinstruments() /* Lempel-Ziv-Tyr ;-) */ -long CcffLoader::cff_unpacker::unpack(unsigned char *ibuf, unsigned char *obuf) +size_t CcffLoader::cff_unpacker::unpack(unsigned char *ibuf, unsigned char *obuf) { - if (memcmp(ibuf,"YsComp""\x07""CUD1997""\x1A\x04",16)) + if (memcmp(ibuf, "YsComp\007CUD1997\x1A\x04", 16)) return 0; input = ibuf + 16; @@ -323,148 +310,118 @@ long CcffLoader::cff_unpacker::unpack(unsigned char *ibuf, unsigned char *obuf) output_length = 0; - heap = (unsigned char *)malloc(0x10000); - dictionary = (unsigned char **)malloc(sizeof(unsigned char *)*0x8000); + heap = new unsigned char[0x10000]; + dictionary = new unsigned char *[0x8000]; - memset(heap,0,0x10000); - memset(dictionary,0,0x8000); - - cleanup(); - if(!startup()) - goto out; + if(!start_block()) + goto fail; // LZW - while (1) - { - new_code = get_code(); - - // 0x00: end of data - if (new_code == 0) - break; + for (;;) { + switch (unsigned long new_code = get_code()) { + case 0x00: // end of data + goto done; - // 0x01: end of block - if (new_code == 1) - { - cleanup(); - if(!startup()) - goto out; + case 0x01: // end of block + if (!start_block()) goto fail; + break; - continue; - } + case 0x02: // expand code length + code_length++; + if (code_length > 16) goto fail; + break; - // 0x02: expand code length - if (new_code == 2) - { - code_length++; + case 0x03: { // RLE + unsigned char repeat_length = get_code(2) + 1; - continue; - } + unsigned char length = 4 << get_code(2); + size_t repeat_counter = get_code(length); - // 0x03: RLE - if (new_code == 3) - { - unsigned char old_code_length = code_length; + size_t end = output_length + repeat_counter * repeat_length; + if (repeat_length > output_length || + repeat_counter > 0x10000 || end > 0x10000) + goto fail; - code_length = 2; + while (output_length < end) + put_string(&output[output_length - repeat_length], repeat_length); - unsigned char repeat_length = get_code() + 1; - - code_length = 4 << get_code(); - - unsigned long repeat_counter = get_code(); - - if(output_length + repeat_counter * repeat_length > 0x10000) { - output_length = 0; - goto out; - } - - for (unsigned int i=0;i= (0x104 + dictionary_length)) - { + default: + if (new_code >= 0x104 + dictionary_length) { // dictionary <- old.code.string + old.code.char the_string[++the_string[0]] = the_string[1]; - } - else - { + } else { // dictionary <- old.code.string + new.code.char unsigned char temp_string[256]; - translate_code(new_code,temp_string); + translate_code(new_code, temp_string); the_string[++the_string[0]] = temp_string[1]; - } + } expand_dictionary(the_string); // output <- new.code.string - translate_code(new_code,the_string); - - if(output_length + the_string[0] > 0x10000) { - output_length = 0; - goto out; - } - - for (int i=0;i>= code_length; - bits_left -= code_length; + bits_buffer = (unsigned long)(bits >> bitlength); + bits_left -= bitlength; return code; } void CcffLoader::cff_unpacker::translate_code(unsigned long code, unsigned char *string) { - unsigned char translated_string[256]; + if (code >= 0x104 + dictionary_length) { // invalid code + string[0] = string[1] = 0; + } else if (code >= 0x104) { // dictionary entry + memcpy(string, dictionary[code - 0x104], dictionary[code - 0x104][0] + 1); + } else { // single character + string[0] = 1; + string[1] = (code - 4) & 0xFF; + } +} - if (code >= 0x104) - { - memcpy(translated_string,dictionary[code - 0x104],(*(dictionary[code - 0x104])) + 1); - } - else - { - translated_string[0] = 1; - translated_string[1] = (code - 4) & 0xFF; - } +bool CcffLoader::cff_unpacker::put_string(unsigned char *string, size_t length) +{ + if (output_length + length > 0x10000) return false; + + memcpy(&output[output_length], string, length); + output_length += length; - memcpy(string,translated_string,256); + return true; } -void CcffLoader::cff_unpacker::cleanup() +bool CcffLoader::cff_unpacker::start_block() { code_length = 9; @@ -473,40 +430,27 @@ void CcffLoader::cff_unpacker::cleanup() heap_length = 0; dictionary_length = 0; + + return start_string(); } -int CcffLoader::cff_unpacker::startup() +bool CcffLoader::cff_unpacker::start_string() { - old_code = get_code(); - - translate_code(old_code,the_string); - - if(output_length + the_string[0] > 0x10000) { - output_length = 0; - return 0; - } - - for (int i=0;i= 0xF0) + if (string[0] >= 0xF0 || heap_length + string[0] + 1 > 0x10000) return; - memcpy(&heap[heap_length],string,string[0] + 1); - - dictionary[dictionary_length] = &heap[heap_length]; - - dictionary_length++; - - heap_length += (string[0] + 1); + memcpy(&heap[heap_length], string, string[0] + 1); + dictionary[dictionary_length++] = &heap[heap_length]; + heap_length += string[0] + 1; } -#ifdef _WIN32 +#if defined(_WIN32) && defined(_MSC_VER) #pragma warning(default:4244) #pragma warning(default:4018) #endif diff --git a/plugins/adplug/adplug/cff.h b/plugins/adplug/adplug/cff.h index 55224d587c..6b8e8efa2a 100644 --- a/plugins/adplug/adplug/cff.h +++ b/plugins/adplug/adplug/cff.h @@ -43,27 +43,30 @@ class CcffLoader: public CmodPlayer { public: - long unpack(unsigned char *ibuf, unsigned char *obuf); + size_t unpack(unsigned char *ibuf, unsigned char *obuf); private: - unsigned long get_code(); + unsigned long get_code(unsigned char bitlength); + unsigned long get_code() { return get_code(code_length); } + void translate_code(unsigned long code, unsigned char *string); + bool put_string(unsigned char *string, size_t length); + bool put_string() { return put_string(&the_string[1], the_string[0]); } - void cleanup(); - int startup(); + bool start_block(); + bool start_string(); void expand_dictionary(unsigned char *string); unsigned char *input; unsigned char *output; - long output_length; + size_t output_length; unsigned char code_length; - + unsigned char bits_left; unsigned long bits_buffer; - unsigned int bits_left; unsigned char *heap; unsigned char **dictionary; @@ -71,8 +74,6 @@ class CcffLoader: public CmodPlayer unsigned int heap_length; unsigned int dictionary_length; - unsigned long old_code,new_code; - unsigned char the_string[256]; }; diff --git a/plugins/adplug/adplug/cmf.cpp b/plugins/adplug/adplug/cmf.cpp index 474188da7a..cb1fc3d02a 100644 --- a/plugins/adplug/adplug/cmf.cpp +++ b/plugins/adplug/adplug/cmf.cpp @@ -64,7 +64,7 @@ // so any that aren't overridden are still available for use with these default // patches. The Word Rescue CMFs are good examples of songs that rely on these // default patches. -uint8_t cDefaultPatches[] = +static const uint8_t cDefaultPatches[] = "\x01\x11\x4F\x00\xF1\xD2\x53\x74\x00\x00\x06" "\x07\x12\x4F\x00\xF2\xF2\x60\x72\x00\x00\x08" "\x31\xA1\x1C\x80\x51\x54\x03\x67\x00\x00\x0E" @@ -209,6 +209,10 @@ bool CcmfPlayer::load(const std::string &filename, const CFileProvider &fp) // Load the MIDI data into memory f->seek(this->cmfHeader.iMusicOffset); this->iSongLen = fp.filesize(f) - this->cmfHeader.iMusicOffset; + if (this->iSongLen <= 0) { // invalid offset value + fp.close(f); + return false; + } this->data = new unsigned char[this->iSongLen]; f->readString((char *)data, this->iSongLen); @@ -225,7 +229,7 @@ bool CcmfPlayer::update() // Read in the next event while (!this->iDelayRemaining) { - uint8_t iCommand = this->data[this->iPlayPointer++]; + uint8_t iCommand = this->iPlayPointer < this->iSongLen ? this->data[this->iPlayPointer++] : 0; if ((iCommand & 0x80) == 0) { // Running status, use previous command this->iPlayPointer--; @@ -236,12 +240,14 @@ bool CcmfPlayer::update() uint8_t iChannel = iCommand & 0x0F; switch (iCommand & 0xF0) { case 0x80: { // Note off (two data bytes) + if (this->iPlayPointer > this->iSongLen - 2) break; uint8_t iNote = this->data[this->iPlayPointer++]; uint8_t iVelocity = this->data[this->iPlayPointer++]; // release velocity this->cmfNoteOff(iChannel, iNote, iVelocity); break; } case 0x90: { // Note on (two data bytes) + if (this->iPlayPointer > this->iSongLen - 2) break; uint8_t iNote = this->data[this->iPlayPointer++]; uint8_t iVelocity = this->data[this->iPlayPointer++]; // attack velocity if (iVelocity) { @@ -272,29 +278,34 @@ bool CcmfPlayer::update() break; } case 0xA0: { // Polyphonic key pressure (two data bytes) + if (this->iPlayPointer > this->iSongLen - 2) break; uint8_t iNote = this->data[this->iPlayPointer++]; uint8_t iPressure = this->data[this->iPlayPointer++]; AdPlug_LogWrite("CMF: Key pressure not yet implemented! (wanted ch%d/note %d set to %d)\n", iChannel, iNote, iPressure); break; } case 0xB0: { // Controller (two data bytes) + if (this->iPlayPointer > this->iSongLen - 2) break; uint8_t iController = this->data[this->iPlayPointer++]; uint8_t iValue = this->data[this->iPlayPointer++]; this->MIDIcontroller(iChannel, iController, iValue); break; } case 0xC0: { // Instrument change (one data byte) + if (this->iPlayPointer >= this->iSongLen) break; uint8_t iNewInstrument = this->data[this->iPlayPointer++]; this->chMIDI[iChannel].iPatch = iNewInstrument; AdPlug_LogWrite("CMF: Remembering MIDI channel %d now uses patch %d\n", iChannel, iNewInstrument); break; } case 0xD0: { // Channel pressure (one data byte) + if (this->iPlayPointer >= this->iSongLen) break; uint8_t iPressure = this->data[this->iPlayPointer++]; AdPlug_LogWrite("CMF: Channel pressure not yet implemented! (wanted ch%d set to %d)\n", iChannel, iPressure); break; } case 0xE0: { // Pitch bend (two data bytes) + if (this->iPlayPointer > this->iSongLen - 2) break; uint8_t iLSB = this->data[this->iPlayPointer++]; uint8_t iMSB = this->data[this->iPlayPointer++]; uint16_t iValue = (iMSB << 7) | iLSB; @@ -307,26 +318,31 @@ bool CcmfPlayer::update() case 0xF0: // System message (arbitrary data bytes) switch (iCommand) { case 0xF0: { // Sysex - uint8_t iNextByte; + uint8_t iNextByte = 0; AdPlug_LogWrite("Sysex message: "); - do { + while ((iNextByte & 0x80) == 0 && this->iPlayPointer < this->iSongLen) { iNextByte = this->data[this->iPlayPointer++]; AdPlug_LogWrite("%02X", iNextByte); - } while ((iNextByte & 0x80) == 0); + } AdPlug_LogWrite("\n"); // This will have read in the terminating EOX (0xF7) message too break; } case 0xF1: // MIDI Time Code Quarter Frame - this->data[this->iPlayPointer++]; // message data (ignored) + if (this->iPlayPointer < this->iSongLen) + (void)this->data[this->iPlayPointer++]; // message data (ignored) break; case 0xF2: // Song position pointer - this->data[this->iPlayPointer++]; // message data (ignored) - this->data[this->iPlayPointer++]; + if (this->iPlayPointer < this->iSongLen - 1) { + (void)this->data[this->iPlayPointer++]; // message data (ignored) + (void)this->data[this->iPlayPointer++]; + } break; case 0xF3: // Song select - this->data[this->iPlayPointer++]; // message data (ignored) - AdPlug_LogWrite("CMF: MIDI Song Select is not implemented.\n"); + if (this->iPlayPointer < this->iSongLen - 1) { + (void)this->data[this->iPlayPointer++]; // message data (ignored) + AdPlug_LogWrite("CMF: MIDI Song Select is not implemented.\n"); + } break; case 0xF6: // Tune request break; @@ -348,6 +364,7 @@ bool CcmfPlayer::update() this->iPlayPointer = 0; // for repeat in endless-play mode break; case 0xFF: { // System reset, used as meta-events in a MIDI file + if (this->iPlayPointer >= this->iSongLen) break; uint8_t iEvent = this->data[this->iPlayPointer++]; switch (iEvent) { case 0x2F: // end of track @@ -489,7 +506,7 @@ uint32_t CcmfPlayer::readMIDINumber() { uint32_t iValue = 0; for (int i = 0; i < 4; i++) { - uint8_t iNext = this->data[this->iPlayPointer++]; + uint8_t iNext = this->iPlayPointer < this->iSongLen ? this->data[this->iPlayPointer++] : 0; iValue <<= 7; iValue |= (iNext & 0x7F); // ignore the MSB if ((iNext & 0x80) == 0) break; // last byte has the MSB unset diff --git a/plugins/adplug/adplug/cmfmcsop.cpp b/plugins/adplug/adplug/cmfmcsop.cpp index 995863037b..0306f84237 100644 --- a/plugins/adplug/adplug/cmfmcsop.cpp +++ b/plugins/adplug/adplug/cmfmcsop.cpp @@ -75,8 +75,8 @@ channelSlots[] = { {0, 3}, {1, 4}, {2, 5}, {6, 9}, {7, 10}, {8, 11}, {12, 15}, { /* Note that - op_table[chan] == slotRegisterOffets[channelSlots[chan].slotOp1] - op_table[chan] + 3 == slotRegisterOffets[channelSlots[chan].slotOp2] + op_table[chan] == slotRegisterOffsets[channelSlots[chan].slotOp1] + op_table[chan] + 3 == slotRegisterOffsets[channelSlots[chan].slotOp2] However, the slot-numbers are needed for rhythm mode, thus the extra tables. */ @@ -197,7 +197,7 @@ bool CcmfmacsoperaPlayer::loadInstruments(binistream* f, int nrOfInstruments) }; for (int i = 0; i < nrOfInstruments; ++i) { - for (int j = 0; j < sizeof(loadOffsets)/sizeof(loadOffsets[0]); ++j) { + for (unsigned int j = 0; j < sizeof(loadOffsets)/sizeof(loadOffsets[0]); ++j) { int v = f->readInt(2); if (loadOffsets[j] >= 0 ) { *(int16_t*)((char*)&instruments[i] + loadOffsets[j]) = v; @@ -403,16 +403,16 @@ void CcmfmacsoperaPlayer::setVolume(int channelNr, int vol) bool CcmfmacsoperaPlayer::advanceRow() { for (;;) { - if (currentRow < 0 || ++currentRow >= 64) { + if (++currentRow >= 64) { // next pattern currentRow = 0; currentPatternIndex = 0; do { - currentOrderIndex++; + currentOrderIndex++; // overflows ~0 into 0 when needed // check bounds - if (currentOrderIndex < 0 || currentOrderIndex >= sizeof(patternOrder) / sizeof(patternOrder[0])) + if (currentOrderIndex >= (sizeof(patternOrder) / sizeof(patternOrder[0]))) return false; // end of song? @@ -421,13 +421,13 @@ bool CcmfmacsoperaPlayer::advanceRow() } while (patternOrder[currentOrderIndex] >= patterns.size()); // loop to skip invalid pattern references - AdPlug_LogWrite("order %d, pattern %d\n", currentOrderIndex, patternOrder[currentOrderIndex]); + AdPlug_LogWrite("order %u, pattern %d\n", currentOrderIndex, patternOrder[currentOrderIndex]); } // check for pattern break const Pattern &p = patterns[patternOrder[currentOrderIndex]]; if (currentPatternIndex < p.size() && p[currentPatternIndex].row == currentRow && p[currentPatternIndex].note == 1) { - currentRow = -1; + currentRow = 64; } else // no pattern break, done! break; @@ -459,7 +459,7 @@ void CcmfmacsoperaPlayer::processNoteEvent(const CcmfmacsoperaPlayer::NoteEvent bool CcmfmacsoperaPlayer::update() { - AdPlug_LogWrite( "%2d: ", currentRow); + AdPlug_LogWrite( "%2u: ", currentRow); const Pattern& p = patterns[patternOrder[currentOrderIndex]]; @@ -491,8 +491,8 @@ bool CcmfmacsoperaPlayer::update() void CcmfmacsoperaPlayer::resetPlayer() { - currentRow = -1; - currentOrderIndex = -1; + currentRow = 64; + currentOrderIndex = ~0; advanceRow(); } diff --git a/plugins/adplug/adplug/cmfmcsop.h b/plugins/adplug/adplug/cmfmcsop.h index 26c471c128..bcbc4a2bf8 100644 --- a/plugins/adplug/adplug/cmfmcsop.h +++ b/plugins/adplug/adplug/cmfmcsop.h @@ -85,16 +85,16 @@ class CcmfmacsoperaPlayer: public CPlayer bool rhythmMode; bool songDone; - int nrOfPatterns; - int16_t patternOrder[99]; - int nrOfOrders; + int nrOfPatterns; + uint16_t patternOrder[99]; + int nrOfOrders; std::vector instruments; std::vector patterns; - int currentOrderIndex; - int currentRow; - int currentPatternIndex; + unsigned int currentOrderIndex; + unsigned int currentRow; + unsigned int currentPatternIndex; const Instrument* channelCurrentInstrument[11]; int current0xBx[9]; diff --git a/plugins/adplug/adplug/coktel.cpp b/plugins/adplug/adplug/coktel.cpp new file mode 100644 index 0000000000..122ebaec88 --- /dev/null +++ b/plugins/adplug/adplug/coktel.cpp @@ -0,0 +1,272 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2008 Simon Peter , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * coktel.cpp - Coktel Vision ADL player by Stas'M + * + * REFERENCES: + * http://www.vgmpf.com/Wiki/index.php?title=ADL_(Coktel_Vision) + * https://github.com/DrMcCoy/CoktelADL2VGM/blob/master/src/adlib/adlplayer.cpp + * https://github.com/scummvm/scummvm/blob/master/engines/gob/sound/adlplayer.cpp + */ + +#include + +#include "coktel.h" + +#ifdef DEBUG +#include "debug.h" +#endif + +/*** public methods *************************************/ + +CPlayer *CcoktelPlayer::factory(Copl *newopl) +{ + return new CcoktelPlayer(newopl); +} + +bool CcoktelPlayer::load(const std::string &filename, const CFileProvider &fp) +{ + binistream *f = fp.open(filename); + if (!f) + return false; + + // file validation + if (!fp.extension(filename, ".adl")) + { + fp.close(f); + return false; + } + if (fp.filesize(f) < COK_MIN_SIZE) + { + fp.close(f); + return false; + } + + soundMode = static_cast(f->readInt(1)); + nrTimbre = static_cast(f->readInt(1)); + uint8_t reserved = static_cast(f->readInt(1)); + + // validate header and data size + if (soundMode > 1 || + nrTimbre == 0xFF || + reserved != 0 || + fp.filesize(f) < (unsigned)(COK_HEADER_LEN + (nrTimbre + 1) * TIMBRE_DEF_SIZE + 1)) + { + fp.close(f); + return false; + } + + // read timbre data + nrTimbre++; + insts = new adl_inst[nrTimbre]; + for (int i = 0; i < nrTimbre; i++) + { + for (int j = 0; j < ADLIB_INST_LEN; j++) + { + uint16_t val = static_cast(f->readInt(2)); + insts[i].initial[j] = val & 0xFF; + } + insts[i].backend_index = -1; + } + + // read MIDI data + size = fp.filesize(f) - COK_HEADER_LEN - nrTimbre * TIMBRE_DEF_SIZE; + data = new uint8_t[size]; + f->readString((char *)data, size); + + fp.close(f); + rewind(0); + return true; +} + +void CcoktelPlayer::frontend_rewind(int subsong) +{ + pos = 0; + songend = false; + first_tick = false; + + SetRhythmMode(soundMode); + + // reset modified instruments + for (int i = 0; i < nrTimbre; i++) + { + memcpy(insts[i].modified, insts[i].initial, ADLIB_INST_LEN); + insts[i].backend_index = load_instrument_data(&insts[i].initial[0], ADLIB_INST_LEN); + } + // reset voices + for (int i = 0; i < MAX_VOICES; i++) + { + timbre[i] = 0; + } + for (int i = 0; i < (soundMode ? kNumPercussiveVoices : kNumMelodicVoices); i++) + { + SetInstrument(i, insts[timbre[i]].backend_index); + SetVolume(i, 127); + } + + counter = 0; + ticks = 0; + modifyTimbre = 0xFF; +} + +void CcoktelPlayer::executeCommand() +{ + uint8_t status, voice, note, val; + uint16_t pitch; + + // execute MIDI command + status = data[pos++]; + if (status == COK_END_OF_SONG) + { + pos = size; + } + else if (status == COK_SET_MOD_TIMBRE) + { + modifyTimbre = data[pos++]; + } + else if (status > COK_MODIFY_TIMBRE) + { + note = data[pos++]; // what parameter to modify + val = data[pos++]; // set this value for specified parameter + + if (insts && modifyTimbre != 0xFF && modifyTimbre < nrTimbre) + { + insts[modifyTimbre].modified[note] = val; + insts[modifyTimbre].backend_index = load_instrument_data(&insts[modifyTimbre].modified[0], ADLIB_INST_LEN); + + // update timbre for voices where it's used + for (int i = 0; i < (soundMode ? kNumPercussiveVoices : kNumMelodicVoices); i++) + { + if (timbre[i] == modifyTimbre) + { + SetInstrument(i, insts[modifyTimbre].backend_index); + } + } + } + } + else + { + // voice command + voice = status & 0xF; + + switch (status & 0xF0) + { + case COK_NOTE_ON_VOL: + note = data[pos++]; + val = data[pos++]; + if (voice >= MAX_VOICES) + break; + SetVolume(voice, val); + NoteOn(voice, note); + break; + + case COK_NOTE_OFF: + if (voice >= MAX_VOICES) + break; + NoteOff(voice); + break; + + case COK_NOTE_ON: + note = data[pos++]; + if (voice >= MAX_VOICES) + break; + NoteOn(voice, note); + break; + + case COK_PITCH_BEND: + pitch = data[pos++] << 7; + if (voice >= MAX_VOICES) + break; + ChangePitch(voice, pitch); + break; + + case COK_VOLUME_SLIDE: + val = data[pos++]; + if (voice >= MAX_VOICES) + break; + SetVolume(voice, val); + break; + + case COK_TIMBRE_CHANGE: + val = data[pos++]; + if (voice >= MAX_VOICES) + break; + if (insts) + { + if (val < nrTimbre) + { + timbre[voice] = val; + SetInstrument(voice, insts[val].backend_index); + } + #ifdef DEBUG + else + { + AdPlug_LogWrite("Timbre not found: %d\n", val); + } + #endif + } + break; + + default: + #ifdef DEBUG + AdPlug_LogWrite("Unsupported command: 0x%02X. Stopping playback.\n", status); + #endif + pos = size; + break; + } + } +} + +bool CcoktelPlayer::update() +{ + if (pos >= size) + { + rewind(0); + songend = true; + } + if (!counter) + { + ticks = data[pos++]; + if (ticks & 0x80) + ticks = ((ticks & ~0x80) << 8) | data[pos++]; + if (ticks && !first_tick) + { + // skip first delay + ticks = 0; + first_tick = true; + } + } + if (++counter >= ticks) + { + counter = 0; + while (pos < size) + { + executeCommand(); + if (pos >= size) + { + return false; + } + else if (!data[pos]) // if next delay is zero + { + pos++; + } + else break; + } + } + return !songend; +} diff --git a/plugins/adplug/adplug/coktel.h b/plugins/adplug/adplug/coktel.h new file mode 100644 index 0000000000..1f33d1303b --- /dev/null +++ b/plugins/adplug/adplug/coktel.h @@ -0,0 +1,105 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2005 Simon Peter , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * coktel.h - Coktel Vision ADL player by Stas'M + * + * REFERENCES: + * http://www.vgmpf.com/Wiki/index.php?title=ADL_(Coktel_Vision) + * https://github.com/DrMcCoy/CoktelADL2VGM/blob/master/src/adlib/adlplayer.cpp + * https://github.com/scummvm/scummvm/blob/master/engines/gob/sound/adlplayer.cpp + */ + +#ifndef H_ADPLUG_COKTELPLAYER +#define H_ADPLUG_COKTELPLAYER + +#include "composer.h" + +#define COK_NOTE_ON_VOL 0x00 +#define COK_NOTE_OFF 0x80 +#define COK_NOTE_ON 0x90 +#define COK_PITCH_BEND 0xA0 +#define COK_VOLUME_SLIDE 0xB0 +#define COK_TIMBRE_CHANGE 0xC0 +#define COK_MODIFY_TIMBRE 0xD0 +#define COK_SET_MOD_TIMBRE 0xFE +#define COK_END_OF_SONG 0xFF + +#define TIMBRE_DEF_SIZE (ADLIB_INST_LEN * sizeof(int16_t)) // 28 * 2 = 56 +#define COK_HEADER_LEN 3 +#define COK_MIN_SIZE (COK_HEADER_LEN + TIMBRE_DEF_SIZE + 1) // 60 + +class CcoktelPlayer: public CcomposerBackend +{ +public: + static CPlayer *factory(Copl *newopl); + + CcoktelPlayer(Copl *newopl) + : CcomposerBackend(newopl), data(0), insts(0), modifyTimbre(0xff) + { } + ~CcoktelPlayer() + { + if (insts) delete [] insts; + if (data) delete [] data; + }; + + bool load(const std::string &filename, const CFileProvider &fp); + bool update(); + void frontend_rewind(int subsong); + + float getrefresh() + { + return 1000.0f; + }; + + std::string gettype() + { + return std::string("AdLib Visual Composer: Coktel Vision"); + } + + unsigned int getinstruments() + { + return insts ? nrTimbre : 0; + }; + +private: + void executeCommand(); + +protected: + unsigned long pos, size; + bool songend; + bool first_tick; + uint8_t * data; /* MIDI data */ + uint8_t soundMode; /* 0: melodic, 1: percussive */ + uint8_t nrTimbre; /* # of timbres */ + + uint32_t counter; /* tick counter */ + uint32_t ticks; /* ticks to wait for next event */ + uint8_t timbre[MAX_VOICES]; /* actual instrument of all voices */ + + /* structure for timbres */ + struct adl_inst { + uint8_t initial[ADLIB_INST_LEN]; + uint8_t modified[ADLIB_INST_LEN]; + int backend_index; + }; + + adl_inst * insts; /* instrument definitions */ + uint8_t modifyTimbre; /* this instrument index will be modified */ +}; + +#endif diff --git a/plugins/adplug/adplug/composer.cpp b/plugins/adplug/adplug/composer.cpp new file mode 100644 index 0000000000..4b88e406f9 --- /dev/null +++ b/plugins/adplug/adplug/composer.cpp @@ -0,0 +1,646 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2006 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * composer.cpp - AdLib Visual Composer synth class by OPLx + * with improvements by Stas'M and Jepael + * + * Source references ADLIB.C from Adlib MSC SDK. + */ +#include +#include +#include + +#include "composer.h" +#include "debug.h" + +//--------------------------------------------------------- +static int16_t const skNrStepPitch = 25; // 25 steps within a half-tone for pitch bend +static uint8_t const skMaxNotes = 96U; +static uint8_t const skCarrierOpOffset = 3U; +static uint8_t const skNumSemitonesInOctave = 12U; +//--------------------------------------------------------- +static uint8_t const skOPL2_WaveCtrlBaseAddress = 0x01U; // Test LSI / Enable waveform control +static uint8_t const skOPL2_AaMultiBaseAddress = 0x20U; // Amp Mod / Vibrato / EG type / Key Scaling / Multiple +static uint8_t const skOPL2_KSLTLBaseAddress = 0x40U; // Key scaling level / Operator output level +static uint8_t const skOPL2_ArDrBaseAddress = 0x60U; // Attack Rate / Decay Rate +static uint8_t const skOPL2_SlrrBaseAddress = 0x80U; // Sustain Level / Release Rate +static uint8_t const skOPL2_FreqLoBaseAddress = 0xA0U; // Frequency (low 8 bits) +static uint8_t const skOPL2_KeyOnFreqHiBaseAddress = 0xB0U; // Key On / Octave / Frequency (high 2 bits) +static uint8_t const skOPL2_AmVibRhythmBaseAddress = 0xBDU; // AM depth / Vibrato depth / Rhythm control +static uint8_t const skOPL2_FeedConBaseAddress = 0xC0U; // Feedback strength / Connection type +static uint8_t const skOPL2_WaveformBaseAddress = 0xE0U; // Waveform select +//--------------------------------------------------------- +static uint8_t const skOPL2_EnableWaveformSelectMask = 0x20U; +static uint8_t const skOPL2_KeyOnMask = 0x20U; +static uint8_t const skOPL2_RhythmMask = 0x20U; +static uint8_t const skOPL2_KSLMask = 0xC0U; +static uint8_t const skOPL2_TLMask = 0x3FU; +static uint8_t const skOPL2_TLMinLevel = 0x3FU; +static uint8_t const skOPL2_FNumLSBMask = 0xFFU; +static uint8_t const skOPL2_FNumMSBMask = 0x03U; +static uint8_t const skOPL2_FNumMSBShift = 0x08U; +static uint8_t const skOPL2_BlockNumberShift = 0x02U; +//--------------------------------------------------------- +static uint8_t const skNoteOctave[skMaxNotes] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 +}; +//--------------------------------------------------------- +static uint8_t const skNoteIndex[skMaxNotes] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; +//--------------------------------------------------------- +// Table below generated by initialize_fnum_table function (from Adlib Music SDK). +static uint16_t const skFNumNotes[skNrStepPitch][skNumSemitonesInOctave] = +{ + 343, 364, 385, 408, 433, 459, 486, 515, 546, 579, 614, 650, + 344, 365, 387, 410, 434, 460, 488, 517, 548, 581, 615, 652, + 345, 365, 387, 410, 435, 461, 489, 518, 549, 582, 617, 653, + 346, 366, 388, 411, 436, 462, 490, 519, 550, 583, 618, 655, + 346, 367, 389, 412, 437, 463, 491, 520, 551, 584, 619, 657, + 347, 368, 390, 413, 438, 464, 492, 522, 553, 586, 621, 658, + 348, 369, 391, 415, 439, 466, 493, 523, 554, 587, 622, 660, + 349, 370, 392, 415, 440, 467, 495, 524, 556, 589, 624, 661, + 350, 371, 393, 416, 441, 468, 496, 525, 557, 590, 625, 663, + 351, 372, 394, 417, 442, 469, 497, 527, 558, 592, 627, 665, + 351, 372, 395, 418, 443, 470, 498, 528, 559, 593, 628, 666, + 352, 373, 396, 419, 444, 471, 499, 529, 561, 594, 630, 668, + 353, 374, 397, 420, 445, 472, 500, 530, 562, 596, 631, 669, + 354, 375, 398, 421, 447, 473, 502, 532, 564, 597, 633, 671, + 355, 376, 398, 422, 448, 474, 503, 533, 565, 599, 634, 672, + 356, 377, 399, 423, 449, 475, 504, 534, 566, 600, 636, 674, + 356, 378, 400, 424, 450, 477, 505, 535, 567, 601, 637, 675, + 357, 379, 401, 425, 451, 478, 506, 537, 569, 603, 639, 677, + 358, 379, 402, 426, 452, 479, 507, 538, 570, 604, 640, 679, + 359, 380, 403, 427, 453, 480, 509, 539, 571, 606, 642, 680, + 360, 381, 404, 428, 454, 481, 510, 540, 572, 607, 643, 682, + 360, 382, 405, 429, 455, 482, 511, 541, 574, 608, 645, 683, + 361, 383, 406, 430, 456, 483, 512, 543, 575, 610, 646, 685, + 362, 384, 407, 431, 457, 484, 513, 544, 577, 611, 648, 687, + 363, 385, 408, 432, 458, 485, 514, 545, 578, 612, 649, 688 +}; +//--------------------------------------------------------- +static uint8_t const drum_op_table[4] = { 0x14, 0x12, 0x15, 0x11 }; +//--------------------------------------------------------- +int const CcomposerBackend::kSizeofDataRecord = 30; +int const CcomposerBackend::kSilenceNote = -12; +int const CcomposerBackend::kNumMelodicVoices = MAX_VOICES - 2; +int const CcomposerBackend::kNumPercussiveVoices = MAX_VOICES; +int const CcomposerBackend::kBassDrumChannel = 6; +int const CcomposerBackend::kSnareDrumChannel = 7; +int const CcomposerBackend::kTomtomChannel = 8; +int const CcomposerBackend::kTomTomNote = 24; +int const CcomposerBackend::kTomTomToSnare = 7; // 7 half-tones between voice 7 & 8 +int const CcomposerBackend::kSnareNote = CcomposerBackend::kTomTomNote + CcomposerBackend::kTomTomToSnare; +//--------------------------------------------------------- +char * strup(char *str) +{ + char *next = str; + while (*next) + { + *next = toupper((unsigned char)*next); + next++; + } + return str; +} +//--------------------------------------------------------- +uint32_t const CcomposerBackend::kMidPitch = 0x2000U; +uint8_t const CcomposerBackend::kMaxVolume = 0x7FU; + +/*** public methods **************************************/ + +CcomposerBackend::CcomposerBackend(Copl * const pNewOpl) + : CPlayer (pNewOpl) + , mpOldFNumFreqPtr (NULL) + , mInstrumentList () + , mFNumFreqPtrList (kNumPercussiveVoices, skFNumNotes[0]) + , mHalfToneOffset (kNumPercussiveVoices, 0) + , mVolumeCache (kNumPercussiveVoices, kMaxVolume) + , mKSLTLCache (kNumPercussiveVoices, 0) + , mNoteCache (kNumPercussiveVoices, 0) + , mKOnOctFNumCache (kNumMelodicVoices, 0) + , mKeyOnCache (kNumPercussiveVoices, false) + , mRhythmMode (0) + , mOldPitchBendLength(~0) + , mPitchRangeStep (skNrStepPitch) + , mOldHalfToneOffset (0) + , mAMVibRhythmCache (0) +{ +} +//--------------------------------------------------------- +void CcomposerBackend::rewind(int subsong) +{ + mHalfToneOffset = TInt16Vector(kNumPercussiveVoices, 0); + mVolumeCache = TUInt8Vector(kNumPercussiveVoices, kMaxVolume); + mKSLTLCache = TUInt8Vector(kNumPercussiveVoices, 0); + mNoteCache = TUInt8Vector(kNumPercussiveVoices, 0); + mKOnOctFNumCache = TUInt8Vector(kNumMelodicVoices, 0); + mKeyOnCache = TBoolVector(kNumPercussiveVoices, false); + + opl->init(); // initialize to melodic by default + opl->write(skOPL2_WaveCtrlBaseAddress, skOPL2_EnableWaveformSelectMask); // Enable waveform select + + frontend_rewind(subsong); +} +//--------------------------------------------------------- +void CcomposerBackend::SetRhythmMode(int const mode) +{ + if (mode) + { + mAMVibRhythmCache |= skOPL2_RhythmMask; + opl->write(skOPL2_AmVibRhythmBaseAddress, mAMVibRhythmCache); // Enable rhythm mode + + SetFreq(kTomtomChannel, kTomTomNote); + SetFreq(kSnareDrumChannel, kSnareNote); + } + else + { + mAMVibRhythmCache &= ~skOPL2_RhythmMask; + opl->write(skOPL2_AmVibRhythmBaseAddress, mAMVibRhythmCache); // Disable rhythm mode + } + + mRhythmMode = mode; +} +//--------------------------------------------------------- +void CcomposerBackend::SetNote(int const voice, int const note) +{ + if ((voice < kBassDrumChannel) || !mRhythmMode) + { + SetNoteMelodic(voice, note); + } + else + { + SetNotePercussive(voice, note); + } +} +//--------------------------------------------------------- +void CcomposerBackend::NoteOn(int const voice, int const note) +{ + SetNote(voice, note + kSilenceNote); +} +//--------------------------------------------------------- +void CcomposerBackend::NoteOff(int const voice) +{ + SetNote(voice, kSilenceNote); +} +//--------------------------------------------------------- +void CcomposerBackend::SetNotePercussive(int const voice, int const note) +{ + int const channel_bit_mask = 1 << (4-voice+kBassDrumChannel); + + mAMVibRhythmCache &= ~channel_bit_mask; + opl->write(skOPL2_AmVibRhythmBaseAddress, mAMVibRhythmCache); + mKeyOnCache[voice] = false; + + if (note != kSilenceNote) + { + switch(voice) + { + case kTomtomChannel: + SetFreq(kTomtomChannel, note); + SetFreq(kSnareDrumChannel, note + kTomTomToSnare); + break; + + case kBassDrumChannel: + SetFreq(voice, note); + break; + default: + // Does nothing + break; + } + + mKeyOnCache[voice] = true; + mAMVibRhythmCache |= channel_bit_mask; + opl->write(skOPL2_AmVibRhythmBaseAddress, mAMVibRhythmCache); + } +} +//--------------------------------------------------------- +void CcomposerBackend::SetNoteMelodic(int const voice, int const note) +{ + if (voice >= kNumMelodicVoices) + { + AdPlug_LogWrite ("COMPOSER: SetNoteMelodic() voice %d >= %d\n", voice, kNumMelodicVoices); + return; + } + opl->write(skOPL2_KeyOnFreqHiBaseAddress + voice, mKOnOctFNumCache[voice] & ~skOPL2_KeyOnMask); + mKeyOnCache[voice] = false; + + if (note != kSilenceNote) + { + SetFreq(voice, note, true); + } +} +//--------------------------------------------------------- +void CcomposerBackend::SetPitchRange(uint8_t pitchRange) +{ + if (pitchRange > 12) + pitchRange = 12; + if (pitchRange < 1) + pitchRange = 1; + mPitchRangeStep = pitchRange * skNrStepPitch; +} +//--------------------------------------------------------- +// From Adlib Music SDK's ADLIB.C ... +void CcomposerBackend::ChangePitch(int voice, uint16_t const pitchBend) +{ + int32_t const pitchBendLength = (int32_t)(pitchBend - kMidPitch) * mPitchRangeStep; + + if ((voice >= kBassDrumChannel) && mRhythmMode) + { + return; + } + + if (mOldPitchBendLength == pitchBendLength) + { + // optimisation ... + mFNumFreqPtrList[voice] = mpOldFNumFreqPtr; + mHalfToneOffset[voice] = mOldHalfToneOffset; + } + else + { + int16_t const pitchStepDir = pitchBendLength / kMidPitch; + int16_t delta; + if (pitchStepDir < 0) + { + int16_t const pitchStepDown = skNrStepPitch - 1 - pitchStepDir; + mOldHalfToneOffset = mHalfToneOffset[voice] = -(pitchStepDown / skNrStepPitch); + delta = (pitchStepDown - skNrStepPitch + 1) % skNrStepPitch; + if (delta) + { + delta = skNrStepPitch - delta; + } + } + else + { + mOldHalfToneOffset = mHalfToneOffset[voice] = pitchStepDir / skNrStepPitch; + delta = pitchStepDir % skNrStepPitch; + } + mpOldFNumFreqPtr = mFNumFreqPtrList[voice] = skFNumNotes[delta]; + mOldPitchBendLength = pitchBendLength; + } + + SetFreq(voice, mNoteCache[voice], mKeyOnCache[voice]); +} +//--------------------------------------------------------- +void CcomposerBackend::SetFreq(int const voice, int const note, bool const keyOn) +{ + int const biased_note = std::max(0, std::min((skMaxNotes-1), note + mHalfToneOffset[voice])); + + uint16_t const frequency = *(mFNumFreqPtrList[voice] + skNoteIndex[biased_note]); + + mNoteCache[voice] = note; + mKeyOnCache[voice] = keyOn; + + mKOnOctFNumCache[voice] = (skNoteOctave[biased_note] << skOPL2_BlockNumberShift) | ((frequency >> skOPL2_FNumMSBShift) & skOPL2_FNumMSBMask); + + opl->write(skOPL2_FreqLoBaseAddress + voice, frequency & skOPL2_FNumLSBMask); + opl->write(skOPL2_KeyOnFreqHiBaseAddress + voice, mKOnOctFNumCache[voice] | (keyOn ? skOPL2_KeyOnMask : 0x0)); +} +//--------------------------------------------------------- +uint8_t CcomposerBackend::GetKSLTL(int const voice) const +{ + uint16_t kslTL = skOPL2_TLMinLevel - (mKSLTLCache[voice] & skOPL2_TLMask); // amplitude + + kslTL = mVolumeCache[voice] * kslTL; + kslTL += kslTL + kMaxVolume; // round off to 0.5 + kslTL = skOPL2_TLMinLevel - (kslTL / (2 * kMaxVolume)); + + kslTL |= mKSLTLCache[voice] & skOPL2_KSLMask; + + return static_cast(kslTL); +} +//--------------------------------------------------------- +void CcomposerBackend::SetVolume(int const voice, uint8_t const volume) +{ + if (voice >= kNumMelodicVoices && !mRhythmMode) + { + AdPlug_LogWrite ("COMPOSER: SetVolume() !mRhythmMode voice %d >= %d\n", voice, kNumMelodicVoices); + return; + } + uint8_t const op_offset = (voice < kSnareDrumChannel || !mRhythmMode) ? op_table[voice] + skCarrierOpOffset : drum_op_table[voice - kSnareDrumChannel]; + + mVolumeCache[voice] = volume; + + opl->write(skOPL2_KSLTLBaseAddress + op_offset, GetKSLTL(voice)); +} +//--------------------------------------------------------- +void CcomposerBackend::SetInstrument(int const voice, int const ins_index) +{ + if (voice >= kNumMelodicVoices && !mRhythmMode) + { + AdPlug_LogWrite ("COMPOSER: SetInstrument() !mRhythmMode voice %d >= %d\n", voice, kNumMelodicVoices); + return; + } + SInstrumentData const & instrument = mInstrumentList[ins_index].instrument; + + send_operator(voice, instrument.modulator, instrument.carrier); +} +//--------------------------------------------------------- +void CcomposerBackend::SetDefaultInstrument(int const voice) +{ + int index; + uint8_t data[ADLIB_INST_LEN]; + + if ((voice >= kNumMelodicVoices && !mRhythmMode) || + (voice >= kNumPercussiveVoices && mRhythmMode)) + return; + + // definition of the ELECTRIC-PIANO voice (opr0 & opr1) + uint8_t pianoParamsOp0[] = + { 1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0 }; + uint8_t pianoParamsOp1[] = + { 0, 1, 1, 15, 7, 0, 2, 4, 0, 0, 0, 1, 0, 0 }; + + // definition of default percussive voices + uint8_t bdOpr0[] = + { 0, 0, 0, 10, 4, 0, 8, 12, 11, 0, 0, 0, 1, 0 }; + uint8_t bdOpr1[] = + { 0, 0, 0, 13, 4, 0, 6, 15, 0, 0, 0, 0, 1, 0 }; + uint8_t sdOpr[] = + { 0, 12, 0, 15, 11, 0, 8, 5, 0, 0, 0, 0, 0, 0 }; + uint8_t tomOpr[] = + { 0, 4, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 }; + uint8_t cymbOpr[] = + { 0, 1, 0, 15, 11, 0, 5, 5, 0, 0, 0, 0, 0, 0 }; + uint8_t hhOpr[] = + { 0, 1, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 }; + + // waveform select is always equal to 0 here, so just memset + memset(&data[0], 0, sizeof(data)); + + for (unsigned int i = 0; i < sizeof(pianoParamsOp0) - 1; i++) + { + if ((voice < kBassDrumChannel) || !mRhythmMode) + { + data[i] = pianoParamsOp0[i]; + data[ADLIB_OPER_LEN + i] = pianoParamsOp1[i]; + } + else if (voice == kBassDrumChannel) + { + data[i] = bdOpr0[i]; + data[ADLIB_OPER_LEN + i] = bdOpr1[i]; + } + else if (voice == kSnareDrumChannel) + { + data[i] = sdOpr[i]; + } + else if (voice == kTomtomChannel) + { + data[i] = tomOpr[i]; + } + else if (voice == kTomtomChannel + 1) + { + data[i] = cymbOpr[i]; + } + else if (voice == kTomtomChannel + 2) + { + data[i] = hhOpr[i]; + } + } + + index = load_instrument_data(&data[0], sizeof(data)); + SetInstrument(voice, index); +} +//--------------------------------------------------------- +void CcomposerBackend::send_operator(int const voice, SOPL2Op const & modulator, SOPL2Op const & carrier) +{ + if ((voice < kSnareDrumChannel) || !mRhythmMode) + { + if (voice >= kNumMelodicVoices) + { + AdPlug_LogWrite ("COMPOSER: send_operator() !mRhythmMode voice %d >= %d\n", voice, kNumMelodicVoices); + return; + } + uint8_t const op_offset = op_table[voice]; + + opl->write(skOPL2_AaMultiBaseAddress + op_offset, modulator.ammulti); + opl->write(skOPL2_KSLTLBaseAddress + op_offset, modulator.ksltl); + opl->write(skOPL2_ArDrBaseAddress + op_offset, modulator.ardr); + opl->write(skOPL2_SlrrBaseAddress + op_offset, modulator.slrr); + opl->write(skOPL2_FeedConBaseAddress + voice , modulator.fbc); + opl->write(skOPL2_WaveformBaseAddress + op_offset, modulator.waveform); + + mKSLTLCache[voice] = carrier.ksltl; + + opl->write(skOPL2_AaMultiBaseAddress + op_offset + skCarrierOpOffset, carrier.ammulti); + opl->write(skOPL2_KSLTLBaseAddress + op_offset + skCarrierOpOffset, GetKSLTL(voice)); + opl->write(skOPL2_ArDrBaseAddress + op_offset + skCarrierOpOffset, carrier.ardr); + opl->write(skOPL2_SlrrBaseAddress + op_offset + skCarrierOpOffset, carrier.slrr); + opl->write(skOPL2_WaveformBaseAddress + op_offset + skCarrierOpOffset, carrier.waveform); + } + else + { + uint8_t const op_offset = drum_op_table[voice-kSnareDrumChannel]; + + mKSLTLCache[voice] = modulator.ksltl; + + opl->write(skOPL2_AaMultiBaseAddress + op_offset, modulator.ammulti); + opl->write(skOPL2_KSLTLBaseAddress + op_offset, GetKSLTL(voice)); + opl->write(skOPL2_ArDrBaseAddress + op_offset, modulator.ardr); + opl->write(skOPL2_SlrrBaseAddress + op_offset, modulator.slrr); + opl->write(skOPL2_WaveformBaseAddress + op_offset, modulator.waveform); + } +} +//--------------------------------------------------------- +bool CcomposerBackend::load_bnk_info(binistream *f, SBnkHeader & header) +{ + header.version_major = static_cast(f->readInt(1)); + header.version_minor = static_cast(f->readInt(1)); + f->readString(header.signature, BNK_SIGNATURE_SIZE); + + header.number_of_list_entries_used = static_cast(f->readInt(2)); + header.total_number_of_list_entries = static_cast(f->readInt(2)); + + header.abs_offset_of_name_list = static_cast(f->readInt(4)); + header.abs_offset_of_data = static_cast(f->readInt(4)); + + f->seek(header.abs_offset_of_name_list, binio::Set); + + std::string prev; + header.case_sensitive = false; + + TInstrumentNames & ins_name_list = header.ins_name_list; + ins_name_list.reserve(header.number_of_list_entries_used); + + for (uint16_t i = 0; i < header.total_number_of_list_entries; ++i) + { + SInstrumentName instrument; + + instrument.index = static_cast(f->readInt(2)); + instrument.record_used = static_cast(f->readInt(1)); + f->readString(instrument.name, INS_MAX_NAME_SIZE); + instrument.name[INS_MAX_NAME_SIZE - 1] = 0; + + if (!instrument.record_used) + continue; + + ins_name_list.push_back(instrument); + + if (!header.case_sensitive) + { + if (!prev.empty() && stricmp(prev.c_str(), instrument.name) > 0) + { + header.case_sensitive = true; + } + prev = instrument.name; + } + } + + return true; +} +//--------------------------------------------------------- +int CcomposerBackend::load_bnk_instrument(binistream *f, SBnkHeader const & header, std::string const & name) +{ + TInstrumentNames const & ins_name_list = header.ins_name_list; + + int const ins_index = get_ins_index(name); + + if (ins_index != -1) + { + return ins_index; + } + + SInstrument usedInstrument; + usedInstrument.name = name; + + char ncs[INS_MAX_NAME_SIZE]; + if (header.case_sensitive) + { + // assuming a bank with case sensitive names stores them in uppercase + // this is true for implay.bnk at least + strncpy(ncs, name.c_str(), INS_MAX_NAME_SIZE - 1); + ncs[INS_MAX_NAME_SIZE - 1] = 0; + strup(ncs); + } + + typedef TInstrumentNames::const_iterator TInsIter; + typedef std::pair TInsIterPair; + + TInsIterPair const range = std::equal_range(ins_name_list.begin(), + ins_name_list.end(), + header.case_sensitive ? std::string(ncs) : name, + StringCompare(header.case_sensitive)); + + if (range.first != range.second) + { + long int const seekOffs = header.abs_offset_of_data + (range.first->index * kSizeofDataRecord); + f->seek(seekOffs, binio::Set); + + read_bnk_instrument(f, usedInstrument.instrument, false); + } + else + { + if (bnk_return_failure) + return ins_index; + + // set up default instrument data here + memset(&usedInstrument.instrument, 0, sizeof(SInstrumentData)); + } + + mInstrumentList.push_back(usedInstrument); + + return mInstrumentList.size()-1; +} +//--------------------------------------------------------- +int CcomposerBackend::load_instrument_data(uint8_t *data, size_t size) +{ + if (size > ADLIB_INST_LEN) + size = ADLIB_INST_LEN; + + binisstream f(data, size); + SInstrument i; + + read_bnk_instrument(&f, i.instrument, true); + + for (size_t index = 0; index < mInstrumentList.size(); ++index) + { + if (memcmp(&mInstrumentList[index].instrument, &i.instrument, sizeof(SInstrumentData)) == 0) + { + return index; + } + } + + mInstrumentList.push_back(i); + + return mInstrumentList.size()-1; +} +//--------------------------------------------------------- +int CcomposerBackend::get_ins_index(std::string const & name) const +{ + for (size_t index = 0; index < mInstrumentList.size(); ++index) + { + if (stricmp(mInstrumentList[index].name.c_str(), name.c_str()) == 0) + { + return index; + } + } + + return -1; +} +//--------------------------------------------------------- +void CcomposerBackend::read_bnk_instrument(binistream * f, SInstrumentData & instrument, bool raw) +{ + instrument.mode = static_cast(raw ? 0 : f->readInt(1)); + instrument.voice_number = static_cast(raw ? 0 : f->readInt(1)); + + read_fm_operator(f, instrument.modulator); + read_fm_operator(f, instrument.carrier); + + instrument.modulator.waveform = static_cast(f->readInt(1)); + instrument.carrier.waveform = static_cast(f->readInt(1)); +} +//--------------------------------------------------------- +void CcomposerBackend::read_fm_operator(binistream *f, SOPL2Op &opl2_op) +{ + SFMOperator fm_op; + + fm_op.key_scale_level = static_cast(f->readInt(1)); + fm_op.freq_multiplier = static_cast(f->readInt(1)); + fm_op.feed_back = static_cast(f->readInt(1)); + fm_op.attack_rate = static_cast(f->readInt(1)); + fm_op.sustain_level = static_cast(f->readInt(1)); + fm_op.sustaining_sound = static_cast(f->readInt(1)); + fm_op.decay_rate = static_cast(f->readInt(1)); + fm_op.release_rate = static_cast(f->readInt(1)); + fm_op.output_level = static_cast(f->readInt(1)); + fm_op.amplitude_vibrato = static_cast(f->readInt(1)); + fm_op.frequency_vibrato = static_cast(f->readInt(1)); + fm_op.envelope_scaling = static_cast(f->readInt(1)); + fm_op.fm_type = static_cast(f->readInt(1)); + + opl2_op.ammulti = fm_op.amplitude_vibrato << 7 | fm_op.frequency_vibrato << 6 | fm_op.sustaining_sound << 5 | fm_op.envelope_scaling << 4 | fm_op.freq_multiplier; + opl2_op.ksltl = fm_op.key_scale_level << 6 | fm_op.output_level; + opl2_op.ardr = fm_op.attack_rate << 4 | fm_op.decay_rate; + opl2_op.slrr = fm_op.sustain_level << 4 | fm_op.release_rate; + opl2_op.fbc = fm_op.feed_back << 1 | (fm_op.fm_type ^ 1); +} diff --git a/plugins/adplug/adplug/composer.h b/plugins/adplug/adplug/composer.h new file mode 100644 index 0000000000..d0f222eb6a --- /dev/null +++ b/plugins/adplug/adplug/composer.h @@ -0,0 +1,239 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2008 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * composer.h - AdLib Visual Composer synth class by OPLx + * with improvements by Stas'M and Jepael + * + * Source references ADLIB.C from Adlib MSC SDK. + */ +#ifndef H_VISUALCOMPOSER +#define H_VISUALCOMPOSER + +#include +#include + +#include "player.h" + +// These are here since Visual C 6 doesn't support statics declared and defined in class. +#define INS_MAX_NAME_SIZE 9U +#define BNK_SIGNATURE_SIZE 6U +#define MAX_VOICES 11 +#define ADLIB_OPER_LEN 13 /* operator length, sizeof(SFMOperator) */ +#define ADLIB_INST_LEN (ADLIB_OPER_LEN * 2 + 2) /* modulator, carrier, mod/car wave select */ + +#include // for uintxx_t + +#ifdef __x86_64__ + typedef signed int int32; +#else + typedef signed long int int32; +#endif + +class CcomposerBackend: public CPlayer +{ +public: + static CPlayer *factory(Copl * pNewOpl); + + CcomposerBackend(Copl * const pNewOpl); + + ~CcomposerBackend() + { + }; + + virtual bool load(const std::string &filename, const CFileProvider &fp) + { + return false; + }; + virtual bool update() + { + return false; + }; + virtual void frontend_rewind(int subsong) = 0; + virtual void rewind(int subsong); // rewinds to specified subsong + virtual float getrefresh() // returns needed timer refresh rate + { + return 1.0f; + }; + + virtual std::string gettype() { return std::string("AdLib Visual Composer"); } + virtual unsigned int getinstruments() { return 0; }; + virtual std::string getinstrument(unsigned int n) { return std::string(); }; + virtual std::string getdesc() { return std::string(); }; + + typedef struct + { + uint16_t index; + uint8_t record_used; + char name[INS_MAX_NAME_SIZE]; + } SInstrumentName; + + typedef std::vector TInstrumentNames; + + typedef struct + { + uint8_t version_major; + uint8_t version_minor; + char signature[BNK_SIGNATURE_SIZE]; + uint16_t number_of_list_entries_used; + uint16_t total_number_of_list_entries; + int32 abs_offset_of_name_list; + int32 abs_offset_of_data; + bool case_sensitive; + + TInstrumentNames ins_name_list; + } SBnkHeader; + + static int const kNumMelodicVoices; + static int const kNumPercussiveVoices; + static uint32_t const kMidPitch; + static uint8_t const kMaxVolume; + + bool load_bnk_info (binistream *f, SBnkHeader & header); + int load_bnk_instrument (binistream *f, SBnkHeader const & header, std::string const & name); + int load_instrument_data (uint8_t *data, size_t size); + bool bnk_return_failure = false; // do not add empty instruments in failure case (default: false) + + void NoteOn(int const voice, int const note); + void NoteOff(int const voice); + void SetRhythmMode(int const mode); + void SetPitchRange(uint8_t pitchRange); + void ChangePitch(int voice, uint16_t const pitchBend); + void SetVolume(int const voice, uint8_t const volume); + void SetInstrument(int const voice, int const ins_index); + void SetDefaultInstrument(int const voice); + +private: + + typedef struct + { + uint8_t key_scale_level; + uint8_t freq_multiplier; + uint8_t feed_back; + uint8_t attack_rate; + uint8_t sustain_level; + uint8_t sustaining_sound; + uint8_t decay_rate; + uint8_t release_rate; + uint8_t output_level; + uint8_t amplitude_vibrato; + uint8_t frequency_vibrato; + uint8_t envelope_scaling; + uint8_t fm_type; + } SFMOperator; + + typedef struct + { + uint8_t ammulti; + uint8_t ksltl; + uint8_t ardr; + uint8_t slrr; + uint8_t fbc; + uint8_t waveform; + } SOPL2Op; + + typedef struct + { + uint8_t mode; + uint8_t voice_number; + SOPL2Op modulator; + SOPL2Op carrier; + } SInstrumentData; + + typedef struct + { + std::string name; + SInstrumentData instrument; + } SInstrument; + + void read_bnk_instrument (binistream *f, SInstrumentData & ins, bool raw); + void read_fm_operator (binistream *f, SOPL2Op & opl2_op); + int get_ins_index (std::string const & name) const; + + void SetNote(int const voice, int const note); + void SetNoteMelodic(int const voice, int const note); + void SetNotePercussive(int const voice, int const note); + void SetFreq(int const voice, int const note, bool const keyOn=false); + uint8_t GetKSLTL(int const voice) const; + void send_operator(int const voice, SOPL2Op const & modulator, SOPL2Op const & carrier); + + class StringCompare + { + public: + StringCompare(bool case_sensitive) + { + sens = case_sensitive; + } + + bool operator()(SInstrumentName const & lhs, SInstrumentName const & rhs) const + { + return keyLess(lhs.name, rhs.name); + } + + bool operator()(SInstrumentName const & lhs, std::string const &rhs) const + { + return keyLess(lhs.name, rhs.c_str()); + } + + bool operator()(std::string const & lhs, SInstrumentName const & rhs) const + { + return keyLess(lhs.c_str(), rhs.name); + } + private: + bool sens; + + bool keyLess(char const * const lhs, char const * const rhs) const + { + if (sens) + return strcmp(lhs, rhs) < 0; + return stricmp(lhs, rhs) < 0; + } + }; + + typedef uint16_t const * TUint16ConstPtr; + typedef std::vector TInstrumentList; + typedef std::vector TUint16PtrVector; + typedef std::vector TInt16Vector; + typedef std::vector TUInt8Vector; + typedef std::vector TBoolVector; + + TUint16ConstPtr mpOldFNumFreqPtr; + TInstrumentList mInstrumentList; + TUint16PtrVector mFNumFreqPtrList; + TInt16Vector mHalfToneOffset; + TUInt8Vector mVolumeCache; + TUInt8Vector mKSLTLCache; + TUInt8Vector mNoteCache; + TUInt8Vector mKOnOctFNumCache; + TBoolVector mKeyOnCache; + uint8_t mRhythmMode; + int32_t mOldPitchBendLength; + uint16_t mPitchRangeStep; + int16_t mOldHalfToneOffset; + uint8_t mAMVibRhythmCache; + + static int const kSizeofDataRecord; + static int const kSilenceNote; + static int const kBassDrumChannel; + static int const kSnareDrumChannel; + static int const kTomtomChannel; + static int const kTomTomNote; + static int const kTomTomToSnare; + static int const kSnareNote; +}; + +#endif diff --git a/plugins/adplug/adplug/d00.cpp b/plugins/adplug/adplug/d00.cpp index 63b5fc2e22..eb68a3b831 100644 --- a/plugins/adplug/adplug/d00.cpp +++ b/plugins/adplug/adplug/d00.cpp @@ -37,6 +37,9 @@ #define HIBYTE(val) (val >> 8) #define LOBYTE(val) (val & 0xff) +#define INDEX_OK(ptr, idx) \ + (((char*)(ptr) - filedata) + ((idx)+1) * sizeof(*(ptr)) <= filesize) + static const unsigned short notetable[12] = // D00 note table {340,363,385,408,432,458,485,514,544,577,611,647}; @@ -58,17 +61,33 @@ bool Cd00Player::load(const std::string &filename, const CFileProvider &fp) binistream *f = fp.open(filename); if(!f) return false; d00header *checkhead; d00header1 *ch; - unsigned long filesize; int i,ver1=0; char *str; + int headerstart=0; // file validation section checkhead = new d00header; f->readString((char *)checkhead, sizeof(d00header)); + // Check for reheadered old-style song + if (strncmp(checkhead->id,"JCH\x26\x02\x66",6) == 0 && checkhead->version & 0x80) { + delete checkhead; + if(!fp.extension(filename, ".d00")) { fp.close(f); return false; } + ch = new d00header1; + // If this is a reheadered old song, the old header begins 0x6b + // into the file. + f->seek(0x6b); f->readString((char *)ch, sizeof(d00header1)); + + if(ch->version > 1 || !ch->subsongs) + { delete ch; fp.close(f); return false; } + delete ch; + ver1 = 1; + // Set the header position so we seek to the right place later + headerstart=0x6b; // Check for version 2-4 header - if(strncmp(checkhead->id,"JCH\x26\x02\x66",6) || checkhead->type || - !checkhead->subsongs || checkhead->soundcard) { + } else if(strncmp(checkhead->id,"JCH\x26\x02\x66",6) || checkhead->type || + !checkhead->subsongs || checkhead->soundcard || + checkhead->version < 2 || checkhead->version > 4) { // Check for version 0 or 1 header (and .d00 file extension) delete checkhead; if(!fp.extension(filename, ".d00")) { fp.close(f); return false; } @@ -85,29 +104,41 @@ bool Cd00Player::load(const std::string &filename, const CFileProvider &fp) filename.c_str(), ver1 ? "Old" : "New"); // load section - filesize = fp.filesize(f); f->seek(0); + filesize = fp.filesize(f); f->seek(headerstart); filedata = new char [filesize + 1]; // 1 byte is needed for old-style DataInfo block f->readString((char *)filedata, filesize); filedata[filesize] = 0; fp.close(f); if(!ver1) { // version 2 and above header = (struct d00header *)filedata; + if (filesize < sizeof(d00header) || + filesize < LE_WORD(&header->infoptr) || + filesize < LE_WORD(&header->instptr) || + filesize < LE_WORD(&header->seqptr)) + return false; version = header->version; datainfo = (char *)filedata + LE_WORD(&header->infoptr); inst = (struct Sinsts *)((char *)filedata + LE_WORD(&header->instptr)); seqptr = (unsigned short *)((char *)filedata + LE_WORD(&header->seqptr)); - for(i=31;i>=0;i--) // erase whitespace + header->songname[31] = '\0'; + for(i=30;i>=0;i--) // erase whitespace if(header->songname[i] == ' ') header->songname[i] = '\0'; else break; - for(i=31;i>=0;i--) + header->author[31] = '\0'; + for(i=30;i>=0;i--) if(header->author[i] == ' ') header->author[i] = '\0'; else break; } else { // version 1 header1 = (struct d00header1 *)filedata; + if (filesize < sizeof(d00header1) || + filesize <= LE_WORD(&header1->infoptr) || + filesize <= LE_WORD(&header1->instptr) || + filesize <= LE_WORD(&header1->seqptr)) + return false; version = header1->version; datainfo = (char *)filedata + LE_WORD(&header1->infoptr); inst = (struct Sinsts *)((char *)filedata + LE_WORD(&header1->instptr)); @@ -120,10 +151,12 @@ bool Cd00Player::load(const std::string &filename, const CFileProvider &fp) header1->speed = 70; // v0 files default to 70Hz break; case 1: + if (filesize <= LE_WORD(&header1->lpulptr)) return false; levpuls = (struct Slevpuls *)((char *)filedata + LE_WORD(&header1->lpulptr)); spfx = 0; break; case 2: + if (filesize <= LE_WORD(&header->spfxptr)) return false; levpuls = (struct Slevpuls *)((char *)filedata + LE_WORD(&header->spfxptr)); spfx = 0; break; @@ -132,6 +165,7 @@ bool Cd00Player::load(const std::string &filename, const CFileProvider &fp) levpuls = 0; break; case 4: + if (filesize <= LE_WORD(&header->spfxptr)) return false; spfx = (struct Sspfx *)((char *)filedata + LE_WORD(&header->spfxptr)); levpuls = 0; break; @@ -140,8 +174,6 @@ bool Cd00Player::load(const std::string &filename, const CFileProvider &fp) while((*str == '\xff' || *str == ' ') && str >= datainfo) { *str = '\0'; str--; } - else // old-style block - memset((char *)filedata+filesize,0,1); rewind(0); return true; @@ -152,16 +184,20 @@ bool Cd00Player::update() unsigned char c,cnt,trackend=0,fx,note; unsigned short ord,*patt,buf,fxop,pattpos; - // effect handling (timer dependant) + // effect handling (timer dependent) for(c=0;c<9;c++) { channel[c].slideval += channel[c].slide; setfreq(c); // sliding vibrato(c); // vibrato - if(channel[c].spfx != 0xffff) { // SpFX + if (channel[c].spfx != 0xffff) do { // SpFX if(channel[c].fxdel) channel[c].fxdel--; else { channel[c].spfx = LE_WORD(&spfx[channel[c].spfx].ptr); + if (channel[c].spfx == 0xffff || !INDEX_OK(spfx, channel[c].spfx)) { + channel[c].spfx = 0xffff; + break; + } channel[c].fxdel = spfx[channel[c].spfx].duration; channel[c].inst = LE_WORD(&spfx[channel[c].spfx].instnr) & 0xfff; if(spfx[channel[c].spfx].modlev != 0xff) @@ -176,16 +212,16 @@ bool Cd00Player::update() } channel[c].modvol += spfx[channel[c].spfx].modlevadd; channel[c].modvol &= 63; setvolume(c); - } + } while (0); if(channel[c].levpuls != 0xff) { // Levelpuls if(channel[c].frameskip) channel[c].frameskip--; - else { + else if (INDEX_OK(inst, channel[c].inst)) { channel[c].frameskip = inst[channel[c].inst].timer; if(channel[c].fxdel) channel[c].fxdel--; - else { + else if (INDEX_OK(levpuls, channel[c].levpuls)) { channel[c].levpuls = levpuls[channel[c].levpuls].ptr - 1; channel[c].fxdel = levpuls[channel[c].levpuls].duration; if(levpuls[channel[c].levpuls].level != 0xff) @@ -230,25 +266,38 @@ bool Cd00Player::update() continue; } readorder: // process arrangement (orderlist) + if (!INDEX_OK(channel[c].order, channel[c].ordpos)) { + channel[c].seqend = 1; continue; + } ord = LE_WORD(&channel[c].order[channel[c].ordpos]); switch(ord) { case 0xfffe: channel[c].seqend = 1; continue; // end of arrangement stream case 0xffff: // jump to order - channel[c].ordpos = LE_WORD(&channel[c].order[channel[c].ordpos + 1]); channel[c].seqend = 1; + if (!INDEX_OK(channel[c].order, channel[c].ordpos + 1)) continue; + channel[c].ordpos = LE_WORD(&channel[c].order[channel[c].ordpos + 1]); goto readorder; default: if(ord >= 0x9000) { // set speed channel[c].speed = ord & 0xff; - ord = LE_WORD(&channel[c].order[channel[c].ordpos - 1]); + if (channel[c].ordpos > 0) + ord = LE_WORD(&channel[c].order[channel[c].ordpos - 1]); + else + ord = 0; channel[c].ordpos++; } else if(ord >= 0x8000) { // transpose track channel[c].transpose = ord & 0xff; if(ord & 0x100) channel[c].transpose = -channel[c].transpose; + if (!INDEX_OK(channel[c].order, channel[c].ordpos + 1)) { + channel[c].seqend = 1; continue; + } ord = LE_WORD(&channel[c].order[++channel[c].ordpos]); } + if (!INDEX_OK(seqptr, ord) || LE_WORD(&seqptr[ord]) + 2U > filesize) { + channel[c].seqend = 1; continue; + } patt = (unsigned short *)((char *)filedata + LE_WORD(&seqptr[ord])); break; } @@ -256,7 +305,8 @@ bool Cd00Player::update() readseq: // process sequence (pattern) if(!version) // v0: always initialize rhcnt channel[c].rhcnt = channel[c].irhcnt; - pattpos = LE_WORD(&patt[channel[c].pattpos]); + pattpos = INDEX_OK(patt, channel[c].pattpos) ? + LE_WORD(&patt[channel[c].pattpos]) : 0xffff; if(pattpos == 0xffff) { // pattern ended? channel[c].pattpos = 0; channel[c].ordpos++; @@ -266,7 +316,9 @@ bool Cd00Player::update() note = LOBYTE(pattpos); fx = pattpos >> 12; fxop = pattpos & 0x0fff; - channel[c].pattpos++; pattpos = LE_WORD(&patt[channel[c].pattpos]); + channel[c].pattpos++; + pattpos = INDEX_OK(patt, channel[c].pattpos) ? + LE_WORD(&patt[channel[c].pattpos]) : 0; channel[c].nextnote = LOBYTE(pattpos) & 0x7f; if(version ? cnt < 0x40 : !fx) { // note event switch(note) { @@ -296,7 +348,8 @@ bool Cd00Player::update() note += channel[c].transpose; channel[c].note = note; // remember note for SpFX - if(channel[c].ispfx != 0xffff && cnt < 0x20) { // reset SpFX + if (channel[c].ispfx != 0xffff && cnt < 0x20 && + INDEX_OK(spfx, channel[c].ispfx)) { // reset SpFX channel[c].spfx = channel[c].ispfx; if(LE_WORD(&spfx[channel[c].spfx].instnr) & 0x8000) // locked frequency note = spfx[channel[c].spfx].halfnote; @@ -310,7 +363,9 @@ bool Cd00Player::update() channel[c].modvol = inst[channel[c].inst].data[7] & 63; } - if(channel[c].ilevpuls != 0xff && cnt < 0x20) { // reset LevelPuls + if (channel[c].ilevpuls != 0xff && cnt < 0x20 && + INDEX_OK(levpuls, channel[c].ilevpuls) && + INDEX_OK(inst, channel[c].inst)) { // reset LevelPuls channel[c].levpuls = channel[c].ilevpuls; channel[c].fxdel = levpuls[channel[c].levpuls].duration; channel[c].frameskip = inst[channel[c].inst].timer; @@ -377,6 +432,11 @@ bool Cd00Player::update() channel[c].ispfx = 0xffff; channel[c].spfx = 0xffff; channel[c].inst = fxop; + if (!INDEX_OK(inst, fxop)) { + channel[c].modvol = 0; + channel[c].levpuls = channel[c].ilevpuls = 0xff; + break; + } channel[c].modvol = inst[fxop].data[7] & 63; if(version < 3 && version && inst[fxop].tunelev) // Set LevelPuls channel[c].ilevpuls = inst[fxop].tunelev - 1; @@ -412,48 +472,44 @@ void Cd00Player::rewind(int subsong) struct Stpoin { unsigned short ptr[9]; unsigned char volume[9],dummy[5]; - } *tpoin; - int i; - - if(subsong == -1) subsong = cursubsong; + } tpoin; - if(version > 1) { // do nothing if subsong > number of subsongs - if(subsong >= header->subsongs) - return; - } else - if(subsong >= header1->subsongs) - return; + if(subsong < 0) subsong = cursubsong; - memset(channel,0,sizeof(channel)); - if(version > 1) - tpoin = (struct Stpoin *)((char *)filedata + LE_WORD(&header->tpoin)); + size_t dataofs = subsong * sizeof(Stpoin) + + LE_WORD(&(version > 1 ? header->tpoin : header1->tpoin)); + if ((unsigned int)subsong < getsubsongs() && dataofs + sizeof(Stpoin) <= filesize) + memcpy(&tpoin, filedata + dataofs, sizeof(Stpoin)); else - tpoin = (struct Stpoin *)((char *)filedata + LE_WORD(&header1->tpoin)); - for(i=0;i<9;i++) { - if(LE_WORD(&tpoin[subsong].ptr[i])) { // track enabled + memset(&tpoin, 0, sizeof(Stpoin)); + + memset(channel, 0, sizeof(channel)); + + for (int i = 0; i < 9; i++) { + if (LE_WORD(&tpoin.ptr[i]) && // track enabled + LE_WORD(&tpoin.ptr[i]) + 4U <= filesize) { channel[i].speed = LE_WORD((unsigned short *) - ((char *)filedata + LE_WORD(&tpoin[subsong].ptr[i]))); + ((char *)filedata + LE_WORD(&tpoin.ptr[i]))); channel[i].order = (unsigned short *) - ((char *)filedata + LE_WORD(&tpoin[subsong].ptr[i]) + 2); + ((char *)filedata + LE_WORD(&tpoin.ptr[i]) + 2); } else { // track disabled channel[i].speed = 0; channel[i].order = 0; } channel[i].ispfx = 0xffff; channel[i].spfx = 0xffff; // no SpFX channel[i].ilevpuls = 0xff; channel[i].levpuls = 0xff; // no LevelPuls - channel[i].cvol = tpoin[subsong].volume[i] & 0x7f; // our player may savely ignore bit 7 + channel[i].cvol = tpoin.volume[i] & 0x7f; // our player may safely ignore bit 7 channel[i].vol = channel[i].cvol; // initialize volume } songend = 0; opl->init(); opl->write(1,32); // reset OPL chip - cursubsong = subsong; + cursubsong = subsong > 0xff ? 0xff : subsong; } std::string Cd00Player::gettype() { char tmpstr[40]; - - sprintf(tmpstr,"EdLib packed (version %d)",version > 1 ? header->version : header1->version); + snprintf(tmpstr, sizeof(tmpstr), "EdLib packed (version %d)", version > 1 ? header->version : header1->version); return std::string(tmpstr); } @@ -479,6 +535,7 @@ void Cd00Player::setvolume(unsigned char chan) { unsigned char op = op_table[chan]; unsigned short insnr = channel[chan].inst; + if (!INDEX_OK(inst, insnr)) return; opl->write(0x43 + op,(int)(63-((63-(inst[insnr].data[2] & 63))/63.0)*(63-channel[chan].vol)) + (inst[insnr].data[2] & 192)); @@ -493,7 +550,8 @@ void Cd00Player::setfreq(unsigned char chan) { unsigned short freq = channel[chan].freq; - if(version == 4) // v4: apply instrument finetune + if (version == 4 && // v4: apply instrument finetune + INDEX_OK(inst, channel[chan].inst)) freq += inst[channel[chan].inst].tunelev; freq += channel[chan].slideval; @@ -508,6 +566,7 @@ void Cd00Player::setinst(unsigned char chan) { unsigned char op = op_table[chan]; unsigned short insnr = channel[chan].inst; + if (!INDEX_OK(inst, insnr)) return; // set instrument data opl->write(0x63 + op, inst[insnr].data[0]); diff --git a/plugins/adplug/adplug/d00.h b/plugins/adplug/adplug/d00.h index e8233862b7..b6901770d6 100644 --- a/plugins/adplug/adplug/d00.h +++ b/plugins/adplug/adplug/d00.h @@ -97,6 +97,7 @@ class Cd00Player: public CPlayer d00header *header; d00header1 *header1; char *filedata; + unsigned long filesize; private: void setvolume(unsigned char chan); diff --git a/plugins/adplug/adplug/database.cpp b/plugins/adplug/adplug/database.cpp index 8881d79f50..3b5d8f2462 100644 --- a/plugins/adplug/adplug/database.cpp +++ b/plugins/adplug/adplug/database.cpp @@ -78,8 +78,11 @@ bool CAdPlugDatabase::load(binistream &f) length = f.readInt(4); // read records - for(unsigned long i = 0; i < length; i++) - insert(CRecord::factory(f)); + for(unsigned long i = 0; i < length; i++) { + CRecord *rec = CRecord::factory(f); + if (!insert(rec)) + delete rec; + } return true; } @@ -301,7 +304,7 @@ bool CAdPlugDatabase::CRecord::user_write(std::ostream &out) default: out << "*** Unknown ***"; break; } out << std::endl; - out << "Key: " << std::hex << key.crc16 << ":" << key.crc32 << std::dec << std::endl; + out << "Key: " << std::hex << key.crc16 << ":" << key.crc32 << std::dec << std::dec << std::endl; out << "File type: " << filetype << std::endl; out << "Comment: " << comment << std::endl; diff --git a/plugins/adplug/adplug/depack.c b/plugins/adplug/adplug/depack.c new file mode 100644 index 0000000000..fd73f3d51c --- /dev/null +++ b/plugins/adplug/adplug/depack.c @@ -0,0 +1,201 @@ +/* + * aPLib compression library - the smaller the better :) + * + * C depacker + * + * Copyright (c) 1998-2014 Joergen Ibsen + * All Rights Reserved + * + * http://www.ibsensoftware.com/ + */ + // aPLib v0.26b decompressor + // incompatible with current releases + // changes up to v1.1.0 have been commented out + // aP_depack_asm(const void *source, void *destination) + +#include "depack.h" + +/* internal data structure */ +struct APDSTATE { + const unsigned char *source; + unsigned char *destination; + unsigned int destlen; + unsigned int tag; + unsigned int bitcount; +}; + +static unsigned int aP_getbit(struct APDSTATE *ud) +{ + unsigned int bit; + + /* check if tag is empty */ + if (!ud->bitcount--) { + /* load next tag */ + ud->tag = *ud->source++; + ud->bitcount = 7; + } + + /* shift bit out of tag */ + bit = (ud->tag >> 7) & 0x01; + ud->tag <<= 1; + + return bit; +} + +static unsigned int aP_getgamma(struct APDSTATE *ud) +{ + unsigned int result = 1; + + /* input gamma2-encoded bits */ + do { + result = (result << 1) + aP_getbit(ud); + } while (aP_getbit(ud)); + + return result; +} + +unsigned int aP_depack(const void *source, void *destination, int srcsize, int dstsize) +{ + struct APDSTATE ud; + unsigned int offs, len, R0/*, LWM*/; + int done; + int i; + + ud.source = (const unsigned char *) source; + ud.destination = (unsigned char *) destination; + ud.destlen = 0; + ud.bitcount = 0; + + R0 = (unsigned int) -1; + //LWM = 0; + done = 0; + + /* first byte verbatim */ + if (!srcsize) return ud.destlen; + if (!dstsize) return ud.destlen; + *ud.destination++ = *ud.source++; + srcsize--; + dstsize--; ud.destlen++; + + /* main decompression loop */ + while (!done) { + if (aP_getbit(&ud)) { + if (aP_getbit(&ud)) { + if (aP_getbit(&ud)) { + offs = 0; + + for (i = 4; i; i--) { + offs = (offs << 1) + aP_getbit(&ud); + } + + if (offs) { + if (!dstsize) return ud.destlen; + if (offs > ud.destlen) return ud.destlen; + *ud.destination = *(ud.destination - offs); + ud.destination++; + dstsize--; ud.destlen++; + } + else { + if (!dstsize) return ud.destlen; + *ud.destination++ = 0x00; + dstsize--; ud.destlen++; + } + + //LWM = 0; + } else { + if (!srcsize) return ud.destlen; + offs = *ud.source++; + srcsize--; + + len = 2 + (offs & 0x0001); + + offs >>= 1; + + if (offs) { + for (; len; len--) { + if (!dstsize) return ud.destlen; + if (offs > ud.destlen) return ud.destlen; + *ud.destination = *(ud.destination - offs); + ud.destination++; + dstsize--; ud.destlen++; + } + } + else { + done = 1; + } + + R0 = offs; + //LWM = 1; + } + } else { + offs = aP_getgamma(&ud); + + //if ((LWM == 0) && (offs == 2)) { + if (offs == 2) { + //------------------- + offs = R0; + + len = aP_getgamma(&ud); + + for (; len; len--) { + if (!dstsize) return ud.destlen; + if (offs > ud.destlen) return ud.destlen; + *ud.destination = *(ud.destination - offs); + ud.destination++; + dstsize--; ud.destlen++; + } + } else { + /* + if (LWM == 0) { + offs -= 2; + } + else { + offs -= 2; + } + */ + offs -= 3; + //------------------- + + offs <<= 8; + if (!srcsize) return ud.destlen; + offs += *ud.source++; + srcsize--; + + len = aP_getgamma(&ud); + + if (offs >= 32000) { + len++; + } + if (offs >= 1280) { + len++; + } + if (offs < 128) { + len += 2; + } + + for (; len; len--) { + if (!dstsize) return ud.destlen; + if (offs > ud.destlen) return ud.destlen; + *ud.destination = *(ud.destination - offs); + ud.destination++; + dstsize--; ud.destlen++; + } + + R0 = offs; + } + + //LWM = 1; + } + } + else { + if (!srcsize) return ud.destlen; + if (!dstsize) return ud.destlen; + *ud.destination++ = *ud.source++; + srcsize--; + dstsize--; ud.destlen++; + //LWM = 0; + } + } + + return ud.destlen; +} diff --git a/plugins/adplug/adplug/depack.h b/plugins/adplug/adplug/depack.h new file mode 100644 index 0000000000..8a64cd7837 --- /dev/null +++ b/plugins/adplug/adplug/depack.h @@ -0,0 +1,30 @@ +/* + * aPLib compression library - the smaller the better :) + * + * C depacker, header file + * + * Copyright (c) 1998-2014 Joergen Ibsen + * All Rights Reserved + * + * http://www.ibsensoftware.com/ + */ + +#ifndef DEPACK_H_INCLUDED +#define DEPACK_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef APLIB_ERROR +# define APLIB_ERROR ((unsigned int) (-1)) +#endif + +/* function prototype */ +unsigned int aP_depack(const void *source, void *destination, int srcsize, int dstsize); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DEPACK_H_INCLUDED */ diff --git a/plugins/adplug/adplug/dfm.cpp b/plugins/adplug/adplug/dfm.cpp index 17d9044359..05e2bd6763 100644 --- a/plugins/adplug/adplug/dfm.cpp +++ b/plugins/adplug/adplug/dfm.cpp @@ -47,9 +47,16 @@ bool CdfmLoader::load(const std::string &filename, const CFileProvider &fp) restartpos = 0; flags = Standard; bpm = 0; init_trackord(); f->readString(songinfo, 33); + if (*songinfo > 32 || *songinfo < 0) { + fp.close(f); return false; + } initspeed = f->readInt(1); - for(i = 0; i < 32; i++) + for(i = 0; i < 32; i++) { f->readString(instname[i], 12); + if (*instname[i] > 11 || *instname[i] < 0) { + fp.close(f); return false; + } + } for(i = 0; i < 32; i++) { inst[i].data[1] = f->readInt(1); inst[i].data[2] = f->readInt(1); @@ -68,8 +75,14 @@ bool CdfmLoader::load(const std::string &filename, const CFileProvider &fp) ; length = i; npats = f->readInt(1); + if (npats > 64) { + fp.close(f); return false; // or: realloc_patterns(npats, 64, 9); + } for(i = 0; i < npats; i++) { n = f->readInt(1); + if (n >= npats) { + fp.close(f); return false; + } for(r = 0; r < 64; r++) for(c = 0; c < 9; c++) { note = f->readInt(1); @@ -106,8 +119,7 @@ bool CdfmLoader::load(const std::string &filename, const CFileProvider &fp) std::string CdfmLoader::gettype() { char tmpstr[20]; - - sprintf(tmpstr,"Digital-FM %d.%d",header.hiver,header.lover); + snprintf(tmpstr,sizeof(tmpstr),"Digital-FM %d.%d",header.hiver,header.lover); return std::string(tmpstr); } diff --git a/plugins/adplug/adplug/dfm.h b/plugins/adplug/adplug/dfm.h index 641c503f1e..18b788fb1d 100644 --- a/plugins/adplug/adplug/dfm.h +++ b/plugins/adplug/adplug/dfm.h @@ -37,9 +37,9 @@ class CdfmLoader: public CmodPlayer unsigned int getinstruments() { return 32; }; std::string getinstrument(unsigned int n) - { if(*instname[n]) return std::string(instname[n],1,*instname[n]); else return std::string(); }; + { if (n < 32 && *instname[n]) return std::string(instname[n] + 1, *instname[n]); else return std::string(); }; std::string getdesc() - { return std::string(songinfo,1,*songinfo); }; + { return std::string(songinfo + 1, *songinfo); }; private: struct { diff --git a/plugins/adplug/adplug/dmo.cpp b/plugins/adplug/adplug/dmo.cpp index b0b3d3ccc4..44b36200d8 100644 --- a/plugins/adplug/adplug/dmo.cpp +++ b/plugins/adplug/adplug/dmo.cpp @@ -31,17 +31,10 @@ #include "dmo.h" #include "debug.h" -#define LOWORD(l) ((l) & 0xffff) -#define HIWORD(l) ((l) >> 16) -#define LOBYTE(w) ((w) & 0xff) -#define HIBYTE(w) ((w) >> 8) - #define ARRAY_AS_DWORD(a, i) \ ((a[i + 3] << 24) + (a[i + 2] << 16) + (a[i + 1] << 8) + a[i]) #define ARRAY_AS_WORD(a, i) ((a[i + 1] << 8) + a[i]) -#define CHARP_AS_WORD(p) (((*(p + 1)) << 8) + (*p)) - /* -------- Public Methods -------------------------------- */ CPlayer *CdmoLoader::factory(Copl *newopl) @@ -51,31 +44,19 @@ CPlayer *CdmoLoader::factory(Copl *newopl) bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp) { - int i,j; - binistream *f; + int i; + + binistream *f = fp.open(filename); + if (!f) return false; // check header - dmo_unpacker *unpacker = new dmo_unpacker; + dmo_unpacker unpacker; unsigned char chkhdr[16]; - if(!fp.extension(filename, ".dmo")) - { - delete unpacker; - return false; - } - - f = fp.open(filename); - if(!f) - { - delete unpacker; - return false; - } - f->readString((char *)chkhdr, 16); - if (!unpacker->decrypt(chkhdr, 16)) + if (!unpacker.decrypt(chkhdr, 16)) { - delete unpacker; fp.close(f); return false; } @@ -91,21 +72,19 @@ bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp) fp.close(f); // decrypt - unpacker->decrypt(packed_module,packed_length); + unpacker.decrypt(packed_module, packed_length); long unpacked_length = 0x2000 * ARRAY_AS_WORD(packed_module, 12); unsigned char *module = new unsigned char [unpacked_length]; // unpack - if (!unpacker->unpack(packed_module+12,module,unpacked_length)) + if (!unpacker.unpack(packed_module, packed_length, module, unpacked_length)) { - delete unpacker; delete [] packed_module; delete [] module; return false; } - delete unpacker; delete [] packed_module; // "TwinTeam" - signed ? @@ -123,6 +102,7 @@ bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp) uf.ignore(22); // ignore DMO header ID string uf.readString(header.name, 28); + header.name[27] = 0; // ensure termination uf.ignore(2); // _unk_1 header.ordnum = uf.readInt(2); @@ -132,6 +112,11 @@ bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp) header.is = uf.readInt(2); header.it = uf.readInt(2); + if (header.ordnum >= 256 || header.insnum > 99 || header.patnum > 99) { + delete [] module; + return false; + } + memset(header.chanset,0xFF,32); for (i=0;i<9;i++) @@ -154,6 +139,7 @@ bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp) memset(&inst[i],0,sizeof(s3minst)); uf.readString(inst[i].name, 28); + inst[i].name[27] = 0; inst[i].volume = uf.readInt(1); inst[i].dsk = uf.readInt(1); @@ -181,36 +167,7 @@ bool CdmoLoader::load(const std::string &filename, const CFileProvider &fp) for (i = 0; i < header.patnum; i++) { long cur_pos = uf.pos(); - for (j = 0; j < 64; j++) { - while (1) { - unsigned char token = uf.readInt(1); - - if (!token) - break; - - unsigned char chan = token & 31; - - // note + instrument ? - if (token & 32) { - unsigned char bufbyte = uf.readInt(1); - - pattern[i][j][chan].note = bufbyte & 15; - pattern[i][j][chan].oct = bufbyte >> 4; - pattern[i][j][chan].instrument = uf.readInt(1); - } - - // volume ? - if (token & 64) - pattern[i][j][chan].volume = uf.readInt(1); - - // command ? - if (token & 128) { - pattern[i][j][chan].command = uf.readInt(1); - pattern[i][j][chan].info = uf.readInt(1); - } - } - } - + load_pattern(i, &uf, my_patlen[i]); uf.seek(cur_pos + my_patlen[i]); } @@ -238,41 +195,21 @@ std::string CdmoLoader::getauthor() unsigned short CdmoLoader::dmo_unpacker::brand(unsigned short range) { - unsigned short ax,bx,cx,dx; - - ax = LOWORD(bseed); - bx = HIWORD(bseed); - cx = ax; - ax = LOWORD(cx * 0x8405); - dx = HIWORD(cx * 0x8405); - cx <<= 3; - cx = (((HIBYTE(cx) + LOBYTE(cx)) & 0xFF) << 8) + LOBYTE(cx); - dx += cx; - dx += bx; - bx <<= 2; - dx += bx; - dx = (((HIBYTE(dx) + LOBYTE(bx)) & 0xFF) << 8) + LOBYTE(dx); - bx <<= 5; - dx = (((HIBYTE(dx) + LOBYTE(bx)) & 0xFF) << 8) + LOBYTE(dx); - ax += 1; - if (!ax) dx += 1; - - // leave it that way or amd64 might get it wrong - bseed = dx; - bseed <<= 16; - bseed += ax; - - return HIWORD(HIWORD(LOWORD(bseed) * range) + HIWORD(bseed) * range); + bseed *= 0x08088405U; + bseed++; + + return (uint64_t)bseed * range >> 32; } -bool CdmoLoader::dmo_unpacker::decrypt(unsigned char *buf, long len) +bool CdmoLoader::dmo_unpacker::decrypt(unsigned char *buf, size_t len) { - unsigned long seed = 0; - int i; + if (len < headersize) + return false; bseed = ARRAY_AS_DWORD(buf, 0); - for (i=0; i < ARRAY_AS_WORD(buf, 4) + 1; i++) + uint32_t seed = 0; + for (int i = 0; i <= ARRAY_AS_WORD(buf, 4); i++) seed += brand(0xffff); bseed = seed ^ ARRAY_AS_DWORD(buf, 6); @@ -280,143 +217,109 @@ bool CdmoLoader::dmo_unpacker::decrypt(unsigned char *buf, long len) if (ARRAY_AS_WORD(buf, 10) != brand(0xffff)) return false; - for (i=0;i<(len-12);i++) - buf[12+i] ^= brand(0x100); + for (size_t i = headersize; i < len; i++) + buf[i] ^= brand(0x100); buf[len - 2] = buf[len - 1] = 0; return true; } -short CdmoLoader::dmo_unpacker::unpack_block(unsigned char *ibuf, long ilen, unsigned char *obuf) +long CdmoLoader::dmo_unpacker::unpack_block(unsigned char *ibuf, size_t ilen, + unsigned char *obuf, size_t olen) { - unsigned char code,par1,par2; - unsigned short ax,bx,cx; - - unsigned char *ipos = ibuf; - unsigned char *opos = obuf; + size_t ipos = 0, opos = 0; // LZ77 child - while (ipos - ibuf < ilen) - { - code = *ipos++; - - // 00xxxxxx: copy (xxxxxx + 1) bytes - if ((code >> 6) == 0) - { - cx = (code & 0x3F) + 1; - - if(opos + cx >= oend) - return -1; - - for (int i=0;i> 6) == 1) - { - par1 = *ipos++; - - ax = ((code & 0x3F) << 3) + ((par1 & 0xE0) >> 5) + 1; - cx = (par1 & 0x1F) + 3; - - if(opos + cx >= oend) - return -1; - - for(int i=0;i> 6) == 2) - { - int i; - - par1 = *ipos++; - - ax = ((code & 0x3F) << 1) + (par1 >> 7) + 1; - cx = ((par1 & 0x70) >> 4) + 3; - bx = par1 & 0x0F; - - if(opos + bx + cx >= oend) - return -1; - - for(i=0;i> 6) == 3) - { - int i; - - par1 = *ipos++; - par2 = *ipos++; - - bx = ((code & 0x3F) << 7) + (par1 >> 1); - cx = ((par1 & 0x01) << 4) + (par2 >> 4) + 4; - ax = par2 & 0x0F; - - if(opos + ax + cx >= oend) - return -1; + while (ipos < ilen) { + size_t cpy = 0, ofs = 0, lit = 0; + unsigned char code, par1, par2; + + code = ibuf[ipos++]; + par1 = ipos < ilen ? ibuf[ipos] : 0; + par2 = ipos + 1 < ilen ? ibuf[ipos + 1] : 0; + switch (code >> 6) { + case 0: + // 00xxxxxx: use (X + 1) literal bytes + lit = (code & 0x3F) + 1; + break; + + case 1: + // 01xxxxxx xxxyyyyy: copy (Y + 3) bytes from offset (X + 1) + ipos++; // for par1 + ofs = ((code & 0x3F) << 3) + ((par1 & 0xE0) >> 5) + 1; + cpy = (par1 & 0x1F) + 3; + break; + + case 2: + // 10xxxxxx xyyyzzzz: copy (Y + 3) bytes from offset(X + 1); + // use Z literal bytes + ipos++; // for par1 + ofs = ((code & 0x3F) << 1) + (par1 >> 7) + 1; + cpy = ((par1 & 0x70) >> 4) + 3; + lit = par1 & 0x0F; + break; + + case 3: + // 11xxxxxx xxxxxxxy yyyyzzzz: copy (Y + 4) from offset X; + // use Z literal bytes + ipos += 2; // for par1 and par2 + ofs = ((code & 0x3F) << 7) + (par1 >> 1); + cpy = ((par1 & 0x01) << 4) + (par2 >> 4) + 4; + lit = par2 & 0x0F; + break; + } - for(i=0;i ilen || opos + cpy + lit > olen || ofs > opos) + return -1; - for (i=0;i inputsize) + return 0; - obuf += bul; - olen += bul; + unsigned short bul = ARRAY_AS_WORD(ibuf, 0); - ibuf += CHARP_AS_WORD(block_length); - block_length += 2; - } + if (unpack_block(ibuf + 2, blen - 2, obuf, outputsize - olen) != bul) + return 0; + + obuf += bul; + olen += bul; + + ibuf += blen; + inputsize -= blen; + } return olen; } diff --git a/plugins/adplug/adplug/dmo.h b/plugins/adplug/adplug/dmo.h index 0e38de025b..0510adfc5e 100644 --- a/plugins/adplug/adplug/dmo.h +++ b/plugins/adplug/adplug/dmo.h @@ -19,6 +19,8 @@ dmo.cpp - TwinTeam loader by Riven the Mage */ +#include + #include "s3m.h" class CdmoLoader: public Cs3mPlayer @@ -37,15 +39,16 @@ class CdmoLoader: public Cs3mPlayer class dmo_unpacker { public: - bool decrypt(unsigned char *buf, long len); - long unpack(unsigned char *ibuf, unsigned char *obuf, - unsigned long outputsize); + enum { headersize = 12 }; + bool decrypt(unsigned char *buf, size_t len); + static size_t unpack(unsigned char *ibuf, size_t inputsize, + unsigned char *obuf, size_t outputsize); private: unsigned short brand(unsigned short range); - short unpack_block(unsigned char *ibuf, long ilen, unsigned char *obuf); + static long unpack_block(unsigned char *ibuf, size_t ilen, + unsigned char *obuf, size_t olen); - unsigned long bseed; - unsigned char *oend; + uint32_t bseed; }; }; diff --git a/plugins/adplug/adplug/dro.cpp b/plugins/adplug/adplug/dro.cpp index 3ab16e7c7c..086277f59a 100644 --- a/plugins/adplug/adplug/dro.cpp +++ b/plugins/adplug/adplug/dro.cpp @@ -73,6 +73,10 @@ bool CdroPlayer::load(const std::string &filename, const CFileProvider &fp) f->ignore(4); // Length in milliseconds this->iLength = f->readInt(4); // stored in file as number of bytes + if (this->iLength < 3 || this->iLength > fp.filesize(f) - f->pos()) { + fp.close(f); + return false; + } this->data = new uint8_t[this->iLength]; @@ -92,7 +96,7 @@ bool CdroPlayer::load(const std::string &filename, const CFileProvider &fp) } // Read the OPL data. - for (; (int)i < this->iLength; i++) { + for (; i < this->iLength; i++) { this->data[i]=f->readInt(1); } @@ -143,19 +147,21 @@ bool CdroPlayer::load(const std::string &filename, const CFileProvider &fp) bool CdroPlayer::update() { - int iIndex; - int iValue; + unsigned int iIndex; + unsigned int iValue; while (this->iPos < this->iLength) { iIndex = this->data[this->iPos++]; // Short delay if (iIndex == this->iCmdDelayS) { + if (this->iPos >= this->iLength) return false; iValue = this->data[this->iPos++]; this->iDelay = iValue + 1; return true; // Long delay } else if (iIndex == this->iCmdDelayL) { + if (this->iPos + 1 >= this->iLength) return false; iValue = this->data[this->iPos] | (this->data[this->iPos + 1] << 8); this->iPos += 2; this->iDelay = (iValue + 1); @@ -168,8 +174,10 @@ bool CdroPlayer::update() // Normal write } else { if (iIndex == 0x04) { + if (this->iPos+1 >= this->iLength) return false; iIndex = this->data[this->iPos++]; } + else if (this->iPos >= this->iLength) return false; iValue = this->data[this->iPos++]; this->opl->write(iIndex, iValue); } diff --git a/plugins/adplug/adplug/dro.h b/plugins/adplug/adplug/dro.h index 89dfcdb76a..2b00d18db3 100644 --- a/plugins/adplug/adplug/dro.h +++ b/plugins/adplug/adplug/dro.h @@ -38,9 +38,9 @@ class CdroPlayer: public CPlayer static const uint8_t iCmdDelayL = 0x01; // Wraithverge: fixed this with "static". uint8_t *data; - int iLength; - int iPos; - int iDelay; + unsigned int iLength; + unsigned int iPos; + unsigned int iDelay; private: char title[40]; diff --git a/plugins/adplug/adplug/dro2.cpp b/plugins/adplug/adplug/dro2.cpp index 8f60e7a9d6..51f9818297 100644 --- a/plugins/adplug/adplug/dro2.cpp +++ b/plugins/adplug/adplug/dro2.cpp @@ -64,7 +64,13 @@ bool Cdro2Player::load(const std::string &filename, const CFileProvider &fp) return false; } - this->iLength = f->readInt(4) * 2; // stored in file as number of byte pairs + this->iLength = f->readInt(4); + if (this->iLength >= 1<<30 || + this->iLength > fp.filesize(f) - f->pos()) { + fp.close(f); + return false; + } + this->iLength *= 2; // stored in file as number of byte p f->ignore(4); // Length in milliseconds f->ignore(1); /// OPL type (0 == OPL2, 1 == Dual OPL2, 2 == OPL3) int iFormat = f->readInt(1); @@ -135,8 +141,8 @@ bool Cdro2Player::load(const std::string &filename, const CFileProvider &fp) bool Cdro2Player::update() { while (this->iPos < this->iLength) { - int iIndex = this->data[this->iPos++]; - int iValue = this->data[this->iPos++]; + unsigned int iIndex = this->data[this->iPos++]; + unsigned int iValue = this->data[this->iPos++]; // Short delay if (iIndex == this->iCmdDelayS) { @@ -157,7 +163,7 @@ bool Cdro2Player::update() } else { this->opl->setchip(0); } - if (iIndex > this->iConvTableLen) { + if (iIndex >= this->iConvTableLen) { printf("DRO2: Error - index beyond end of codemap table! Corrupted .dro?\n"); return false; // EOF } diff --git a/plugins/adplug/adplug/dro2.h b/plugins/adplug/adplug/dro2.h index 3ce64b918c..93782de21b 100644 --- a/plugins/adplug/adplug/dro2.h +++ b/plugins/adplug/adplug/dro2.h @@ -33,13 +33,13 @@ class Cdro2Player: public CPlayer { protected: uint8_t iCmdDelayS, iCmdDelayL; - int iConvTableLen; + uint8_t iConvTableLen; uint8_t *piConvTable; uint8_t *data; - int iLength; - int iPos; - int iDelay; + unsigned int iLength; + unsigned int iPos; + unsigned int iDelay; private: char title[40]; diff --git a/plugins/adplug/adplug/dtm.cpp b/plugins/adplug/adplug/dtm.cpp index 6df5ed7190..a8535e5719 100644 --- a/plugins/adplug/adplug/dtm.cpp +++ b/plugins/adplug/adplug/dtm.cpp @@ -22,6 +22,7 @@ NOTE: Panning (Ex) effect is ignored. */ +#include #include #include "dtm.h" @@ -34,160 +35,166 @@ CPlayer *CdtmLoader::factory(Copl *newopl) bool CdtmLoader::load(const std::string &filename, const CFileProvider &fp) { - binistream *f = fp.open(filename); if(!f) return false; - const unsigned char conv_inst[11] = { 2,1,10,9,4,3,6,5,0,8,7 }; - const unsigned short conv_note[12] = { 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE }; - int i,j,k,t=0; + static const unsigned char conv_inst[11] = { + 2, 1, 10, 9, 4, 3, 6, 5, 0, 8, 7 + }; + static const unsigned short conv_note[12] = { + 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, + 0x202, 0x220, 0x241, 0x263, 0x287, 0x2AE + }; + + binistream *f = fp.open(filename); + if (!f) return false; // read header - f->readString(header.id, 12); + f->readString(header.id, sizeof(header.id)); header.version = f->readInt(1); - f->readString(header.title, 20); f->readString(header.author, 20); - header.numpat = f->readInt(1); header.numinst = f->readInt(1); - - // signature exists ? good version ? - if(memcmp(header.id,"DeFy DTM ",9) || header.version != 0x10) - { fp.close (f); return false; } - - header.numinst++; + f->readString(header.title, sizeof(header.title)); + f->readString(header.author, sizeof(header.author)); + // Ensure title and author are NUL terminated. We may overwrite the + // last character because the arrays are too short. + header.author[sizeof(header.author) - 1] = + header.title[sizeof(header.title) - 1] = 0; + header.numpat = f->readInt(1); + header.numinst = f->readInt(1) + 1; + + // check header + if (memcmp(header.id, "DeFy DTM ", 9) || // signature exists? + header.version != 0x10 || // good version? + header.numinst > MAX_INST || + header.numinst < N_CHAN || // need a default instrument for each channel + header.numpat == 0 || + f->error()) { + fp.close (f); + return false; + } // load description - memset(desc,0,80*16); + memset(desc, 0, sizeof(desc)); - char bufstr[80]; + char *bufstr = desc; - for (i=0;i<16;i++) - { + for (int i = 0; i < DESC_ROWS; i++) { // get line length unsigned char bufstr_length = f->readInt(1); - if(bufstr_length > 80) { + if (bufstr_length > DESC_COLS) { + // "desc" is too small to hold DESC_ROWS lines with DESC_COLS chars + // each plus DESC_ROWS newlines and a NUL terminator. Accept a line + // length of DESC_COLS anyway and truncate the last line if necessary. + // Maybe we should grow desc or allocate it dynamically instead. fp.close(f); return false; } + int max_length = desc + sizeof(desc) - 1 - bufstr; + int discard = bufstr_length > max_length ? bufstr_length - max_length : 0; + bufstr_length -= discard; + // read line - if (bufstr_length) - { + if (bufstr_length) { f->readString(bufstr,bufstr_length); - for (j=0;jignore(discard); + } + if (bufstr_length < max_length) + *(bufstr++) = '\n'; + } + *bufstr = 0; // init CmodPlayer realloc_instruments(header.numinst); - realloc_order(100); - realloc_patterns(header.numpat,64,9); + realloc_order(N_ORD); + realloc_patterns(header.numpat, N_ROW, N_CHAN); init_notetable(conv_note); init_trackord(); // load instruments - for (i=0;ireadInt(1); + if (name_length >= sizeof(instruments[i].name)) { + fp.close(f); + return false; // or truncate the name instead? + } + if (name_length) f->readString(instruments[i].name, name_length); instruments[i].name[name_length] = 0; - for(j = 0; j < 12; j++) - instruments[i].data[j] = f->readInt(1); + f->readString((char *)instruments[i].data, sizeof(instruments[i].data)); - for (j=0;j<11;j++) + for (unsigned int j = 0; j < sizeof(conv_inst); j++) inst[i].data[conv_inst[j]] = instruments[i].data[j]; - } + } // load order - for(i = 0; i < 100; i++) order[i] = f->readInt(1); - - nop = header.numpat; - - unsigned char *pattern = new unsigned char [0x480]; + f->readString((char *)order, N_ORD); // load tracks - for (i=0;ireadInt(2); - - unsigned char *packed_pattern = new unsigned char [packed_length]; - - for(j = 0; j < packed_length; j++) - packed_pattern[j] = f->readInt(1); - - long unpacked_length = unpack_pattern(packed_pattern,packed_length,pattern,0x480); - - delete [] packed_pattern; - - if (!unpacked_length) - { - delete [] pattern; + dtm_event pattern[N_ROW][N_CHAN]; + nop = header.numpat; + for (int t = 0, i = 0; i < nop; i++) { + unsigned short packed_length = f->readInt(2); + if (!unpack_pattern(f, packed_length, pattern, sizeof(pattern))) { fp.close(f); return false; - } + } // convert pattern - for (j=0;j<9;j++) - { - for (k=0;k<64;k++) - { - dtm_event *event = (dtm_event *)&pattern[(k*9+j)*2]; + for (int j = 0; j < N_CHAN; j++, t++) { + for (int k = 0; k < N_ROW; k++) { + dtm_event *event = &pattern[k][j]; // instrument - if (event->byte0 == 0x80) - { - if (event->byte1 <= 0x80) + if (event->byte0 == 0x80) { + if (event->byte1 < header.numinst) // not <= 0x80 ! tracks[t][k].inst = event->byte1 + 1; - } + } // note + effect - else - { + else { tracks[t][k].note = event->byte0; if ((event->byte0 != 0) && (event->byte0 != 127)) tracks[t][k].note++; // convert effects - switch (event->byte1 >> 4) - { + unsigned char ev_param = event->byte1 & 0x0F; + switch (event->byte1 >> 4) { case 0x0: // pattern break - if ((event->byte1 & 15) == 1) + if (ev_param == 1) tracks[t][k].command = 13; break; case 0x1: // freq. slide up tracks[t][k].command = 28; - tracks[t][k].param1 = event->byte1 & 15; + tracks[t][k].param1 = ev_param; break; case 0x2: // freq. slide down tracks[t][k].command = 28; - tracks[t][k].param2 = event->byte1 & 15; + tracks[t][k].param2 = ev_param; break; case 0xA: // set carrier volume case 0xC: // set instrument volume tracks[t][k].command = 22; - tracks[t][k].param1 = (0x3F - (event->byte1 & 15)) >> 4; - tracks[t][k].param2 = (0x3F - (event->byte1 & 15)) & 15; + tracks[t][k].param1 = (0x3F - ev_param) >> 4; // always 3 + tracks[t][k].param2 = (0x3F - ev_param) & 0xF; break; case 0xB: // set modulator volume tracks[t][k].command = 21; - tracks[t][k].param1 = (0x3F - (event->byte1 & 15)) >> 4; - tracks[t][k].param2 = (0x3F - (event->byte1 & 15)) & 15; + tracks[t][k].param1 = (0x3F - ev_param) >> 4; // always 3 + tracks[t][k].param2 = (0x3F - ev_param) & 0xF; break; case 0xE: // set panning @@ -195,24 +202,24 @@ bool CdtmLoader::load(const std::string &filename, const CFileProvider &fp) case 0xF: // set speed tracks[t][k].command = 13; - tracks[t][k].param2 = event->byte1 & 15; + tracks[t][k].param2 = ev_param; break; - } - } - } - - t++; - } - } + } + } + } + } + } - delete [] pattern; + if (f->error()) { + fp.close(f); + return false; + } fp.close(f); // order length - for (i=0;i<100;i++) - { - if (order[i] >= 0x80) - { + length = N_ORD; + for (unsigned int i = 0; i < N_ORD; i++) { + if (order[i] & 0x80) { length = i; if (order[i] == 0xFF) @@ -220,9 +227,14 @@ bool CdtmLoader::load(const std::string &filename, const CFileProvider &fp) else restartpos = order[i] - 0x80; + if (restartpos >= i) // bad restart position or empty order list + return false; + break; - } - } + } else if (order[i] >= nop) { + return false; + } + } // initial speed initspeed = 2; @@ -237,13 +249,12 @@ void CdtmLoader::rewind(int subsong) CmodPlayer::rewind(subsong); // default instruments - for (int i=0;i<9;i++) - { + for (int i = 0; i < N_CHAN; i++) { channel[i].inst = i; channel[i].vol1 = 63 - (inst[i].data[10] & 63); channel[i].vol2 = 63 - (inst[i].data[9] & 63); - } + } } float CdtmLoader::getrefresh() @@ -273,7 +284,7 @@ std::string CdtmLoader::getdesc() std::string CdtmLoader::getinstrument(unsigned int n) { - return std::string(instruments[n].name); + return n < header.numinst ? std::string(instruments[n].name) : std::string(); } unsigned int CdtmLoader::getinstruments() @@ -283,35 +294,30 @@ unsigned int CdtmLoader::getinstruments() /* -------- Private Methods ------------------------------- */ -long CdtmLoader::unpack_pattern(unsigned char *ibuf, long ilen, unsigned char *obuf, long olen) +bool CdtmLoader::unpack_pattern(binistream *f, size_t ilen, + void *obuf, size_t olen) { - unsigned char *input = ibuf; - unsigned char *output = obuf; + unsigned char *outp = (unsigned char *)obuf; - long input_length = 0; - long output_length = 0; + // RLE + while (ilen--) { + size_t repeat_counter = 1; + unsigned char repeat_byte = f->readInt(1); - unsigned char repeat_byte, repeat_counter; + if ((repeat_byte & 0xF0) == 0xD0) { + if (!ilen--) return false; // truncated input - // RLE - while (input_length < ilen) - { - repeat_byte = input[input_length++]; - - if ((repeat_byte & 0xF0) == 0xD0) - { - repeat_counter = repeat_byte & 15; - repeat_byte = input[input_length++]; - } - else - repeat_counter = 1; - - for (int i=0;ireadInt(1); } - return output_length; + // Attempts to generate too much data are normal, ignore the excess data. + repeat_counter = std::min(repeat_counter, olen); + + memset(outp, repeat_byte, repeat_counter); + outp += repeat_counter; + olen -= repeat_counter; + } + + return olen == 0 && !f->error(); // generated enough data? } diff --git a/plugins/adplug/adplug/dtm.h b/plugins/adplug/adplug/dtm.h index d912825d3c..d8cfe41fa5 100644 --- a/plugins/adplug/adplug/dtm.h +++ b/plugins/adplug/adplug/dtm.h @@ -40,6 +40,15 @@ class CdtmLoader: public CmodPlayer unsigned int getinstruments(); private: + enum { + N_CHAN = 9, + N_ROW = 64, + N_ORD = 100, + MAX_INST = 128, + DESC_COLS = 80, + DESC_ROWS = 16, + DESC_SIZE = DESC_COLS * DESC_ROWS, + }; struct dtm_header { @@ -51,13 +60,13 @@ class CdtmLoader: public CmodPlayer unsigned char numinst; } header; - char desc[80*16]; + char desc[DESC_SIZE]; struct dtm_instrument { char name[13]; unsigned char data[12]; - } instruments[128]; + } instruments[MAX_INST]; struct dtm_event { @@ -65,5 +74,5 @@ class CdtmLoader: public CmodPlayer unsigned char byte1; }; - long unpack_pattern(unsigned char *ibuf, long ilen, unsigned char *obuf, long olen); + bool unpack_pattern(binistream *f, size_t ilen, void *obuf, size_t olen); }; diff --git a/plugins/adplug/adplug/emuopl.cpp b/plugins/adplug/adplug/emuopl.cpp index 42f40a36d7..66c65e1512 100644 --- a/plugins/adplug/adplug/emuopl.cpp +++ b/plugins/adplug/adplug/emuopl.cpp @@ -39,6 +39,7 @@ CEmuopl::~CEmuopl() if(mixbufSamples) { delete [] mixbuf0; delete [] mixbuf1; + delete [] mixbuf2; } } @@ -48,12 +49,13 @@ void CEmuopl::update(short *buf, int samples) //ensure that our mix buffers are adequately sized if(mixbufSamples < samples) { - if(mixbufSamples) { delete[] mixbuf0; delete[] mixbuf1; } + if(mixbufSamples) { delete[] mixbuf0; delete[] mixbuf1; delete[] mixbuf2; } mixbufSamples = samples; //*2 = make room for stereo, if we need it mixbuf0 = new short[samples*2]; mixbuf1 = new short[samples*2]; + mixbuf2 = new short[samples*2]; } //data should be rendered to outbuf @@ -68,9 +70,7 @@ void CEmuopl::update(short *buf, int samples) short *tempbuf=mixbuf0; short *tempbuf2=mixbuf1; if(use16bit) outbuf = buf; - else outbuf = mixbuf1; - //...there is a potentially confusing situation where mixbuf1 can be aliased. - //beware. it is a little loony. + else outbuf = mixbuf2; //all of the following rendering code produces 16bit output diff --git a/plugins/adplug/adplug/emuopl.h b/plugins/adplug/adplug/emuopl.h index dd2f29e4b9..12878f898c 100644 --- a/plugins/adplug/adplug/emuopl.h +++ b/plugins/adplug/adplug/emuopl.h @@ -42,7 +42,7 @@ class CEmuopl: public Copl private: bool use16bit, stereo; FM_OPL *opl[2]; // OPL2 emulator data - short *mixbuf0, *mixbuf1; + short *mixbuf0, *mixbuf1, *mixbuf2; int mixbufSamples; }; diff --git a/plugins/adplug/adplug/flash.cpp b/plugins/adplug/adplug/flash.cpp index 8e14a18f7a..ff045921d6 100644 --- a/plugins/adplug/adplug/flash.cpp +++ b/plugins/adplug/adplug/flash.cpp @@ -31,53 +31,25 @@ #include "flash.h" #include "debug.h" -const unsigned char CxadflashPlayer::flash_adlib_registers[99] = -{ - 0x23, 0x20, 0x43, 0x40, 0x63, 0x60, 0x83, 0x80, 0xC0, 0xE3, 0xE0, - 0x24, 0x21, 0x44, 0x41, 0x64, 0x61, 0x84, 0x81, 0xC1, 0xE4, 0xE1, - 0x25, 0x22, 0x45, 0x42, 0x65, 0x62, 0x85, 0x82, 0xC2, 0xE5, 0xE2, - 0x2B, 0x28, 0x4B, 0x48, 0x6B, 0x68, 0x8B, 0x88, 0xC3, 0xEB, 0xE8, - 0x2C, 0x29, 0x4C, 0x49, 0x6C, 0x69, 0x8C, 0x89, 0xC4, 0xEC, 0xE9, - 0x2D, 0x2A, 0x4D, 0x4A, 0x6D, 0x6A, 0x8D, 0x8A, 0xC5, 0xED, 0xEA, - 0x33, 0x30, 0x53, 0x50, 0x73, 0x70, 0x93, 0x90, 0xC6, 0xF3, 0xF0, - 0x34, 0x31, 0x54, 0x51, 0x74, 0x71, 0x94, 0x91, 0xC7, 0xF4, 0xF1, - 0x35, 0x32, 0x55, 0x52, 0x75, 0x72, 0x95, 0x92, 0xC8, 0xF5, 0xF2 +static const unsigned char flash_adlib_registers[9][11] = { + { 0x23, 0x20, 0x43, 0x40, 0x63, 0x60, 0x83, 0x80, 0xC0, 0xE3, 0xE0 }, + { 0x24, 0x21, 0x44, 0x41, 0x64, 0x61, 0x84, 0x81, 0xC1, 0xE4, 0xE1 }, + { 0x25, 0x22, 0x45, 0x42, 0x65, 0x62, 0x85, 0x82, 0xC2, 0xE5, 0xE2 }, + { 0x2B, 0x28, 0x4B, 0x48, 0x6B, 0x68, 0x8B, 0x88, 0xC3, 0xEB, 0xE8 }, + { 0x2C, 0x29, 0x4C, 0x49, 0x6C, 0x69, 0x8C, 0x89, 0xC4, 0xEC, 0xE9 }, + { 0x2D, 0x2A, 0x4D, 0x4A, 0x6D, 0x6A, 0x8D, 0x8A, 0xC5, 0xED, 0xEA }, + { 0x33, 0x30, 0x53, 0x50, 0x73, 0x70, 0x93, 0x90, 0xC6, 0xF3, 0xF0 }, + { 0x34, 0x31, 0x54, 0x51, 0x74, 0x71, 0x94, 0x91, 0xC7, 0xF4, 0xF1 }, + { 0x35, 0x32, 0x55, 0x52, 0x75, 0x72, 0x95, 0x92, 0xC8, 0xF5, 0xF2 } }; -const unsigned short CxadflashPlayer::flash_notes_encoded[268] = -{ - 0x000, - 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 0x800, 0x900, 0xA00, 0xB00, 0xC00, - 0x101, 0x201, 0x301, 0x401, 0x501, 0x601, 0x701, 0x801, 0x901, 0xA01, 0xB01, 0xC01, - 0x102, 0x202, 0x302, 0x402, 0x502, 0x602, 0x702, 0x802, 0x902, 0xA02, 0xB02, 0xC02, - 0x103, 0x203, 0x303, 0x403, 0x503, 0x603, 0x703, 0x803, 0x903, 0xA03, 0xB03, 0xC03, - 0x104, 0x204, 0x304, 0x404, 0x504, 0x604, 0x704, 0x804, 0x904, 0xA04, 0xB04, 0xC04, - 0x105, 0x205, 0x305, 0x405, 0x505, 0x605, 0x705, 0x805, 0x905, 0xA05, 0xB05, 0xC05, - 0x106, 0x206, 0x306, 0x406, 0x506, 0x606, 0x706, 0x806, 0x906, 0xA06, 0xB06, 0xC06, - 0x107, 0x207, 0x307, 0x407, 0x507, 0x607, 0x707, 0x807, 0x907, 0xA07, 0xB07, 0xC07, - 0x108, 0x208, 0x308, 0x408, 0x508, 0x608, 0x708, 0x808, 0x908, 0xA08, 0xB08, 0xC08, - 0x109, 0x209, 0x309, 0x409, 0x509, 0x609, 0x709, 0x809, 0x909, 0xA09, 0xB09, 0xC09, - 0x10A, 0x20A, 0x30A, 0x40A, 0x50A, 0x60A, 0x70A, 0x80A, 0x90A, 0xA0A, 0xB0A, 0xC0A, - 0x10B, 0x20B, 0x30B, 0x40B, 0x50B, 0x60B, 0x70B, 0x80B, 0x90B, 0xA0B, 0xB0B, 0xC0B, - 0x10C, 0x20C, 0x30C, 0x40C, 0x50C, 0x60C, 0x70C, 0x80C, 0x90C, 0xA0C, 0xB0C, 0xC0C, - 0x10D, 0x20D, 0x30D, 0x40D, 0x50D, 0x60D, 0x70D, 0x80D, 0x90D, 0xA0D, 0xB0D, 0xC0D, - 0x10E, 0x20E, 0x30E, 0x40E, 0x50E, 0x60E, 0x70E, 0x80E, 0x90E, 0xA0E, 0xB0E, 0xC0E, - 0x10F, 0x20F, 0x30F, 0x40F, 0x50F, 0x60F, 0x70F, 0x80F, 0x90F, 0xA0F, 0xB0F, 0xC0F, - 0x110, 0x210, 0x310, 0x410, 0x510, 0x610, 0x710, 0x810, 0x910, 0xA10, 0xB10, 0xC10, - 0x111, 0x211, 0x311, 0x411, 0x511, 0x611, 0x711, 0x811, 0x911, 0xA11, 0xB11, 0xC11, - 0x112, 0x212, 0x312, 0x412, 0x512, 0x612, 0x712, 0x812, 0x912, 0xA12, 0xB12, 0xC12, - 0x113, 0x213, 0x313, 0x413, 0x513, 0x613, 0x713, 0x813, 0x913, 0xA13, 0xB13, 0xC13, - 0x114, 0x214, 0x314, 0x414, 0x514, 0x614, 0x714, 0x814, 0x914, 0xA14, 0xB14, 0xC14, - 0x115, 0x215, 0x315 +static const unsigned short flash_notes[12] = { + 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, + 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287 }; -const unsigned short CxadflashPlayer::flash_notes[12] = -{ - 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287 -}; - -const unsigned char CxadflashPlayer::flash_default_instrument[8] = -{ +// unused: +static const unsigned char flash_default_instrument[8] = { 0x00, 0x00, 0x3F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF }; @@ -88,8 +60,6 @@ CPlayer *CxadflashPlayer::factory(Copl *newopl) void CxadflashPlayer::xadplayer_rewind(int subsong) { - int i; - plr.speed = xad.speed; flash.order_pos = 0; @@ -98,100 +68,103 @@ void CxadflashPlayer::xadplayer_rewind(int subsong) opl_write(0x08, 0x00); opl_write(0xBD, 0x00); - // assign default instrument - for(i=0; i<9; i++) - { - opl_write(0xA0+i, 0x00); - opl_write(0xB0+i, 0x00); + // clear channels + for (int i = 0; i < 9; i++) { + opl_write(0xA0 + i, 0x00); + opl_write(0xB0 + i, 0x00); } // assign instruments - for(i=0; i<9; i++) - for(int j=0; j<11; j++) - opl_write(flash_adlib_registers[i*11+j], tune[i*12+j]); + for (int i = 0; i < 9; i++) + for (int j = 0; j < 11; j++) + opl_write(flash_adlib_registers[i][j], tune[i*12 + j]); } void CxadflashPlayer::xadplayer_update() { - unsigned short event_pos = (tune[0x600+flash.order_pos]*1152) + \ - (flash.pattern_pos*18) + \ - 0x633; - - for (int i=0; i<9; i++) - { - unsigned short flash_channel_freq = (adlib[0xB0+i] << 8) + adlib[0xA0+i]; + const unsigned char *order = &tune[0x600]; + unsigned short event_pos = 0x633 + order[flash.order_pos] * 1152 + + flash.pattern_pos * 18; + + for (int i = 0; i < 9; i++) { + if (event_pos > tune_size - 2) { + flash.pattern_pos = 0x3F; // end of data, skip rest of pattern + break; + } unsigned char event_b0 = tune[event_pos++]; unsigned char event_b1 = tune[event_pos++]; #ifdef DEBUG - AdPlug_LogWrite("channel %02X, event %02X %02X:\n",i+1,event_b0,event_b1); + AdPlug_LogWrite("channel %02X, event %02X %02X:\n", i+1, event_b0, event_b1); #endif - if (event_b0 == 0x80) // 0.0x80: Set Instrument - { - for(int j=0; j<11; j++) - opl_write(flash_adlib_registers[i*11+j], tune[event_b1*12+j]); + if (event_b0 == 0x80) { // 0.0x80: Set Instrument + if (event_b1 < 0x80) + for (int j = 0; j < 11; j++) + opl_write(flash_adlib_registers[i][j], tune[event_b1 * 12 + j]); + continue; } - else - { - if (event_b1 == 0x01) - flash.pattern_pos = 0x3F; // 1.0x01: Pattern Break - - unsigned char fx = (event_b1 >> 4); - unsigned char fx_p = (event_b1 & 0x0F); - - switch(fx) - { - case 0x0A: // 1.0xAy: Set Carrier volume - opl_write(flash_adlib_registers[11*i+2], fx_p << 2); - break; - case 0x0B: // 1.0xBy: Set Modulator volume - opl_write(flash_adlib_registers[11*i+3], fx_p << 2); - break; - case 0x0C: // 1.0xCy: Set both operators volume - opl_write(flash_adlib_registers[11*i+2], fx_p << 2); - opl_write(flash_adlib_registers[11*i+3], fx_p << 2); - break; -// case 0x0E: // 1.0xEy: ? (increase some value) - case 0x0F: // 1.0xFy: Set Speed - plr.speed = (fx_p + 1); - break; - } - if (event_b0) - { - // mute channel - opl_write(0xA0+i, adlib[0xA0+i]); - opl_write(0xB0+i, adlib[0xB0+i] & 0xDF); + signed char slide_freq = 0; + unsigned char fx = (event_b1 >> 4), fx_p = (event_b1 & 0x0F); - // is note ? - if (event_b0 != 0x7F) - { - unsigned short note_encoded = flash_notes_encoded[event_b0]; - unsigned short freq = flash_notes[(note_encoded >> 8) - 1]; + switch(fx) { + case 0x0: + if (event_b1 == 0x01) // 1.0x01: Pattern Break + flash.pattern_pos = 0x3F; + break; - flash_channel_freq = freq | ((note_encoded & 0xFF) << 10) | 0x2000; + case 0x1: // 1.0x1y: Fine Frequency Slide Up + slide_freq = fx_p << 1; + break; - opl_write(0xA0+i, flash_channel_freq & 0xFF); - opl_write(0xB0+i, flash_channel_freq >> 8); - } - } + case 0x2: // 1.0x2y: Fine Frequency Slide Down + slide_freq = -(fx_p << 1); + break; - if (fx == 0x01) // 1.0x1y: Fine Frequency Slide Up - { - flash_channel_freq += (fx_p << 1); + case 0xA: // 1.0xAy: Set Carrier volume + opl_write(flash_adlib_registers[i][2], fx_p << 2); + break; - opl_write(0xA0+i, flash_channel_freq & 0xFF); - opl_write(0xB0+i, flash_channel_freq >> 8); - } - else if (fx == 0x02) // 1.0x2y: Fine Frequency Slide Down - { - flash_channel_freq -= (fx_p << 1); + case 0xB: // 1.0xBy: Set Modulator volume + opl_write(flash_adlib_registers[i][3], fx_p << 2); + break; + + case 0xC: // 1.0xCy: Set both operators volume + opl_write(flash_adlib_registers[i][2], fx_p << 2); + opl_write(flash_adlib_registers[i][3], fx_p << 2); + break; + +// case 0xE: // 1.0xEy: ? (increase some value) + + case 0xF: // 1.0xFy: Set Speed + plr.speed = (fx_p + 1); + break; + } - opl_write(0xA0+i, flash_channel_freq & 0xFF); - opl_write(0xB0+i, flash_channel_freq >> 8); + if (event_b0) { + // mute channel + opl_write(0xA0 + i, adlib[0xA0 + i]); + opl_write(0xB0 + i, adlib[0xB0 + i] & 0xDF); + + // is note? + if (event_b0 != 0x7F) { + unsigned char note = (event_b0 - 1) % 12; + unsigned char octave = (event_b0 - 1) / 12; + unsigned short freq = flash_notes[note] | (octave << 10) | 0x2000; + + opl_write(0xA0 + i, freq & 0xFF); + opl_write(0xB0 + i, freq >> 8); } } + + if (slide_freq) { + unsigned short freq = (adlib[0xB0 + i] << 8) + adlib[0xA0 + i]; + freq += slide_freq; + + opl_write(0xA0 + i, freq & 0xFF); + opl_write(0xB0 + i, freq >> 8); + } } // next row @@ -201,14 +174,12 @@ void CxadflashPlayer::xadplayer_update() if (flash.pattern_pos >= 0x40) { flash.pattern_pos = 0; - flash.order_pos++; // end of module ? - if (tune[0x600+flash.order_pos] == 0xFF) + if (flash.order_pos > 0x33 || order[flash.order_pos] == 0xFF) { flash.order_pos = 0; - plr.looping = 1; } } @@ -226,5 +197,5 @@ std::string CxadflashPlayer::xadplayer_gettype() unsigned int CxadflashPlayer::xadplayer_getinstruments() { - return 32; + return 128; } diff --git a/plugins/adplug/adplug/flash.h b/plugins/adplug/adplug/flash.h index f58512b735..891bd8eb0a 100644 --- a/plugins/adplug/adplug/flash.h +++ b/plugins/adplug/adplug/flash.h @@ -38,7 +38,7 @@ class CxadflashPlayer: public CxadPlayer // bool xadplayer_load() { - if(xad.fmt == FLASH) + if (xad.fmt == FLASH && tune_size >= 0x633 + 18) return true; else return false; @@ -48,10 +48,4 @@ class CxadflashPlayer: public CxadPlayer float xadplayer_getrefresh(); std::string xadplayer_gettype(); unsigned int xadplayer_getinstruments(); - -private: - static const unsigned char flash_adlib_registers[99]; - static const unsigned short flash_notes_encoded[268]; - static const unsigned short flash_notes[12]; - static const unsigned char flash_default_instrument[8]; }; diff --git a/plugins/adplug/adplug/fmc.cpp b/plugins/adplug/adplug/fmc.cpp index 95034bd501..5ff694e559 100644 --- a/plugins/adplug/adplug/fmc.cpp +++ b/plugins/adplug/adplug/fmc.cpp @@ -39,10 +39,14 @@ bool CfmcLoader::load(const std::string &filename, const CFileProvider &fp) // read header f->readString(header.id, 4); f->readString(header.title, 21); + header.title[20] = 0; header.numchan = f->readInt(1); // 'FMC!' - signed ? - if (strncmp(header.id,"FMC!",4)) { fp.close(f); return false; } + if (memcmp(header.id, "FMC!", 4) || + header.numchan < 1 || header.numchan > 32) { + fp.close(f); return false; + } // init CmodPlayer realloc_instruments(32); @@ -89,6 +93,7 @@ bool CfmcLoader::load(const std::string &filename, const CFileProvider &fp) instruments[i].pitch_shift = f->readInt(1); f->readString(instruments[i].name, 21); + instruments[i].name[20] = 0; } // load tracks @@ -140,21 +145,20 @@ bool CfmcLoader::load(const std::string &filename, const CFileProvider &fp) for (i=0;i<31;i++) buildinst(i); - // order length - for (i=0;i<256;i++) - { - if (order[i] >= 0xFE) - { - length = i; - break; - } - } - // data for Protracker - activechan = (0xffffffff >> (32 - header.numchan)) << (32 - header.numchan); + activechan = (0xffffffffUL >> (32 - header.numchan)) << (32 - header.numchan); nop = t / header.numchan; + if (!nop) return false; // truncated file, no track data restartpos = 0; + // order length + for (length = 0; length < 256; length++) { + if (order[length] >= 0xFE) // end marker + break; + else if (order[length] >= nop /* or 64*/) // invalid pattern + return false; + } + // flags flags = Faust; @@ -180,7 +184,7 @@ std::string CfmcLoader::gettitle() std::string CfmcLoader::getinstrument(unsigned int n) { - return std::string(instruments[n].name); + return n < 32 ? std::string(instruments[n].name) : std::string(); } unsigned int CfmcLoader::getinstruments() diff --git a/plugins/adplug/adplug/fmopl.c b/plugins/adplug/adplug/fmopl.c index 0cd1d694d6..5b56164df2 100644 --- a/plugins/adplug/adplug/fmopl.c +++ b/plugins/adplug/adplug/fmopl.c @@ -78,11 +78,11 @@ static int opl_dbg_maxchip,opl_dbg_chip; /* -------------------- quality selection --------------------- */ -/* sinwave entries */ +/* sine wave entries */ /* used static memory = SIN_ENT * 4 (byte) */ #define SIN_ENT 2048 -/* output level entries (envelope,sinwave) */ +/* output level entries (envelope,sine wave) */ /* envelope counter lower bits */ #define ENV_BITS 16 /* envelope output entries */ @@ -185,13 +185,13 @@ static const INT32 SL_TABLE[16]={ }; #undef SC -#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */ +#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sine wave */ /* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */ /* TL_TABLE[ 0 to TL_MAX ] : plus section */ /* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */ static INT32 *TL_TABLE; -/* pointers to TL_TABLE with sinwave output offset */ +/* pointers to TL_TABLE with sine wave output offset */ static INT32 **SIN_TABLE; /* LFO table */ @@ -365,15 +365,15 @@ INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT ) return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0); } -/* set algorythm connection */ -static void set_algorythm( OPL_CH *CH) +/* set algorithm connection */ +static void set_algorithm( OPL_CH *CH) { INT32 *carrier = &outd[0]; CH->connect1 = CH->CON ? carrier : &feedback2; CH->connect2 = carrier; } -/* ---------- frequency counter for operater update ---------- */ +/* ---------- frequency counter for operator update ---------- */ INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) { int ksr; @@ -501,7 +501,7 @@ INLINE void OPL_CALC_CH( OPL_CH *CH ) } } -/* ---------- calcrate rythm block ---------- */ +/* ---------- calcrate rhythm block ---------- */ #define WHITE_NOISE_db 6.0 INLINE void OPL_CALC_RH( OPL_CH *CH ) { @@ -657,7 +657,7 @@ static int OPLOpenTable( void ) TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0; } - /* make sinwave table (total level offet) */ + /* make sine wave table (total level offset) */ /* degree 0 = degree 180 = off */ SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1]; for (s = 1;s <= SIN_ENT/4;s++){ @@ -718,8 +718,8 @@ static void OPLCloseTable( void ) free(VIB_TABLE); } -/* CSM Key Controll */ -INLINE void CSMKeyControll(OPL_CH *CH) +/* CSM Key Control */ +INLINE void CSMKeyControl(OPL_CH *CH) { OPL_SLOT *slot1 = &CH->SLOT[SLOT1]; OPL_SLOT *slot2 = &CH->SLOT[SLOT2]; @@ -736,7 +736,7 @@ INLINE void CSMKeyControll(OPL_CH *CH) } /* ---------- opl initialize ---------- */ -static void OPL_initalize(FM_OPL *OPL) +static void OPL_initialize(FM_OPL *OPL) { int fn; @@ -765,7 +765,7 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v) switch(r&0xe0) { - case 0x00: /* 00-1f:controll */ + case 0x00: /* 00-1f:control */ switch(r&0x1f) { case 0x01: @@ -829,7 +829,7 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v) LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n")); } return; - case 0x07: /* DELTA-T controll : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ + case 0x07: /* DELTA-T control : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ if(OPL->type&OPL_TYPE_ADPCM) YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v); return; @@ -898,14 +898,14 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v) case 0xbd: /* amsep,vibdep,r,bd,sd,tom,tc,hh */ { - UINT8 rkey = OPL->rythm^v; + UINT8 rkey = OPL->rhythm^v; OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0]; OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0]; - OPL->rythm = v&0x3f; - if(OPL->rythm&0x20) + OPL->rhythm = v&0x3f; + if(OPL->rhythm&0x20) { #if 0 - usrintf_showmessage("OPL Rythm mode select"); + usrintf_showmessage("OPL Rhythm mode select"); #endif /* BD key on/off */ if(rkey&0x10) @@ -998,7 +998,7 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v) int feedback = (v>>1)&7; CH->FB = feedback ? (8+1) - feedback : 0; CH->CON = v&1; - set_algorythm(CH); + set_algorithm(CH); } return; case 0xe0: /* wave type */ @@ -1052,7 +1052,7 @@ void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) OPLSAMPLE *buf = buffer; UINT32 amsCnt = OPL->amsCnt; UINT32 vibCnt = OPL->vibCnt; - UINT8 rythm = OPL->rythm&0x20; + UINT8 rhythm = OPL->rhythm&0x20; OPL_CH *CH,*R_CH; if( (void *)OPL != cur_chip ){ @@ -1060,7 +1060,7 @@ void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) /* channel pointers */ S_CH = OPL->P_CH; E_CH = &S_CH[9]; - /* rythm slot */ + /* rhythm slot */ SLOT7_1 = &S_CH[7].SLOT[SLOT1]; SLOT7_2 = &S_CH[7].SLOT[SLOT2]; SLOT8_1 = &S_CH[8].SLOT[SLOT1]; @@ -1071,7 +1071,7 @@ void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) ams_table = OPL->ams_table; vib_table = OPL->vib_table; } - R_CH = rythm ? &S_CH[6] : E_CH; + R_CH = rhythm ? &S_CH[6] : E_CH; for( i=0; i < length ; i++ ) { /* channel A channel B channel C */ @@ -1083,7 +1083,7 @@ void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) for(CH=S_CH ; CH < R_CH ; CH++) OPL_CALC_CH(CH); /* Rythn part */ - if(rythm) + if(rhythm) OPL_CALC_RH(S_CH); /* limit check */ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); @@ -1113,7 +1113,7 @@ void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) OPLSAMPLE *buf = buffer; UINT32 amsCnt = OPL->amsCnt; UINT32 vibCnt = OPL->vibCnt; - UINT8 rythm = OPL->rythm&0x20; + UINT8 rhythm = OPL->rhythm&0x20; OPL_CH *CH,*R_CH; YM_DELTAT *DELTAT = OPL->deltat; @@ -1125,7 +1125,7 @@ void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) /* channel pointers */ S_CH = OPL->P_CH; E_CH = &S_CH[9]; - /* rythm slot */ + /* rhythm slot */ SLOT7_1 = &S_CH[7].SLOT[SLOT1]; SLOT7_2 = &S_CH[7].SLOT[SLOT2]; SLOT8_1 = &S_CH[8].SLOT[SLOT1]; @@ -1136,7 +1136,7 @@ void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) ams_table = OPL->ams_table; vib_table = OPL->vib_table; } - R_CH = rythm ? &S_CH[6] : E_CH; + R_CH = rhythm ? &S_CH[6] : E_CH; for( i=0; i < length ; i++ ) { /* channel A channel B channel C */ @@ -1151,7 +1151,7 @@ void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) for(CH=S_CH ; CH < R_CH ; CH++) OPL_CALC_CH(CH); /* Rythn part */ - if(rythm) + if(rhythm) OPL_CALC_RH(S_CH); /* limit check */ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT ); @@ -1181,7 +1181,7 @@ void OPLResetChip(FM_OPL *OPL) OPLWriteReg(OPL,0x03,0); /* Timer2 */ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); - /* reset OPerator paramater */ + /* reset OPerator parameter */ for( c = 0 ; c < OPL->max_ch ; c++ ) { OPL_CH *CH = &OPL->P_CH[c]; @@ -1217,7 +1217,7 @@ FM_OPL *OPLCreate(int type, int clock, int rate) char *ptr; FM_OPL *OPL; int state_size; - int max_ch = 9; /* normaly 9 channels */ + int max_ch = 9; /* normally 9 channels */ if( OPL_LockTable() ==-1) return NULL; /* allocate OPL state space */ @@ -1241,8 +1241,8 @@ FM_OPL *OPLCreate(int type, int clock, int rate) OPL->clock = clock; OPL->rate = rate; OPL->max_ch = max_ch; - /* init grobal tables */ - OPL_initalize(OPL); + /* init global tables */ + OPL_initialize(OPL); /* reset chip */ OPLResetChip(OPL); #ifdef OPL_OUTPUT_LOG @@ -1269,6 +1269,10 @@ FM_OPL *OPLCreate(int type, int clock, int rate) /* ---------- Destroy one of vietual YM3812 ---------- */ void OPLDestroy(FM_OPL *OPL) { + if(!OPL) + { + return; + } #ifdef OPL_OUTPUT_LOG if(opl_dbg_fp) { @@ -1385,13 +1389,13 @@ int OPLTimerOver(FM_OPL *OPL,int c) else { /* Timer A */ OPL_STATUS_SET(OPL,0x40); - /* CSM mode key,TL controll */ + /* CSM mode key,TL control */ if( OPL->mode & 0x80 ) { /* CSM mode total level latch and auto key on */ int ch; if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); for(ch=0;ch<9;ch++) - CSMKeyControll( &OPL->P_CH[ch] ); + CSMKeyControl( &OPL->P_CH[ch] ); } } /* reload timer */ diff --git a/plugins/adplug/adplug/fmopl.h b/plugins/adplug/adplug/fmopl.h index a01ff902c7..b84c847433 100644 --- a/plugins/adplug/adplug/fmopl.h +++ b/plugins/adplug/adplug/fmopl.h @@ -110,8 +110,8 @@ typedef struct fm_opl_f { /* FM channel slots */ OPL_CH *P_CH; /* pointer of CH */ int max_ch; /* maximum channel */ - /* Rythm sention */ - UINT8 rythm; /* Rythm mode , key flag */ + /* Rhythm section */ + UINT8 rhythm; /* Rhythm mode , key flag */ #if BUILD_Y8950 /* Delta-T ADPCM unit (Y8950) */ YM_DELTAT *deltat; /* DELTA-T ADPCM */ diff --git a/plugins/adplug/adplug/got.h b/plugins/adplug/adplug/got.h index 6d11b5f460..b5890b2180 100644 --- a/plugins/adplug/adplug/got.h +++ b/plugins/adplug/adplug/got.h @@ -56,6 +56,11 @@ class CgotPlayer: public CPlayer return std::string("God of Thunder Music"); } + unsigned int getspeed() + { + return (int)rate; + } + protected: unsigned long pos, size; unsigned short del; diff --git a/plugins/adplug/adplug/herad.cpp b/plugins/adplug/adplug/herad.cpp index 7fe7e7758a..3c88c45892 100644 --- a/plugins/adplug/adplug/herad.cpp +++ b/plugins/adplug/adplug/herad.cpp @@ -1,4 +1,4 @@ -/* +/* * Adplug - Replayer for many OPL2/OPL3 audio file formats. * Copyright (C) 1999 - 2008 Simon Peter , et al. * @@ -40,8 +40,12 @@ #ifdef DEBUG #include "debug.h" +#else +#define AdPlug_LogWrite #endif +#include "load_helper.h" + const uint8_t CheradPlayer::slot_offset[HERAD_NUM_VOICES] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 }; @@ -65,9 +69,9 @@ std::string CheradPlayer::gettype() { char scomp[12 + 1] = ""; if (comp > HERAD_COMP_NONE) - sprintf(scomp, ", %s packed", (comp == HERAD_COMP_HSQ ? "HSQ" : "SQX")); + snprintf(scomp, sizeof(scomp), ", %s packed", (comp == HERAD_COMP_HSQ ? "HSQ" : "SQX")); char type[40 + 1]; - sprintf(type, "HERAD System %s (version %d%s)", (AGD ? "AGD" : "SDB"), (v2 ? 2 : 1), scomp); + snprintf(type, sizeof(type), "HERAD System %s (version %d%s)", (AGD ? "AGD" : "SDB"), (v2 ? 2 : 1), scomp); return std::string(type); } @@ -79,18 +83,17 @@ bool isHSQ(uint8_t * data, int size) // data[3] - word CompSize // data[4] // data[5] - byte Checksum - if ( data[2] != 0 ) + if (data[2] != 0) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: Is not HSQ, wrong check byte.\n"); - #endif return false; } - if ( *(uint16_t *)(data + 3) != size ) + + const uint16_t temp_size = u16_unaligned(data + 3); + + if (temp_size != size) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: Is not HSQ, wrong compressed size.\n"); - #endif return false; } uint8_t checksum = 0; @@ -98,11 +101,9 @@ bool isHSQ(uint8_t * data, int size) { checksum += data[i]; } - if ( checksum != 0xAB ) + if (checksum != 0xAB) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: Is not HSQ, wrong checksum.\n"); - #endif return false; } return true; @@ -116,18 +117,14 @@ bool isSQX(uint8_t * data) // data[3] - byte SQX flag #2 // data[4] - byte SQX flag #3 // data[5] - byte CntOffPart - if ( data[2] > 2 || data[3] > 2 || data[4] > 2 ) + if (data[2] > 2 || data[3] > 2 || data[4] > 2) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: Is not SQX, wrong flags.\n"); - #endif return false; } - if ( data[5] == 0 || data[5] > 15 ) + if (data[5] == 0 || data[5] > 15) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: Is not SQX, wrong bit count.\n"); - #endif return false; } return true; @@ -138,7 +135,7 @@ uint16_t HSQ_decompress(uint8_t * data, int size, uint8_t * out) uint32_t queue = 1; int8_t bit; int16_t offset; - uint16_t count, out_size = *(uint16_t *)data; + uint16_t count, out_size = u16_unaligned(data); uint8_t * src = data; uint8_t * dst = out; @@ -148,7 +145,7 @@ uint16_t HSQ_decompress(uint8_t * data, int size, uint8_t * out) // get next bit of the queue if (queue == 1) { - queue = *(uint16_t *)src | 0x10000; + queue = u16_unaligned(src) | 0x10000; src += 2; } bit = queue & 1; @@ -164,7 +161,7 @@ uint16_t HSQ_decompress(uint8_t * data, int size, uint8_t * out) // get next bit of the queue if (queue == 1) { - queue = *(uint16_t *)src | 0x10000; + queue = u16_unaligned(src) | 0x10000; src += 2; } bit = queue & 1; @@ -174,7 +171,7 @@ uint16_t HSQ_decompress(uint8_t * data, int size, uint8_t * out) { // count = next 3 bits of the input // offset = next 13 bits of the input minus 8192 - count = *(uint16_t *)src; + count = u16_unaligned(src); offset = (count >> 3) - 8192; count &= 7; src += 2; @@ -194,7 +191,7 @@ uint16_t HSQ_decompress(uint8_t * data, int size, uint8_t * out) // count = next bit of the queue * 2 + next bit of the queue if (queue == 1) { - queue = *(uint16_t *)src | 0x10000; + queue = u16_unaligned(src) | 0x10000; src += 2; } bit = queue & 1; @@ -202,7 +199,7 @@ uint16_t HSQ_decompress(uint8_t * data, int size, uint8_t * out) count = bit << 1; if (queue == 1) { - queue = *(uint16_t *)src | 0x10000; + queue = u16_unaligned(src) | 0x10000; src += 2; } bit = queue & 1; @@ -233,7 +230,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) uint8_t * dst = out; bool done = false; - *(uint16_t *)dst = *(uint16_t *)src; + std::memcpy(dst, src, sizeof(uint16_t)); src += 6; uint16_t queue = 1; uint8_t bit, bit_p; @@ -243,7 +240,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) queue >>= 1; if (queue == 0) { - queue = *(uint16_t *)src; + queue = u16_unaligned(src); src += 2; bit_p = bit; bit = queue & 1; @@ -264,7 +261,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) queue >>= 1; if (queue == 0) { - queue = *(uint16_t *)src; + queue = u16_unaligned(src); src += 2; bit_p = bit; bit = queue & 1; @@ -282,7 +279,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) queue >>= 1; if (queue == 0) { - queue = *(uint16_t *)src; + queue = u16_unaligned(src); src += 2; bit_p = bit; bit = queue & 1; @@ -303,7 +300,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) } break; case 2: - count = *(uint16_t *)src; + count = u16_unaligned(src); offset = (count >> data[5]) - (1 << (16 - data[5])); count &= (1 << data[5]) - 1; src += 2; @@ -335,7 +332,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) queue >>= 1; if (queue == 0) { - queue = *(uint16_t *)src; + queue = u16_unaligned(src); src += 2; bit_p = bit; bit = queue & 1; @@ -356,7 +353,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) queue >>= 1; if (queue == 0) { - queue = *(uint16_t *)src; + queue = u16_unaligned(src); src += 2; bit_p = bit; bit = queue & 1; @@ -374,7 +371,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) queue >>= 1; if (queue == 0) { - queue = *(uint16_t *)src; + queue = u16_unaligned(src); src += 2; bit_p = bit; bit = queue & 1; @@ -395,7 +392,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) } break; case 2: - count = *(uint16_t *)src; + count = u16_unaligned(src); offset = (count >> data[5]) - (1 << (16 - data[5])); count &= (1 << data[5]) - 1; src += 2; @@ -434,7 +431,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) queue >>= 1; if (queue == 0) { - queue = *(uint16_t *)src; + queue = u16_unaligned(src); src += 2; bit_p = bit; bit = queue & 1; @@ -452,7 +449,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) queue >>= 1; if (queue == 0) { - queue = *(uint16_t *)src; + queue = u16_unaligned(src); src += 2; bit_p = bit; bit = queue & 1; @@ -473,7 +470,7 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) } break; case 2: - count = *(uint16_t *)src; + count = u16_unaligned(src); offset = (count >> data[5]) - (1 << (16 - data[5])); count &= (1 << data[5]) - 1; src += 2; @@ -504,6 +501,89 @@ uint16_t SQX_decompress(uint8_t * data, int size, uint8_t * out) return dst - out; } +bool CheradPlayer::validEvent(int i, uint16_t * offset, bool v2) +{ + while (*offset < track[i].size && (track[i].data[(*offset)++] & 0x80) > 0); + if (*offset >= track[i].size) + { + AdPlug_LogWrite("HERAD: Reading out of range.\n"); + return false; + } + + uint8_t status = track[i].data[(*offset)++]; + uint8_t check; + + if (status < 0x80) + { + AdPlug_LogWrite("HERAD: Unexpected status.\n"); + return false; + } + else if (status < 0x90 && v2) + { + check = track[i].data[(*offset)++]; + if (check > 0x7F) + { + AdPlug_LogWrite("HERAD: Unexpected param.\n"); + return false; + } + } + else if (status < 0xC0) + { + check = track[i].data[(*offset)++]; + if (check > 0x7F) + { + AdPlug_LogWrite("HERAD: Unexpected param 1.\n"); + return false; + } + check = track[i].data[(*offset)++]; + if (check > 0x7F) + { + AdPlug_LogWrite("HERAD: Unexpected param 2.\n"); + return false; + } + } + else if (status < 0xF0) + { + check = track[i].data[(*offset)++]; + if (check > 0x7F) + { + AdPlug_LogWrite("HERAD: Unexpected param.\n"); + return false; + } + } + else if (status == 0xFF) + { + *offset = track[i].size; + } + + return true; +} + +uint8_t CheradPlayer::validTracks() +{ + for (int i = 0; i < nTracks; i++) + { + uint16_t of_v1 = 0, of_v2 = 0; + + while (of_v1 < track[i].size || of_v2 < track[i].size) + { + if (of_v1 < track[i].size) + { + if (!validEvent(i, &of_v1, false)) + return 1; + } + + if (of_v2 < track[i].size) + { + if (!validEvent(i, &of_v2, true)) + return 2; + } + } + } + + return 0; +} + bool CheradPlayer::load(const std::string &filename, const CFileProvider &fp) { binistream *f = fp.open(filename); if(!f) return false; @@ -515,26 +595,20 @@ bool CheradPlayer::load(const std::string &filename, const CFileProvider &fp) !fp.extension(filename, ".agd") && !fp.extension(filename, ".ha2")) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: Unsupported file extension.\n"); - #endif fp.close(f); return false; } int size = fp.filesize(f); if (size < HERAD_MIN_SIZE) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: File size is too small.\n"); - #endif fp.close(f); return false; } if (size > HERAD_MAX_SIZE) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: File size is too big.\n"); - #endif fp.close(f); return false; } @@ -573,50 +647,40 @@ bool CheradPlayer::load(const std::string &filename, const CFileProvider &fp) uint16_t offset; if (size < HERAD_HEAD_SIZE) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: File size is too small.\n"); - #endif goto failure; } - if ( size < *(uint16_t *)data ) + if (size < u16_unaligned(data)) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: Incorrect offset / file size.\n"); - #endif goto failure; } - nInsts = (size - *(uint16_t *)data) / HERAD_INST_SIZE; - if ( nInsts == 0 ) + nInsts = (size - u16_unaligned(data)) / HERAD_INST_SIZE; + if (nInsts == 0) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: M32 files are not supported.\n"); - #endif goto failure; } - offset = *(uint16_t *)(data + 2); - if ( offset != 0x32 && offset != 0x52 ) + offset = u16_unaligned(data + 2); + if (offset != 0x32 && offset != 0x52) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: Wrong first track offset.\n"); - #endif goto failure; } AGD = offset == 0x52; - wLoopStart = *(uint16_t *)(data + 0x2C); - wLoopEnd = *(uint16_t *)(data + 0x2E); - wLoopCount = *(uint16_t *)(data + 0x30); - wSpeed = *(uint16_t *)(data + 0x32); + wLoopStart = u16_unaligned(data + 0x2C); + wLoopEnd = u16_unaligned(data + 0x2E); + wLoopCount = u16_unaligned(data + 0x30); + wSpeed = u16_unaligned(data + 0x32); if (wSpeed == 0) { - #ifdef DEBUG AdPlug_LogWrite("HERAD: Speed is not defined.\n"); - #endif goto failure; } nTracks = 0; for (int i = 0; i < HERAD_MAX_TRACKS; i++) { - if ( *(uint16_t *)(data + 2 + i * 2) == 0 ) + if (u16_unaligned(data + 2 + i * 2) == 0) break; nTracks++; } @@ -624,22 +688,28 @@ bool CheradPlayer::load(const std::string &filename, const CFileProvider &fp) chn = new herad_chn[nTracks]; for (int i = 0; i < nTracks; i++) { - offset = *(uint16_t *)(data + 2 + i * 2) + 2; - uint16_t next = (i < HERAD_MAX_TRACKS - 1 ? *(uint16_t *)(data + 2 + (i + 1) * 2) + 2 : *(uint16_t *)data); - if (next <= 2) next = *(uint16_t *)data; + offset = u16_unaligned(data + 2 + i * 2) + 2; + uint16_t next = (i < HERAD_MAX_TRACKS - 1) ? u16_unaligned(data + 2 + (i + 1) * 2) + 2 : u16_unaligned(data); + if (next <= 2) next = u16_unaligned(data); track[i].size = next - offset; track[i].data = new uint8_t[track[i].size]; memcpy(track[i].data, data + offset, track[i].size); } inst = new herad_inst[nInsts]; - offset = *(uint16_t *)data; - v2 = true; + offset = u16_unaligned(data); + v2 = false; for (int i = 0; i < nInsts; i++) { memcpy(inst[i].data, data + offset + i * HERAD_INST_SIZE, HERAD_INST_SIZE); - if (v2 && inst[i].param.mode == HERAD_INSTMODE_SDB1) - v2 = false; + if (!v2 && inst[i].param.mode == HERAD_INSTMODE_KMAP) + v2 = true; + } + if (!v2) + { + // Aggressive detection for HERAD version 2 without keymap instruments + // (if version 1 event parser reports error, it's version 2) + v2 = (validTracks() == 1); } delete[] data; goto good; @@ -659,9 +729,9 @@ void CheradPlayer::rewind(int subsong) wTime = 0; songend = false; - ticks_pos = -1; // there's always 1 excess tick at start + ticks_pos = ~0; // there's always 1 excess tick at start total_ticks = 0; - loop_pos = -1; + loop_pos = ~0; loop_times = 1; for (int i = 0; i < nTracks; i++) diff --git a/plugins/adplug/adplug/herad.h b/plugins/adplug/adplug/herad.h index 9c18d11de9..6a2a5a8673 100644 --- a/plugins/adplug/adplug/herad.h +++ b/plugins/adplug/adplug/herad.h @@ -1,4 +1,4 @@ -/* +/* * Adplug - Replayer for many OPL2/OPL3 audio file formats. * Copyright (C) 1999 - 2005 Simon Peter , et al. * @@ -121,6 +121,8 @@ class CheradPlayer: public CPlayer }; private: + bool validEvent(int i, uint16_t * offset, bool v2); + uint8_t validTracks(); uint32_t GetTicks(uint8_t t); void executeCommand(uint8_t t); void processEvents(); @@ -146,7 +148,7 @@ class CheradPlayer: public CPlayer protected: bool songend; int16_t wTime; - int32_t ticks_pos; /* current tick counter */ + uint32_t ticks_pos; /* current tick counter */ uint32_t total_ticks; /* total ticks in song */ uint8_t comp; /* File compression type (see HERAD_COMP_*) */ @@ -236,9 +238,9 @@ class CheradPlayer: public CPlayer herad_chn * chn; /* active channels [nTracks] */ herad_inst * inst; /* instruments [nInsts] */ - int32_t loop_pos; + uint32_t loop_pos; uint16_t loop_times; herad_trk loop_data[HERAD_MAX_TRACKS]; }; -#endif \ No newline at end of file +#endif diff --git a/plugins/adplug/adplug/hsc.cpp b/plugins/adplug/adplug/hsc.cpp index af6520cb41..25b406c007 100644 --- a/plugins/adplug/adplug/hsc.cpp +++ b/plugins/adplug/adplug/hsc.cpp @@ -51,24 +51,23 @@ bool ChscPlayer::load(const std::string &filename, const CFileProvider &fp) int total_patterns_in_hsc = (fp.filesize(f) - 1587) / 1152; // load section - f->readBuf ((char *)instr, 128*12); + for(i=0;i<128*12;i++) // load instruments + *((unsigned char *)instr + i) = f->readInt(1); for (i=0;i<128;i++) { // correct instruments instr[i][2] ^= (instr[i][2] & 0x40) << 1; instr[i][3] ^= (instr[i][3] & 0x40) << 1; instr[i][11] >>= 4; // slide } - - f->readBuf ((char *)song, 51); // load tracklist - for(i=0;i<51;i++) { // load tracklist + song[i] = f->readInt(1); // if out of range, song ends here if ( ((song[i] & 0x7F) > 0x31) || ((song[i] & 0x7F) >= total_patterns_in_hsc) ) song[i] = 0xFF; } - - f->readBuf ((char *)patterns, 50*64*9); // load patterns + for(i=0;i<50*64*9;i++) // load patterns + *((char *)patterns + i) = f->readInt(1); fp.close(f); rewind(0); // rewind module diff --git a/plugins/adplug/adplug/hybrid.cpp b/plugins/adplug/adplug/hybrid.cpp index 550ee43e4f..f4003154fe 100644 --- a/plugins/adplug/adplug/hybrid.cpp +++ b/plugins/adplug/adplug/hybrid.cpp @@ -193,8 +193,10 @@ void CxadhybridPlayer::xadplayer_update() // is slide ? if (slide) { - hyb.channel[i].freq_slide = (((slide >> 3) * -1) * (slide & 7)) << 1; - + // Looks bogus. Should values 0..8 really result + // in 0 or is bit 3 supposed to be a sign? + hyb.channel[i].freq_slide = -(slide >> 3) * (slide & 7) * 2; + // And this statement is dead anyway. if (slide & 0x80) slide = -(slide & 7); } diff --git a/plugins/adplug/adplug/hyp.cpp b/plugins/adplug/adplug/hyp.cpp index 3f0ddae3ff..12a2c98d08 100644 --- a/plugins/adplug/adplug/hyp.cpp +++ b/plugins/adplug/adplug/hyp.cpp @@ -107,7 +107,7 @@ void CxadhypPlayer::xadplayer_update() hyp.pointer += 3; - if (hyp.pointer >= tune_size) + if (hyp.pointer > tune_size - 9) { hyp.pointer = 0x69; plr.looping = 1; diff --git a/plugins/adplug/adplug/hyp.h b/plugins/adplug/adplug/hyp.h index 0ddd54c6ad..8a1103c068 100644 --- a/plugins/adplug/adplug/hyp.h +++ b/plugins/adplug/adplug/hyp.h @@ -37,7 +37,7 @@ class CxadhypPlayer: public CxadPlayer // bool xadplayer_load() { - if(xad.fmt == HYP) + if (xad.fmt == HYP && tune_size >= 0x69 + 9) return true; else return false; diff --git a/plugins/adplug/adplug/imf.cpp b/plugins/adplug/adplug/imf.cpp index df2cb1d1e8..19ff04ed85 100644 --- a/plugins/adplug/adplug/imf.cpp +++ b/plugins/adplug/adplug/imf.cpp @@ -41,7 +41,7 @@ * and more. */ -#include +#include #include "imf.h" #include "database.h" @@ -55,11 +55,11 @@ CPlayer *CimfPlayer::factory(Copl *newopl) bool CimfPlayer::load(const std::string &filename, const CFileProvider &fp) { - binistream *f = fp.open(filename); if(!f) return false; - unsigned long fsize, flsize, mfsize = 0; - unsigned int i; + binistream *f = fp.open(filename); + if (!f) return false; // file validation section + unsigned long hdr_size = 0; { char header[5]; int version; @@ -67,8 +67,8 @@ bool CimfPlayer::load(const std::string &filename, const CFileProvider &fp) f->readString(header, 5); version = f->readInt(1); - if(strncmp(header, "ADLIB", 5) || version != 1) { - if(!fp.extension(filename, ".imf") && !fp.extension(filename, ".wlf")) { + if (memcmp(header, "ADLIB", 5) || version != 1) { + if (!fp.extension(filename, ".imf") && !fp.extension(filename, ".wlf")) { // It's no IMF file at all fp.close(f); return false; @@ -79,45 +79,73 @@ bool CimfPlayer::load(const std::string &filename, const CFileProvider &fp) track_name = f->readString('\0'); game_name = f->readString('\0'); f->ignore(1); - mfsize = f->pos() + 2; + hdr_size = f->pos(); } } - // load section - if(mfsize) - fsize = f->readInt(4); - else - fsize = f->readInt(2); - flsize = fp.filesize(f); - if(!fsize) { // footerless file (raw music data) - if(mfsize) - f->seek(-4, binio::Add); - else - f->seek(-2, binio::Add); - size = (flsize - mfsize) / 4; - } else // file has got a footer - size = fsize / 4; + // determine data size + unsigned long file_size = fp.filesize(f); + int len_size = hdr_size ? 4 : 2; + unsigned long song_size = f->readInt(len_size); + + if (!song_size) { // raw music data (no length field, no footer) + f->seek(-len_size, binio::Add); + len_size = 0; + song_size = file_size - hdr_size; + // some IMF files end *before* the last time value (either format) + if (song_size & 2) song_size += 2; + } + + hdr_size += len_size; + + // validity check + if (hdr_size + 4 > file_size || song_size & 3 // too short || invalid length + || (song_size > file_size - hdr_size && // truncated song data + song_size != file_size + 2 - hdr_size)) { // allow trunc. final time + fp.close(f); + return false; + } + // read song data + size = song_size / 4; data = new Sdata[size]; - for(i = 0; i < size; i++) { - data[i].reg = f->readInt(1); data[i].val = f->readInt(1); + for (unsigned long i = 0; i < size; i++) { + data[i].reg = f->readInt(1); + data[i].val = f->readInt(1); data[i].time = f->readInt(2); } // read footer, if any - if(fsize && (fsize < flsize - 2 - mfsize)) { - if(f->readInt(1) == 0x1a) { + if (song_size < file_size - hdr_size) { + unsigned long footerlen = file_size - hdr_size - song_size; + char signature = f->readInt(1); + + if (signature == 0x1a && footerlen <= 1 + 3 * 256 + 9) { // Adam Nielsen's footer format track_name = f->readString(); author_name = f->readString(); remarks = f->readString(); + // f->ignore(9); // tagging program } else { // Generic footer - unsigned long footerlen = flsize - fsize - 2 - mfsize; - footer = new char[footerlen + 1]; - f->readString(footer, footerlen); + footer[0] = signature; + f->readString(&footer[1], footerlen); footer[footerlen] = '\0'; // Make ASCIIZ string + + if (footerlen == 88 && // maybe Muse tag data + !footer[17] && !footer[81] && track_name.empty()) { + // For the lack of a better test assume it's Muse data if the size + // is correct and both string fields end with NUL. Format is: + // 2 Bytes: unknown + // 16 Bytes: title + // 64 Bytes: remarks (usually source file) + // 6 Bytes: unknown data + track_name = std::string(&footer[2]); + remarks = std::string(&footer[18]); + delete[] footer; + footer = 0; + } } } @@ -152,31 +180,18 @@ void CimfPlayer::rewind(int subsong) std::string CimfPlayer::gettitle() { - std::string title; + if (game_name.empty()) return track_name; + if (track_name.empty()) return game_name; - title = track_name; - - if(!track_name.empty() && !game_name.empty()) - title += " - "; - - title += game_name; - - return title; + return track_name + " - " + game_name; } std::string CimfPlayer::getdesc() { - std::string desc; - - if(footer) - desc = std::string(footer); - - if(!remarks.empty() && footer) - desc += "\n\n"; - - desc += remarks; + if (footer) + return std::string(footer); - return desc; + return remarks; } /*** private methods *************************************/ diff --git a/plugins/adplug/adplug/jbm.cpp b/plugins/adplug/adplug/jbm.cpp index 93013bd13c..dd3caa152e 100644 --- a/plugins/adplug/adplug/jbm.cpp +++ b/plugins/adplug/adplug/jbm.cpp @@ -65,9 +65,9 @@ CPlayer *CjbmPlayer::factory(Copl *newopl) bool CjbmPlayer::load(const std::string &filename, const CFileProvider &fp) { - binistream *f = fp.open(filename); if(!f) return false; - int filelen = fp.filesize(f); - int i; + binistream *f = fp.open(filename); if(!f) return false; + unsigned int filelen = fp.filesize(f); + int i; if (!filelen || !fp.extension(filename, ".jbm")) goto loaderr; @@ -177,14 +177,14 @@ bool CjbmPlayer::update() if (flags&1 && c > 6) opl->write(0x40 + percmx_tab[c-7], voice[c].vol ^ 0x3f); - else + else if (c < 9) opl->write(0x43 + op_table[c], voice[c].vol ^ 0x3f); // Write new frequencies and Gate bit opl_noteonoff(c, &voice[c], !(voice[c].note & 0x80)); } - return (voicemask); + return (!!voicemask); } void CjbmPlayer::rewind(int subsong) @@ -270,6 +270,9 @@ void CjbmPlayer::set_opl_instrument(int channel, JBMVoice *v) return; } + if (channel >= 9) + return; + // AM/VIB/EG/KSR/FRQMUL, KSL/OUTPUT, ADSR for 1st operator opl->write(0x20 + op_table[channel], m[i+0]); opl->write(0x40 + op_table[channel], m[i+1] ^ 0x3f); diff --git a/plugins/adplug/adplug/jbm.h b/plugins/adplug/adplug/jbm.h index 2677274d0d..1f86cd31cb 100644 --- a/plugins/adplug/adplug/jbm.h +++ b/plugins/adplug/adplug/jbm.h @@ -29,10 +29,10 @@ class CjbmPlayer: public CPlayer public: static CPlayer *factory(Copl *newopl); - CjbmPlayer(Copl *newopl) : CPlayer(newopl), m(0) + CjbmPlayer(Copl *newopl) : CPlayer(newopl), m(0), sequences(0) { } ~CjbmPlayer() - { if(m != NULL) delete [] m; } + { delete[] sequences; delete[] m; } bool load(const std::string &filename, const CFileProvider &fp); bool update(); diff --git a/plugins/adplug/adplug/kemuopl.cpp b/plugins/adplug/adplug/kemuopl.cpp new file mode 100644 index 0000000000..782951a8de --- /dev/null +++ b/plugins/adplug/adplug/kemuopl.cpp @@ -0,0 +1,70 @@ +#include "kemuopl.h" + +CKemuopl::CKemuopl(int rate, bool bit16, bool usestereo) + : use16bit(bit16), stereo(usestereo), sampleerate(rate), mixbufSamples(0) +{ + memset (ctx, 0, sizeof (ctx)); + currType = TYPE_DUAL_OPL2; + init(); +}; + +CKemuopl::~CKemuopl() +{ + if(mixbufSamples) { + delete [] mixbuf0; + delete [] mixbuf1; + delete [] mixbuf2; + } +} + +void CKemuopl::update(short *buf, int samples) +{ + int i; + //ensure that our mix buffers are adequately sized + if(mixbufSamples < samples) { + if(mixbufSamples) { delete[] mixbuf0; delete[] mixbuf1; delete[] mixbuf2; } + mixbufSamples = samples; + + //*2 = make room for stereo, if we need it + mixbuf0 = new short[samples*2]; + mixbuf1 = new short[samples*2]; + mixbuf2 = new short[samples*2]; + } + + short *outbuf = use16bit ? buf : mixbuf2; + + //render each chip to a different tempbuffer + adlibgetsample(&ctx[0], (unsigned char *)mixbuf0, samples * 2 /* 16-bit */); + adlibgetsample(&ctx[1], (unsigned char *)mixbuf1, samples * 2 /* 16-bit */); + + //output stereo: + //then we need to interleave the two buffers + if(stereo){ + //left channel + for(i=0;i>1) + (mixbuf1[i]>>1); + + //now reduce to 8bit if we need to + if(!use16bit) + for(i=0;i<(stereo ? samples*2 : samples);i++) + ((char *)buf)[i] = (outbuf[i] >> 8) ^ 0x80; +} + +void CKemuopl::write(int reg, int val) +{ + adlib0(&ctx[currChip], reg, val); +}; + +void CKemuopl::init() +{ + adlibinit(&ctx[0], sampleerate, /* mono */ 1, /* 16-bit */ 2); + adlibinit(&ctx[1], sampleerate, /* mono */ 1, /* 16-bit */ 2); + currChip = 0; +}; diff --git a/plugins/adplug/adplug/kemuopl.h b/plugins/adplug/adplug/kemuopl.h index d68785fa92..daa940e72e 100644 --- a/plugins/adplug/adplug/kemuopl.h +++ b/plugins/adplug/adplug/kemuopl.h @@ -23,7 +23,11 @@ #ifndef H_ADPLUG_KEMUOPL #define H_ADPLUG_KEMUOPL +#define CKEMUOPL_MULTIINSTANCE 1 + #include "opl.h" +#include + extern "C" { #include "adlibemu.h" } @@ -31,31 +35,21 @@ extern "C" { class CKemuopl: public Copl { public: - CKemuopl(int rate, bool bit16, bool usestereo) - : use16bit(bit16), stereo(usestereo) - { - adlibinit(rate, usestereo ? 2 : 1, bit16 ? 2 : 1); - currType = TYPE_OPL2; - }; - - void update(short *buf, int samples) - { - if(use16bit) samples *= 2; - if(stereo) samples *= 2; - adlibgetsample(buf, samples); - } - - // template methods - void write(int reg, int val) - { - if(currChip == 0) - adlib0(reg, val); - }; - - void init() {}; + CKemuopl(int rate, bool bit16, bool usestereo); + virtual ~CKemuopl(); + + + void update(short *buf, int samples); + void write(int reg, int val); + + void init(); private: - bool use16bit,stereo; + bool use16bit, stereo; + int sampleerate; + adlibemu_context ctx[2]; + short *mixbuf0, *mixbuf1, *mixbuf2; + int mixbufSamples; }; #endif diff --git a/plugins/adplug/adplug/ksm.cpp b/plugins/adplug/adplug/ksm.cpp index ecf72a19de..67259c7e1c 100644 --- a/plugins/adplug/adplug/ksm.cpp +++ b/plugins/adplug/adplug/ksm.cpp @@ -98,7 +98,7 @@ bool CksmPlayer::load(const std::string &filename, const CFileProvider &fp) bool CksmPlayer::update() { - int quanter,chan = 0,drumnum = 0,freq,track,volevel,volval; + int quanter,chan,drumnum,freq,track,volevel,volval; unsigned int i,j,bufnum; unsigned long temp,templong; @@ -181,6 +181,7 @@ bool CksmPlayer::update() case 13: drumnum = 4; chan = 8; break; case 14: drumnum = 2; chan = 8; break; case 15: drumnum = 1; chan = 7; freq -= 2048; break; + default: drumnum = 0; chan = 0; break; // should not be reachable } databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0xa0+chan); bufnum++; @@ -240,21 +241,21 @@ void CksmPlayer::rewind(int subsong) if (trchan[11] == 1) { for(i=0;i<11;i++) instbuf[i] = inst[trinst[11]][i]; - instbuf[1] = ((instbuf[1]&192)|(trvol[11])^63); + instbuf[1] = ((instbuf[1]&192)|(trvol[11]^63)); setinst(6,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]); for(i=0;i<5;i++) instbuf[i] = inst[trinst[12]][i]; for(i=5;i<11;i++) instbuf[i] = inst[trinst[15]][i]; - instbuf[1] = ((instbuf[1]&192)|(trvol[12])^63); - instbuf[6] = ((instbuf[6]&192)|(trvol[15])^63); + instbuf[1] = ((instbuf[1]&192)|(trvol[12]^63)); + instbuf[6] = ((instbuf[6]&192)|(trvol[15]^63)); setinst(7,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]); for(i=0;i<5;i++) instbuf[i] = inst[trinst[14]][i]; for(i=5;i<11;i++) instbuf[i] = inst[trinst[13]][i]; - instbuf[1] = ((instbuf[1]&192)|(trvol[14])^63); - instbuf[6] = ((instbuf[6]&192)|(trvol[13])^63); + instbuf[1] = ((instbuf[1]&192)|(trvol[14]^63)); + instbuf[6] = ((instbuf[6]&192)|(trvol[13]^63)); setinst(8,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]); } diff --git a/plugins/adplug/adplug/lds.cpp b/plugins/adplug/adplug/lds.cpp index e97df80590..b02124787e 100644 --- a/plugins/adplug/adplug/lds.cpp +++ b/plugins/adplug/adplug/lds.cpp @@ -146,8 +146,9 @@ bool CldsPlayer::load(const std::string &filename, const CFileProvider &fp) // load patterns f->ignore(2); // ignore # of digital sounds (not played by this player) - patterns = new unsigned short[(fp.filesize(f) - f->pos()) / 2 + 1]; - for(i = 0; !f->eof(); i++) + patterns_size = (fp.filesize(f) - f->pos()) / 2; + patterns = new unsigned short[patterns_size + 1]; + for(i = 0; i < patterns_size; i++) patterns[i] = f->readInt(2); fp.close(f); @@ -166,11 +167,11 @@ bool CldsPlayer::update() if(!playing) return false; // handle fading - if(fadeonoff) + if(fadeonoff) { if(fadeonoff <= 128) { - if(allvolume > fadeonoff || allvolume == 0) + if(allvolume > fadeonoff || allvolume == 0) { allvolume -= fadeonoff; - else { + } else { allvolume = 1; fadeonoff = 0; if(hardfade != 0) { @@ -180,13 +181,15 @@ bool CldsPlayer::update() channel[i].keycount = 1; } } - } else - if(((allvolume + (0x100 - fadeonoff)) & 0xff) <= mainvolume) + } else { + if(((allvolume + (0x100 - fadeonoff)) & 0xff) <= mainvolume) { allvolume += 0x100 - fadeonoff; - else { + } else { allvolume = mainvolume; fadeonoff = 0; } + } + } // handle channel delay for(chan = 0; chan < 9; chan++) { @@ -205,9 +208,13 @@ bool CldsPlayer::update() unsigned short patnum = positions[posplay * 9 + chan].patnum; unsigned char transpose = positions[posplay * 9 + chan].transpose; - comword = patterns[patnum + c->packpos]; + if ((patnum + c->packpos) < patterns_size) + comword = patterns[patnum + c->packpos]; + else + comword = 0x8001; + comhi = comword >> 8; comlo = comword & 0xff; - if(comword) + if(comword) { if(comhi == 0x80) c->packwait = comlo; else @@ -320,6 +327,7 @@ bool CldsPlayer::update() c->chancheat.high = high; } } + } c->packpos++; } else @@ -549,11 +557,6 @@ void CldsPlayer::rewind(int subsong) void CldsPlayer::playsound(int inst_number, int channel_number, int tunehigh) { Channel *c = &channel[channel_number]; // current channel - - if (inst_number >= numpatch) { - inst_number = numpatch-1; - } - SoundBank *i = &soundbank[inst_number]; // current instrument unsigned int regnum = op_table[channel_number]; // channel's OPL2 register unsigned char volcalc, octave; diff --git a/plugins/adplug/adplug/load_helper.h b/plugins/adplug/adplug/load_helper.h new file mode 100644 index 0000000000..33119693e7 --- /dev/null +++ b/plugins/adplug/adplug/load_helper.h @@ -0,0 +1,58 @@ +#ifndef ADPLUG_LOAD_HELPER_H +#define ADPLUG_LOAD_HELPER_H + +#include +#include + +inline uint16_t adplug_byteswap(const uint16_t val) +{ + return + ((val & 0x00FF) << 8) | + ((val & 0xFF00) >> 8); +} + +inline uint32_t adplug_byteswap(const uint32_t val) +{ + return + ((val & 0x000000FF) << 24) | + ((val & 0x0000FF00) << 8) | + ((val & 0x00FF0000) >> 8) | + ((val & 0xFF000000) >> 24); +} + +// In many cases, we need to load a uint16_t/uint32_t from a (possibly) +// unaligned byte stream. In order to avoid undefined behavior, we have to use +// memcpy as the only portable way to perform type punning. See: +// https://blog.regehr.org/archives/959 +template +static inline T load_unaligned_impl(const unsigned char* src, const bool big_endian) +{ + T result; + std::memcpy(&result, src, sizeof(T)); + +#ifdef WORDS_BIGENDIAN + // big-endian CHOST + if (!big_endian) +#else + // little-endian CHOST + if (big_endian) +#endif + { + // have to do a byte-swap + result = adplug_byteswap(result); + } + + return result; +} + +inline uint16_t u16_unaligned(const unsigned char* src, const bool big_endian = false) +{ + return load_unaligned_impl(src, big_endian); +} + +inline uint32_t u32_unaligned(const unsigned char* src, const bool big_endian = false) +{ + return load_unaligned_impl(src, big_endian); +} + +#endif // ADPLUG_LOAD_HELPER_H diff --git a/plugins/adplug/adplug/mdi.cpp b/plugins/adplug/adplug/mdi.cpp index 982621a61a..b7566bdf06 100644 --- a/plugins/adplug/adplug/mdi.cpp +++ b/plugins/adplug/adplug/mdi.cpp @@ -92,24 +92,26 @@ bool CmdiPlayer::load(const std::string &filename, const CFileProvider &fp) f->readString((char *)data, size); fp.close(f); - drv = new CadlibDriver(opl); rewind(0); return true; } -void CmdiPlayer::rewind(int subsong) +void CmdiPlayer::frontend_rewind(int subsong) { // set default MIDI tempo SetTempo(MIDI_DEF_TEMPO); pos = 0; songend = false; + // midiplay uses rhythm mode by default + SetRhythmMode(1); + for (int i = 0; i < MAX_VOICES; i++) + { volume[i] = 0; + SetDefaultInstrument(i); + } counter = 0; ticks = 0; - - opl->init(); - if (drv) drv->SoundWarmInit(); } /* @@ -193,18 +195,16 @@ void CmdiPlayer::executeCommand() Following bytes contain instrument parameters. */ voice = data[pos + 5]; - int16_t params[ADLIB_INST_LEN]; - for (int n = 0; n < ADLIB_INST_LEN; n++) - params[n] = (int8_t)data[pos + META_MIN_SIZE + n]; - if (drv) drv->SetVoiceTimbre(voice, ¶ms[0]); + int index = load_instrument_data(&data[pos + META_MIN_SIZE], ADLIB_INST_LEN); + SetInstrument(voice, index); } else if (code == ADLIB_RHYTHM) { /* Melo/perc mode code. 0 is melodic, !0 is percussive. */ - if (drv) drv->SetMode((int)data[pos + 5]); + SetRhythmMode((int)data[pos + 5]); } else if (code == ADLIB_PITCH) { /* Sets the interval over which pitch bend changes will be applied. */ - if (drv) drv->SetPitchRange((int)data[pos + 5]); + SetPitchRange((int)data[pos + 5]); } } } @@ -220,19 +220,19 @@ void CmdiPlayer::executeCommand() { case NOTE_OFF: pos += 2; - if (voice > MAX_VOICES - 1) + if (voice >= MAX_VOICES) break; - if (drv) drv->NoteOff(voice); + NoteOff(voice); break; case NOTE_ON: note = data[pos++]; vol = data[pos++]; - if (voice > MAX_VOICES - 1) + if (voice >= MAX_VOICES) break; if (!vol) { /* A note-on with a volume of 0 is equivalent to a note-off. */ - if (drv) drv->NoteOff(voice); + NoteOff(voice); volume[voice] = vol; } else @@ -240,20 +240,20 @@ void CmdiPlayer::executeCommand() /* Regular note-on */ if (vol != volume[voice]) { - if (drv) drv->SetVoiceVolume(voice, vol); + SetVolume(voice, vol); volume[voice] = vol; } - if (drv) drv->NoteOn(voice, note); + NoteOn(voice, note); } break; case AFTER_TOUCH: pos++; // skip note vol = data[pos++]; - if (voice > MAX_VOICES - 1) + if (voice >= MAX_VOICES) break; if (vol != volume[voice]) { - if (drv) drv->SetVoiceVolume(voice, vol); + SetVolume(voice, vol); volume[voice] = vol; } break; @@ -267,20 +267,20 @@ void CmdiPlayer::executeCommand() break; case CHANNEL_PRESSURE: vol = data[pos++]; - if (voice > MAX_VOICES - 1) + if (voice >= MAX_VOICES) break; if (vol != volume[voice]) { - if (drv) drv->SetVoiceVolume(voice, vol); + SetVolume(voice, vol); volume[voice] = vol; } break; case PITCH_BEND: pitch = data[pos++]; pitch |= data[pos++] << 7; - if (voice > MAX_VOICES - 1) + if (voice >= MAX_VOICES) break; - if (drv) drv->SetVoicePitch(voice, pitch); + ChangePitch(voice, pitch); break; default: /* diff --git a/plugins/adplug/adplug/mdi.h b/plugins/adplug/adplug/mdi.h index 13ead4c35c..d8b1ac3298 100644 --- a/plugins/adplug/adplug/mdi.h +++ b/plugins/adplug/adplug/mdi.h @@ -28,8 +28,7 @@ #ifndef H_ADPLUG_MDIPLAYER #define H_ADPLUG_MDIPLAYER -#include "player.h" -#include "adlib.h" +#include "composer.h" #define MIDI_CHUNK_SIZE 4 /* FOURCC size */ #define MIDI_HEAD_SIZE 6 /* MThd data size */ @@ -62,23 +61,22 @@ #define ADLIB_RHYTHM 2 #define ADLIB_PITCH 3 -class CmdiPlayer: public CPlayer +class CmdiPlayer: public CcomposerBackend { public: static CPlayer *factory(Copl *newopl); CmdiPlayer(Copl *newopl) - : CPlayer(newopl), drv(0), data(0) + : CcomposerBackend(newopl), data(0) { } ~CmdiPlayer() { if(data) delete [] data; - if (drv) drv->~CadlibDriver(); }; bool load(const std::string &filename, const CFileProvider &fp); bool update(); - void rewind(int subsong); + void frontend_rewind(int subsong); float getrefresh() { @@ -87,14 +85,13 @@ class CmdiPlayer: public CPlayer std::string gettype() { - return std::string("AdLib MIDIPlay File"); + return std::string("AdLib Visual Composer: MIDIPlay File"); } private: void SetTempo(uint32_t tempo); uint32_t GetVarVal(); void executeCommand(); - CadlibDriver *drv; protected: unsigned long pos, size; diff --git a/plugins/adplug/adplug/mid.cpp b/plugins/adplug/adplug/mid.cpp index bf3d22340e..6bef04b198 100644 --- a/plugins/adplug/adplug/mid.cpp +++ b/plugins/adplug/adplug/mid.cpp @@ -35,14 +35,14 @@ * xxxPATCH.003 (patch.003 must be saved from the * sierra resource from each game.) * - * 6/2/2000: v1.0 relased by phil hassey + * 6/2/2000: v1.0 released by phil hassey * Status: LAA is almost perfect - * - some volumes are a bit off (intrument too quiet) + * - some volumes are a bit off (instrument too quiet) * MID is fine (who wants to listen to MIDI vid adlib anyway) - * CMF is okay (still needs the adlib rythm mode implemented + * CMF is okay (still needs the adlib rhythm mode implemented * for real) * 6/6/2000: - * Status: SCI: there are two SCI formats, orginal and advanced. + * Status: SCI: there are two SCI formats, original and advanced. * original: (Found in SCI/EGA Sierra Adventures) * played almost perfectly, I believe * there is one mistake in the instrument @@ -75,12 +75,14 @@ * SD - the SCI Decoder (to get all .sci out of the Sierra files) */ +#include "strnlen.h" #include #include #include #include #include "mid.h" #include "mididata.h" +#include "load_helper.h" /*#define TESTING*/ #ifdef TESTING @@ -91,14 +93,7 @@ void CmidPlayer::midiprintf(const char *format, ...) } #endif -#if !defined(UINT8_MAX) -typedef signed char int8_t; -typedef short int16_t; -typedef int int32_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -#endif +#include // for uintxx_t #define LUCAS_STYLE 1 #define CMF_STYLE 2 @@ -107,7 +102,7 @@ typedef unsigned int uint32_t; // AdLib melodic and rhythm mode defines #define ADLIB_MELODIC 0 -#define ADLIB_RYTHM 1 +#define ADLIB_RHYTHM 1 // File types #define FILE_LUCAS 1 @@ -141,12 +136,22 @@ CmidPlayer::CmidPlayer(Copl *newopl) { } -unsigned char CmidPlayer::datalook(long pos) +unsigned char CmidPlayer::datalook(unsigned long pos) { - if (pos<0 || pos >= flen) return(0); + if (pos >= flen) return(0); return(data[pos]); } +void CmidPlayer::readString(char *dst, unsigned long num) +{ + unsigned long i; + + for (i=0; i= 0; i--) @@ -202,7 +204,9 @@ bool CmidPlayer::load_sierra_ins(const std::string &fname, const CFileProvider & j = i+1; break; } - sprintf(pfilename+j+3,"patch.003"); + for (i = 0; i < 3; i++) + if (pfilename[j]) j++; + snprintf(pfilename+j,fl-j,"patch.003"); f = fp.open(pfilename); free(pfilename); @@ -300,7 +304,7 @@ bool CmidPlayer::load(const std::string &filename, const CFileProvider &fp) uint32_t size; f->readString((char *)s, 6); - size = *(uint32_t *)s; // size of FILE_OLDLUCAS + size = u32_unaligned(s); // size of FILE_OLDLUCAS good=0; subsongs=0; switch(s[0]) @@ -309,7 +313,24 @@ bool CmidPlayer::load(const std::string &filename, const CFileProvider &fp) if (s[1]=='D' && s[2]=='L') good=FILE_LUCAS; break; case 'M': - if (s[1]=='T' && s[2]=='h' && s[3]=='d') good=FILE_MIDI; + if (s[1]=='T' && s[2]=='h' && s[3]=='d') + { + /* "MThd" header-chunk + * 0x00 0x00 0x00 0x06 header size + * 0x00 0x0n midi file type: 0=single-track format, 1=multiple-track format, 2=multiple-song format + * 0xnn 0xnn number of tracks + * 0xnn 0xnn tempo + */ + f->seek(-2, binio::Add); // we have already read 6 bytes from the start + f->setFlag(binio::BigEndian, true); + if (f->readInt(4) != 6) + break; + midi_type=f->readInt(2); + if (midi_type > 2 || f->readInt(2) < 1) + break; + good=FILE_MIDI; + midiprintf ("General MIDI type: %d\n", midi_type); + } break; case 'C': if (s[1]=='T' && s[2]=='M' && s[3]=='F') good=FILE_CMF; @@ -433,9 +454,7 @@ void CmidPlayer::midi_fm_volume(int voice, int volume) void CmidPlayer::midi_fm_playnote(int voice, int note, int volume) { - if (note < 0) { - note = 0; - } + if (note < 0) return; // prevent invalid access int freq=fnums[note%12]; int oct=note/12; int c; @@ -525,7 +544,7 @@ bool CmidPlayer::update() // doing=0; note=getnext(1); vel=getnext(1); - if(adlib_mode == ADLIB_RYTHM) + if(adlib_mode == ADLIB_RHYTHM) numchan = 6; else numchan = 9; @@ -580,7 +599,7 @@ bool CmidPlayer::update() chp[on][1]=note; chp[on][2]=0; - if(adlib_mode == ADLIB_RYTHM && c >= 11) { + if(adlib_mode == ADLIB_RHYTHM && c >= 11) { // Still need to turn off the perc instrument before playing it again, // as not all songs send a noteoff. midi_write_adlib(0xbd, adlib_data[0xbd] & ~(0x10 >> (c - 11))); @@ -590,7 +609,7 @@ bool CmidPlayer::update() } else { if (vel==0) { //same code as end note - if (adlib_mode == ADLIB_RYTHM && c >= 11) { + if (adlib_mode == ADLIB_RHYTHM && c >= 11) { // Turn off the percussion instrument midi_write_adlib(0xbd, adlib_data[0xbd] & ~(0x10 >> (c - 11))); //midi_fm_endnote(percussion_map[c]); @@ -655,7 +674,7 @@ midi_fm_playnote(i,note+cnote[c],my_midi_fm_vol_table[(cvols[c]*vel)/128]*2); midiprintf("Rhythm mode: %ld\n", vel); if ((adlib_style&CMF_STYLE)!=0) { adlib_mode=vel; - if(adlib_mode == ADLIB_RYTHM) + if(adlib_mode == ADLIB_RHYTHM) midi_write_adlib(0xbd, adlib_data[0xbd] | (1 << 5)); else midi_write_adlib(0xbd, adlib_data[0xbd] & ~(1 << 5)); @@ -665,11 +684,11 @@ midi_fm_playnote(i,note+cnote[c],my_midi_fm_vol_table[(cvols[c]*vel)/128]*2); break; case 0xc0: /*patch change*/ x=getnext(1); - ch[c].inum=x; + ch[c].inum = x & 0x7f; for (j=0; j<11; j++) ch[c].ins[j]=myinsbank[ch[c].inum][j]; break; - case 0xd0: /*chanel touch*/ + case 0xd0: /*channel touch*/ x=getnext(1); break; case 0xe0: /*pitch wheel*/ @@ -700,24 +719,34 @@ midi_fm_playnote(i,note+cnote[c],my_midi_fm_vol_table[(cvols[c]*vel)/128]*2); midiprintf ("\n"); getnext(1); getnext(1); - c=getnext(1); + c = getnext(1) & 0x0f; getnext(1); // getnext(22); //temp - ch[c].ins[0]=(unsigned char)((getnext(1)<<4)+getnext(1)); - ch[c].ins[2]=(unsigned char)(0xff-(((getnext(1)<<4)+getnext(1))&0x3f)); - ch[c].ins[4]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1))); - ch[c].ins[6]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1))); - ch[c].ins[8]=(unsigned char)((getnext(1)<<4)+getnext(1)); - - ch[c].ins[1]=(unsigned char)((getnext(1)<<4)+getnext(1)); - ch[c].ins[3]=(unsigned char)(0xff-(((getnext(1)<<4)+getnext(1))&0x3f)); - ch[c].ins[5]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1))); - ch[c].ins[7]=(unsigned char)(0xff-((getnext(1)<<4)+getnext(1))); - ch[c].ins[9]=(unsigned char)((getnext(1)<<4)+getnext(1)); - - i=(getnext(1)<<4)+getnext(1); - ch[c].ins[10]=i; + unsigned char tmp; + tmp = getnext(1) << 4; + ch[c].ins[0] = tmp + getnext(1); + tmp = getnext(1) << 4; + ch[c].ins[2] = 0xff - ((tmp + getnext(1)) & 0x3f); + tmp = getnext(1) << 4; + ch[c].ins[4] = 0xff - (tmp + getnext(1)); + tmp = getnext(1) << 4; + ch[c].ins[6] = 0xff-(tmp + getnext(1)); + tmp = getnext(1) << 4; + ch[c].ins[8] = tmp + getnext(1); + tmp = getnext(1) << 4; + ch[c].ins[1] = tmp + getnext(1); + tmp = getnext(1) << 4; + ch[c].ins[3] = 0xff - ((tmp + getnext(1)) & 0x3f); + tmp = getnext(1) << 4; + ch[c].ins[5] = 0xff - (tmp + getnext(1)); + tmp = getnext(1) << 4; + ch[c].ins[7] = 0xff - (tmp + getnext(1)); + tmp = getnext(1) << 4; + ch[c].ins[9] = tmp + getnext(1); + + i = getnext(1) << 4; + ch[c].ins[10] = (i += getnext(1)); //if ((i&1)==1) ch[c].ins[10]=1; @@ -822,7 +851,7 @@ midi_fm_playnote(i,note+cnote[c],my_midi_fm_vol_table[(cvols[c]*vel)/128]*2); if (ret==1) { - iwait=0xffffff; // bigger than any wait can be! + iwait = ~0UL; // bigger than any wait can be! for (curtrack=0; curtrack<16; curtrack++) if (track[curtrack].on == 1 && track[curtrack].pos < track[curtrack].tend && @@ -887,7 +916,7 @@ void CmidPlayer::rewind(int subsong) for (i=0; i<128; i++) for (j=0; j<14; j++) myinsbank[i][j]=midi_fm_instruments[i][j]; - for (i=0; i<16; i++) + for (i=0; i<16; i++) { ch[i].inum=0; for (j=0; j<11; j++) @@ -935,16 +964,43 @@ void CmidPlayer::rewind(int subsong) case FILE_MIDI: if (type != FILE_LUCAS) tins=128; - getnext(11); /*skip header*/ + getnext(11); /* skip past header data until deltas is reached */ deltas=getnext(2); midiprintf ("deltas:%ld\n",deltas); - getnext(4); + + /* MIDI files should be played back one octave and a semi-note off in to be played back correctly */ + if (type != FILE_LUCAS) + { + for (i=0; i<16; i++) + { + ch[i].nshift=-13; + } + } curtrack=0; - track[curtrack].on=1; - track[curtrack].tend=getnext(4); - track[curtrack].spos=pos; - midiprintf ("tracklen:%lu\n",track[curtrack].tend); + while ((curtrack == 0) || + ((midi_type == 1) && (curtrack < 16))) + { + /* MIDI type 0 (and LucasArts AdLib MIDI) stores all the MIDI channels in a single track, + * while MIDI type 1 splits each channel into separate tracks */ + int len; + char s[5]; + readString(s, 4); + s[4] = 0; + midiprintf ("Offset=0x%08lx\n", pos); + midiprintf ("trkHeader: '%s'\n", s); + if (strcmp(s, "MTrk")) + break; + track[curtrack].on=1; + len=getnext(4); + midiprintf ("tracklen:%lu\n",len); + track[curtrack].tend = pos + len; + if (track[curtrack].tend > flen) // no music after end of file + track[curtrack].tend = flen; + track[curtrack].spos=pos; + pos+=len; + curtrack++; + } break; case FILE_CMF: getnext(3); // ctmf @@ -952,15 +1008,21 @@ void CmidPlayer::rewind(int subsong) n=getnexti(2); // instrument offset m=getnexti(2); // music offset deltas=getnexti(2); //ticks/qtr note - msqtr=1000000/getnexti(2)*deltas; - //the stuff in the cmf is click ticks per second.. + i = getnexti(2); // stuff in cmf is click ticks per second.. + if (i) msqtr = 1000000L / i * deltas; i=getnexti(2); - if(i) title = (char *)data+i; + if (i > 0 && (unsigned long)i < flen && + strnlen((char *)data + i, flen - i) < flen - i) + title = (char *)data + i; i=getnexti(2); - if(i) author = (char *)data+i; + if (i > 0 && (unsigned long)i < flen && + strnlen((char *)data + i, flen - i) < flen - i) + author = (char *)data + i; i=getnexti(2); - if(i) remarks = (char *)data+i; + if (i > 0 && (unsigned long)i < flen && + strnlen((char *)data + i, flen - i) < flen - i) + remarks = (char *)data + i; getnext(16); // channel in use table .. i=getnexti(2); // num instr @@ -1079,7 +1141,7 @@ void CmidPlayer::rewind(int subsong) { ch[i].nshift=-13; ch[i].on=getnext(1); - ch[i].inum=getnext(1); + ch[i].inum = getnext(1) & 0x7f; for (j=0; j<11; j++) ch[i].ins[j]=myinsbank[ch[i].inum][j]; } @@ -1111,7 +1173,8 @@ std::string CmidPlayer::gettype() case FILE_LUCAS: return std::string("LucasArts AdLib MIDI"); case FILE_MIDI: - return std::string("General MIDI"); + // avoid using std::to_string(midi_type) which is c++11 and may be broken in mingw32 + return std::string("General MIDI (type " + std::string(1, '0' + midi_type) + ")"); case FILE_CMF: return std::string("Creative Music Format (CMF MIDI)"); case FILE_OLDLUCAS: diff --git a/plugins/adplug/adplug/mid.h b/plugins/adplug/adplug/mid.h index 5bfda13e8f..d170cbc0ef 100644 --- a/plugins/adplug/adplug/mid.h +++ b/plugins/adplug/adplug/mid.h @@ -69,7 +69,7 @@ class CmidPlayer: public CPlayer }; char *author,*title,*remarks,emptystr; - long flen; + unsigned long flen; unsigned long pos; unsigned long sierra_pos; //sierras gotta be special.. :> int subsongs; @@ -79,6 +79,7 @@ class CmidPlayer: public CPlayer int adlib_style; int adlib_mode; unsigned char myinsbank[128][16], smyinsbank[128][16]; + int midi_type; midi_channel ch[16]; int chp[18][3]; @@ -97,9 +98,10 @@ class CmidPlayer: public CPlayer private: bool load_sierra_ins(const std::string &fname, const CFileProvider &fp); void midiprintf(const char *format, ...); - unsigned char datalook(long pos); + unsigned char datalook(unsigned long pos); unsigned long getnexti(unsigned long num); unsigned long getnext(unsigned long num); + void readString(char *dst, unsigned long num); unsigned long getval(); void sierra_next_section(); void midi_write_adlib(unsigned int r, unsigned char v); diff --git a/plugins/adplug/adplug/mididata.h b/plugins/adplug/adplug/mididata.h index 7817c351fc..0eb82402bd 100644 --- a/plugins/adplug/adplug/mididata.h +++ b/plugins/adplug/adplug/mididata.h @@ -22,7 +22,7 @@ * for further acknowledgements. */ -unsigned char midi_fm_instruments[128][14] = +static const unsigned char midi_fm_instruments[128][14] = { /* This set of GM instrument patches was provided by Jorrit Rouwe... @@ -51,7 +51,7 @@ unsigned char midi_fm_instruments[128][14] = { 0x61, 0xb1, 0x13, 0x89, 0x97, 0x55, 0x04, 0x04, 0x01, 0x00, 0x00, 0, 0, 0 }, /* Reed Organ */ { 0x24, 0xb1, 0x48, 0x09, 0x98, 0x46, 0x2a, 0x1a, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Accoridan */ { 0x61, 0x21, 0x13, 0x09, 0x91, 0x61, 0x06, 0x07, 0x01, 0x00, 0x0a, 0, 0, 0 }, /* Harmonica */ - { 0x21, 0xa1, 0x13, 0x92, 0x71, 0x61, 0x06, 0x07, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Tango Accordian */ + { 0x21, 0xa1, 0x13, 0x92, 0x71, 0x61, 0x06, 0x07, 0x00, 0x00, 0x06, 0, 0, 0 }, /* Tango Accordion */ { 0x02, 0x41, 0x9c, 0x89, 0xf3, 0xf3, 0x94, 0xc8, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Acoustic Guitar(nylon) */ { 0x03, 0x11, 0x54, 0x09, 0xf3, 0xf1, 0x9a, 0xe7, 0x01, 0x00, 0x0c, 0, 0, 0 }, /* Acoustic Guitar(steel) */ { 0x23, 0x21, 0x5f, 0x09, 0xf1, 0xf2, 0x3a, 0xf8, 0x00, 0x00, 0x00, 0, 0, 0 }, /* Electric Guitar(jazz) */ @@ -160,7 +160,7 @@ unsigned char midi_fm_instruments[128][14] = }; /* logarithmic relationship between midi and FM volumes */ -static int my_midi_fm_vol_table[128] = { +static const int my_midi_fm_vol_table[128] = { 0, 11, 16, 19, 22, 25, 27, 29, 32, 33, 35, 37, 39, 40, 42, 43, 45, 46, 48, 49, 50, 51, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 64, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 75, 76, 77, diff --git a/plugins/adplug/adplug/mkj.cpp b/plugins/adplug/adplug/mkj.cpp index 0e7955213c..05e9f3672b 100644 --- a/plugins/adplug/adplug/mkj.cpp +++ b/plugins/adplug/adplug/mkj.cpp @@ -36,7 +36,6 @@ bool CmkjPlayer::load(const std::string &filename, const CFileProvider &fp) char id[6]; float ver; int i, j; - short inst[8]; // file validation f->readString(id, 6); @@ -46,24 +45,35 @@ bool CmkjPlayer::load(const std::string &filename, const CFileProvider &fp) // load maxchannel = f->readInt(2); - opl->init(); opl->write(1, 32); + if (maxchannel > 9 || maxchannel < 0) { + fp.close(f); + return false; + } + // load and store the channel instruments. for(i = 0; i < maxchannel; i++) { - for(j = 0; j < 8; j++) inst[j] = f->readInt(2); - opl->write(0x20+op_table[i],inst[4]); - opl->write(0x23+op_table[i],inst[0]); - opl->write(0x40+op_table[i],inst[5]); - opl->write(0x43+op_table[i],inst[1]); - opl->write(0x60+op_table[i],inst[6]); - opl->write(0x63+op_table[i],inst[2]); - opl->write(0x80+op_table[i],inst[7]); - opl->write(0x83+op_table[i],inst[3]); + for (j = 0; j < 8; j++) { + inst[i].value[j] = f->readInt(2); + } } maxnotes = f->readInt(2); + if (maxnotes < 1 || + // Larger values would need code changes to avoid integer overflows: + maxnotes > 0x7fff / (maxchannel + 1) || + // That's what gets actually accessed in update(): [yeah, it's weird] + maxnotes - 1 + 3 * maxchannel > (maxchannel + 1) * maxnotes) { + fp.close(f); + return false; + } + delete[] songbuf; songbuf = new short [(maxchannel+1)*maxnotes]; for(i = 0; i < maxchannel; i++) channel[i].defined = f->readInt(2); for(i = 0; i < (maxchannel + 1) * maxnotes; i++) songbuf[i] = f->readInt(2); + if (f->error()) { + fp.close(f); + return false; + } AdPlug_LogWrite("CmkjPlayer::load(\"%s\"): loaded file ver %.2f, %d channels," " %d notes/channel.\n", filename.c_str(), ver, maxchannel, maxnotes); @@ -109,18 +119,26 @@ bool CmkjPlayer::update() case 15: opl->write(0xa0 + c,0x63); opl->write(0xb0 + c,0x22 + 4 * channel[c].octave); break; case 255: // delay channel[c].songptr += maxchannel; + if (songbuf[channel[c].songptr] < 0) + goto bad_data; // avoid integer overflow channel[c].pstat = songbuf[channel[c].songptr]; break; case 254: // set octave channel[c].songptr += maxchannel; + if ((songbuf[channel[c].songptr] | 7) != 7) + goto bad_data; // value out of range channel[c].octave = songbuf[channel[c].songptr]; break; case 253: // set speed channel[c].songptr += maxchannel; + if (songbuf[channel[c].songptr] < 0) + goto bad_data; // avoid integer overflow channel[c].speed = songbuf[channel[c].songptr]; break; case 252: // set waveform channel[c].songptr += maxchannel; + if (((songbuf[channel[c].songptr] - 300) | 0xff) != 0xff) + goto bad_data; // value out of range channel[c].waveform = songbuf[channel[c].songptr] - 300; if(c > 2) opl->write(0xe0 + c + (c+6),channel[c].waveform); @@ -128,6 +146,7 @@ bool CmkjPlayer::update() opl->write(0xe0 + c,channel[c].waveform); break; case 251: // song end + bad_data: for(i = 0; i < maxchannel; i++) channel[i].songptr = i; songend = true; return false; @@ -147,12 +166,22 @@ void CmkjPlayer::rewind(int subsong) { int i; + opl->init(); opl->write(1, 32); for(i = 0; i < maxchannel; i++) { channel[i].pstat = 0; channel[i].speed = 0; channel[i].waveform = 0; channel[i].songptr = i; channel[i].octave = 4; + // Set up channel instruments + opl->write(0x20+op_table[i],inst[i].value[4]); + opl->write(0x23+op_table[i],inst[i].value[0]); + opl->write(0x40+op_table[i],inst[i].value[5]); + opl->write(0x43+op_table[i],inst[i].value[1]); + opl->write(0x60+op_table[i],inst[i].value[6]); + opl->write(0x63+op_table[i],inst[i].value[2]); + opl->write(0x80+op_table[i],inst[i].value[7]); + opl->write(0x83+op_table[i],inst[i].value[3]); } songend = false; diff --git a/plugins/adplug/adplug/mkj.h b/plugins/adplug/adplug/mkj.h index 390df9a843..5794d95d77 100644 --- a/plugins/adplug/adplug/mkj.h +++ b/plugins/adplug/adplug/mkj.h @@ -47,4 +47,8 @@ class CmkjPlayer: public CPlayer struct { short defined,songptr,octave,waveform,pstat,speed,delay; } channel[9]; + struct { + short value[8]; + } inst[9]; + }; diff --git a/plugins/adplug/adplug/msc.cpp b/plugins/adplug/adplug/msc.cpp index 6635ee17ed..ab8440bc93 100644 --- a/plugins/adplug/adplug/msc.cpp +++ b/plugins/adplug/adplug/msc.cpp @@ -176,7 +176,7 @@ std::string CmscPlayer::gettype() { char vstr [40]; - sprintf(vstr, "AdLib MSCplay (version %d)", version); + snprintf(vstr, sizeof (vstr), "AdLib MSCplay (version %d)", version); return std::string (vstr); } diff --git a/plugins/adplug/adplug/msc.h b/plugins/adplug/adplug/msc.h index 94b8971b50..878aa2ead1 100644 --- a/plugins/adplug/adplug/msc.h +++ b/plugins/adplug/adplug/msc.h @@ -58,7 +58,7 @@ class CmscPlayer: public CPlayer }; // file data - char * desc; // song desctiption + char * desc; // song description unsigned short version; // file version unsigned short nr_blocks; // number of music blocks unsigned short block_len; // maximal block length diff --git a/plugins/adplug/adplug/mtk.cpp b/plugins/adplug/adplug/mtk.cpp index a418b3f486..1a63a72a49 100644 --- a/plugins/adplug/adplug/mtk.cpp +++ b/plugins/adplug/adplug/mtk.cpp @@ -31,111 +31,116 @@ CPlayer *CmtkLoader::factory(Copl *newopl) bool CmtkLoader::load(const std::string &filename, const CFileProvider &fp) { - binistream *f = fp.open(filename); if(!f) return false; + binistream *f = fp.open(filename); + if (!f) return false; + struct { char id[18]; - unsigned short crc,size; + unsigned short crc, size; } header; struct mtkdata { - char songname[34],composername[34],instname[0x80][34]; - unsigned char insts[0x80][12],order[0x80],dummy,patterns[0x32][0x40][9]; + struct { char dummy, str[33]; } songname, composername, instname[0x80]; + unsigned char insts[0x80][12], order[0x80], dummy; + // followed by pattern data: + // hscnote patterns[50][64*9]; } *data; - unsigned char *cmp,*org; - unsigned int i; - unsigned long cmpsize,cmpptr=0,orgptr=0; - unsigned short ctrlbits=0,ctrlmask=0,cmd,cnt,offs; + unsigned int i, cnt; // read header - f->readString(header.id, 18); + f->readString(header.id, sizeof(header.id)); header.crc = f->readInt(2); header.size = f->readInt(2); // file validation section - if(strncmp(header.id,"mpu401tr\x92kk\xeer@data",18)) - { fp.close(f); return false; } - - // load section - cmpsize = fp.filesize(f) - 22; - cmp = new unsigned char[cmpsize]; - org = new unsigned char[header.size]; - for(i = 0; i < cmpsize; i++) cmp[i] = f->readInt(1); - fp.close(f); + if (memcmp(header.id, "mpu401tr\x92kk\xeer@data", sizeof(header.id)) || + header.size < sizeof(*data)) { + fp.close(f); return false; + } + + // load & decompress section + unsigned short ctrlbits = 0, ctrlmask = 0; + unsigned char *org = new unsigned char[header.size]; + for (size_t orgptr = 0; orgptr < header.size; orgptr += cnt) { + if (f->error()) goto err; - while(cmpptr < cmpsize) { // decompress ctrlmask >>= 1; - if(!ctrlmask) { - ctrlbits = cmp[cmpptr] + (cmp[cmpptr + 1] << 8); - cmpptr += 2; + if (!ctrlmask) { + ctrlbits = f->readInt(2); ctrlmask = 0x8000; } - if(!(ctrlbits & ctrlmask)) { // uncompressed data - if(orgptr >= header.size) - goto err; - org[orgptr] = cmp[cmpptr]; - orgptr++; cmpptr++; + if (!(ctrlbits & ctrlmask)) { // uncompressed data + org[orgptr] = f->readInt(1); + cnt = 1; continue; } // compressed data - cmd = (cmp[cmpptr] >> 4) & 0x0f; - cnt = cmp[cmpptr] & 0x0f; - cmpptr++; - switch(cmd) { - case 0: - if(orgptr + cnt > header.size) goto err; - cnt += 3; - memset(&org[orgptr],cmp[cmpptr],cnt); - cmpptr++; orgptr += cnt; + unsigned offs; + unsigned char cmd = f->readInt(1); + cnt = (cmd & 0x0f) + 3; + + switch (cmd >> 4) { + case 0: // repeat a byte 3..18 times + repeat_byte: + if (orgptr + cnt > header.size) goto err; + memset(&org[orgptr], f->readInt(1), cnt); break; - case 1: - if(orgptr + cnt > header.size) goto err; - cnt += (cmp[cmpptr] << 4) + 19; - memset(&org[orgptr],cmp[++cmpptr],cnt); - cmpptr++; orgptr += cnt; + case 1: // repeat a byte 19..4114 times + cnt += (f->readInt(1) << 4) + 16; + goto repeat_byte; + + case 2: // copy range (16..271 bytes) + offs = cnt + (f->readInt(1) << 4); + cnt = f->readInt(1) + 16; + copy_range: + if (orgptr + cnt > header.size || offs > orgptr) goto err; + // may overlap, can't use memcpy() + for (i = 0; i < cnt; i++) + org[orgptr + i] = org[orgptr - offs + i]; break; - case 2: - if(orgptr + cnt > header.size) goto err; - offs = (cnt+3) + (cmp[cmpptr] << 4); - cnt = cmp[++cmpptr] + 16; cmpptr++; - memcpy(&org[orgptr],&org[orgptr - offs],cnt); - orgptr += cnt; - break; - - default: - if(orgptr + cmd > header.size) goto err; - offs = (cnt+3) + (cmp[cmpptr++] << 4); - memcpy(&org[orgptr],&org[orgptr-offs],cmd); - orgptr += cmd; - break; + default: // copy range (3..15 bytes) + offs = cnt + (f->readInt(1) << 4); + cnt = cmd >> 4; + goto copy_range; } } - delete [] cmp; - data = (struct mtkdata *) org; + if (f->error() || !f->ateof()) goto err; + fp.close(f); // convert to HSC replay data - memset(title,0,34); strncpy(title,data->songname+1,33); - memset(composer,0,34); strncpy(composer,data->composername+1,33); - memset(instname,0,0x80*34); - for(i=0;i<0x80;i++) - strncpy(instname[i],data->instname[i]+1,33); - memcpy(instr,data->insts,0x80 * 12); - memcpy(song,data->order,0x80); - memcpy(patterns,data->patterns,header.size-6084); - for (i=0;i<128;i++) { // correct instruments + data = (struct mtkdata *) org; + strncpy(title, data->songname.str, sizeof(title) - 1); + title[sizeof(title) - 1] = 0; + strncpy(composer, data->composername.str, sizeof(composer) - 1); + composer[sizeof(composer) - 1] = 0; + + for (i = 0; i < 0x80; i++) { + strncpy(instname[i], data->instname[i].str, sizeof(instname[i]) - 1); + instname[i][sizeof(instname[i]) - 1] = 0; + } + + memcpy(instr, data->insts, sizeof(instr)); + for (i = 0; i < 0x80; i++) { // correct instruments instr[i][2] ^= (instr[i][2] & 0x40) << 1; instr[i][3] ^= (instr[i][3] & 0x40) << 1; instr[i][11] >>= 4; // make unsigned } + memcpy(song, data->order, sizeof(song)); + + cnt = header.size - sizeof(*data); // was off by 1 + if (cnt > sizeof(patterns)) cnt = sizeof(patterns); // fail? + memcpy(patterns, org + sizeof(*data), cnt); + delete [] org; rewind(0); return true; err: - delete [] cmp; + fp.close(f); delete [] org; return false; } diff --git a/plugins/adplug/adplug/mtk.h b/plugins/adplug/adplug/mtk.h index 986b64c2ae..0e07e3d104 100644 --- a/plugins/adplug/adplug/mtk.h +++ b/plugins/adplug/adplug/mtk.h @@ -43,7 +43,7 @@ class CmtkLoader: public ChscPlayer unsigned int getinstruments() { return 128; }; std::string getinstrument(unsigned int n) - { return std::string(instname[n]); }; + { return n < 128 ? std::string(instname[n]) : std::string(); }; private: char title[34],composer[34],instname[0x80][34]; diff --git a/plugins/adplug/adplug/mtr.cpp b/plugins/adplug/adplug/mtr.cpp new file mode 100644 index 0000000000..439df0a1ec --- /dev/null +++ b/plugins/adplug/adplug/mtr.cpp @@ -0,0 +1,226 @@ +/* + Adplug - Replayer for many OPL2/OPL3 audio file formats. + Copyright (C) 1999 - 2003 Simon Peter, , et al. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + mtr.cpp - MTR loader by Dmitry Smagin + + AKMTRK2.DOC with format description is wrong in various ways. + This loader interpretes v1 and v2 correctly. +*/ + +#include +#include +#include "mtr.h" +#include "debug.h" + +/* -------- Public Methods -------------------------------- */ + +CPlayer *CmtrLoader::factory(Copl *newopl) { + return new CmtrLoader(newopl); +} + +bool CmtrLoader::load(const std::string &filename, const CFileProvider &fp) { + binistream *f = fp.open(filename); + if (!f) + return false; + + unsigned int i, j, k, t = 0, numread, nvoices; + ninstruments = 0; + + char header[51] = {0}; + char mtitle[21] = {0}; + int ndigvoices, npatterns, orderlen, + timervalue = 0x428F, restart, flength; + + f->readString(header, 50); + + if (!strncmp(header, "MTRAC ", 6)) { + version = 1; + numread = sscanf(header + 26, "%02x %02x %02x %02x %08x", + &nvoices, + &npatterns, + &orderlen, + &restart, + &flength + ); + + if (numread != 5) { + fp.close(f); + return false; + } + + strncpy(mtitle, header + 6, 20); + timervalue = f->readInt(2); // usually 428F + f->ignore(1); // 0=SPK 1=ADL 2=SBP + } else if (!strncmp(header, "MTRACK NC", 9)) { + version = 2; + numread = sscanf(header + 10, "%02x %02x %02x %02x %02x %02x %04x %08x", + &nvoices, + &ndigvoices, // unused + &npatterns, + &orderlen, + &ninstruments, + &restart, + &timervalue, // usually 428F + &flength + ); + + if (numread != 8) { + fp.close(f); + return false; + } + + f->readString(mtitle, 20); + } else { + fp.close(f); + return false; + } + + title = std::string(mtitle); + + // corrections for read data + nvoices++; + ninstruments = version == 2 ? ninstruments : 64; + + // data for Protracker + length = orderlen + 1; + nop = npatterns + 1; + timer = 1193180 / (timervalue ? timervalue : 0x428F); + + // init CmodPlayer + realloc_instruments(ninstruments); + realloc_order(length); + realloc_patterns(nop, 64, nvoices); + init_trackord(); + + // load order + for (i = 0; i < length; i++) + order[i] = f->readInt(1); + f->ignore(256 - length); + + const unsigned char table[12] = { + 4, 0, 6, 2, 8, 3, 9, 5, 11, 1, 7 + }; + + // read instruments + for (i = 0; i < ninstruments; i++) { + f->readString(instruments[i].name, 20); + instruments[i].name[20] = 0; + instruments[i].is_used = f->readInt(1); + f->readString(instruments[i].data, 12); + f->ignore(31); + + // convert + if (instruments[i].is_used == 2) { + for (j = 0; j < 11; j++) { + inst[i].data[j] = instruments[i].data[table[j]]; + } + } + } + + // load tracks + for (i = 0; i < nop; i++) + for (k = 0; k < 64; k++) { + for (j = 0; j < nvoices; j++) { + char event[4], note, inst, fx, val; + f->readString(event, 4); + note = event[0] ? ((event[0] & 0xf) + ((event[0] >> 4) * 12)) : 0; + inst = event[1] & 0x3f; + fx = event[2] & 0xf; + val = event[3]; + + t = i * nvoices + j; + tracks[t][k].note = note; + tracks[t][k].inst = inst; + + // translate effects + switch (fx) { + case 0: // 0xy, arp + // 1 and 2 never occur in any .mtr, so might sound wrong + case 1: // 1xy, slide up + case 2: // 2xy, slide down + tracks[t][k].command = fx; + tracks[t][k].param1 = val >> 4; + tracks[t][k].param2 = val & 0xf; + break; + case 3: // 3xy, fine slide up + case 4: // 4xy, fine slide down + tracks[t][k].command = fx == 3 ? 0x17 : 0x18; + tracks[t][k].param1 = val >> 4; + tracks[t][k].param2 = val & 0xf; + break; + case 5: // 5xy -> C(63-xy), set volume + tracks[t][k].command = 0xc; + tracks[t][k].param1 = (63 - val) >> 4; + tracks[t][k].param2 = (63 - val) & 0xf; + break; + case 0xB: // Bxy -> Fxy, set speed + tracks[t][k].command = 0xf; + tracks[t][k].param1 = val >> 4; + tracks[t][k].param2 = val & 0xf; + break; + case 0xF: + if (val == 1) { // F01 -> D00, pattern break + tracks[t][k].command = 0xd; + tracks[t][k].param1 = 0; + tracks[t][k].param2 = 0; + break; + } else if (val == 2) { // F02 -> note off + tracks[t][k].note = 0x7f; + tracks[t][k].inst = 0; + break; + } + default: + // Unsupported: + // Axy - retrigger + // Cxy - go to order position + // F00 - stop playing and restart + if (fx | val) + AdPlug_LogWrite("Unsupported effect: %02x-%02x\n", fx, val); + } + } + } + + fp.close(f); + + // data for Protracker + restartpos = restart; + initspeed = 6; + + rewind(0); + return true; +} + +float CmtrLoader::getrefresh() { + return (float)timer; +} + +std::string CmtrLoader::gettype() { + return std::string("Master Tracker (version " + std::string(1, '0' + version) + ")"); +} + +std::string CmtrLoader::getinstrument(unsigned int n) { + return std::string(instruments[n].name, 20); +} + +unsigned int CmtrLoader::getinstruments() { + return ninstruments; +} + +std::string CmtrLoader::gettitle() { + return title; +} diff --git a/plugins/adplug/adplug/mtr.h b/plugins/adplug/adplug/mtr.h new file mode 100644 index 0000000000..412ca5638d --- /dev/null +++ b/plugins/adplug/adplug/mtr.h @@ -0,0 +1,48 @@ +/* + Adplug - Replayer for many OPL2/OPL3 audio file formats. + Copyright (C) 1999 - 2003 Simon Peter, , et al. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + mtr.h - MTR loader by Dmitry Smagin +*/ + +#include "protrack.h" + +class CmtrLoader : public CmodPlayer { +public: + static CPlayer *factory(Copl *newopl); + + CmtrLoader(Copl *newopl) : CmodPlayer(newopl){}; + + bool load(const std::string &filename, const CFileProvider &fp); + float getrefresh(); + + std::string gettype(); + std::string getinstrument(unsigned int n); + unsigned int getinstruments(); + std::string gettitle(); + +private: + struct mtr_instrument { + char name[21]; + char is_used; // 2 - used, 0 unused + char data[12]; + } instruments[64]; + + unsigned char timer; + unsigned int version, ninstruments; + std::string title; +}; diff --git a/plugins/adplug/adplug/mus.cpp b/plugins/adplug/adplug/mus.cpp index dec8da6794..643b8114b4 100644 --- a/plugins/adplug/adplug/mus.cpp +++ b/plugins/adplug/adplug/mus.cpp @@ -30,6 +30,7 @@ */ #include +#include #include #include "mus.h" @@ -50,10 +51,10 @@ std::string CmusPlayer::gettype() char tmpstr[30]; if (isIMS) - sprintf(tmpstr, "IMPlay Song Format v%d.%d", majorVersion, minorVersion); + snprintf(tmpstr, sizeof(tmpstr), "IMPlay Song v%d.%d", majorVersion, minorVersion); else - sprintf(tmpstr, "AdLib MIDI Format v%d.%d", majorVersion, minorVersion); - return std::string(tmpstr); + snprintf(tmpstr, sizeof(tmpstr), "MIDI Format v%d.%d", majorVersion, minorVersion); + return std::string("AdLib Visual Composer: ") + std::string(tmpstr); } bool CmusPlayer::load(const std::string &filename, const CFileProvider &fp) @@ -61,8 +62,17 @@ bool CmusPlayer::load(const std::string &filename, const CFileProvider &fp) binistream *f = fp.open(filename); if(!f) return false; // file validation - if (!fp.extension(filename, ".mus") && - !fp.extension(filename, ".ims")) + static const char * musicExt[] = { KNOWN_MUS_EXT, NULL }; + size_t ex; + for (ex = 0; musicExt[ex] != NULL; ex++) + { + std::string ext = "."; + ext.append(musicExt[ex]); + + if (fp.extension(filename, ext)) + break; + } + if (musicExt[ex] == NULL) { fp.close(f); return false; @@ -116,15 +126,15 @@ bool CmusPlayer::load(const std::string &filename, const CFileProvider &fp) isIMS = true; nrTimbre = f->readInt(2); // validate post-data size - if (fp.filesize(f) >= (unsigned)(HEADER_LEN + dataSize) + 4 + nrTimbre * TIMBRE_NAME_SIZE) + if (fp.filesize(f) >= (unsigned)(HEADER_LEN + dataSize) + 4 + nrTimbre * INS_MAX_NAME_SIZE) { insts = new mus_inst[nrTimbre]; // read timbre names for (int i = 0; i < nrTimbre; i++) { - f->readString(insts[i].name, TIMBRE_NAME_SIZE); - insts[i].name[TIMBRE_NAME_SIZE - 1] = 0; - insts[i].loaded = false; + f->readString(insts[i].name, INS_MAX_NAME_SIZE); + insts[i].name[INS_MAX_NAME_SIZE - 1] = 0; + insts[i].backend_index = -1; } } else @@ -132,93 +142,94 @@ bool CmusPlayer::load(const std::string &filename, const CFileProvider &fp) } fp.close(f); - bool bankload; + bool bankload = false; if (!insts) { + static const char * timbreExt[] = { KNOWN_SND_EXT, NULL }; + static const char * timbreName[] = { KNOWN_SND_NAME, NULL }; + // load SND timbre bank - bankload = LoadTimbreBank(filename.substr(0, filename.length() - 3).append("snd"), fp); - #ifndef DOS - #ifndef WIN32 - if (!bankload) // for case-sensitive file systems - bankload = LoadTimbreBank(filename.substr(0, filename.length() - 3).append("SND"), fp); - #endif - #endif - if (!bankload) - bankload = LoadTimbreBank(filename.substr(0, filename.length() - 3).append("tim"), fp); - #ifndef DOS - #ifndef WIN32 - if (!bankload) // for case-sensitive file systems - bankload = LoadTimbreBank(filename.substr(0, filename.length() - 3).append("TIM"), fp); - #endif - #endif - if (!bankload) + for (size_t nm = 0; timbreName[nm] != NULL && !bankload; nm++) { - // fetch default bank - size_t np = filename.find_last_of("/"); - if (np == std::string::npos) - np = filename.find_last_of("\\"); - if (np != std::string::npos) - bankload = LoadTimbreBank(filename.substr(0, np + 1).append("timbres.snd"), fp); - #ifndef DOS - #ifndef WIN32 - if (!bankload) // for case-sensitive file systems - bankload = LoadTimbreBank(filename.substr(0, np + 1).append("TIMBRES.SND"), fp); - #endif - #endif - if (!bankload) - bankload = LoadTimbreBank(filename.substr(0, np + 1).append("timbres.tim"), fp); - #ifndef DOS - #ifndef WIN32 - if (!bankload) // for case-sensitive file systems - bankload = LoadTimbreBank(filename.substr(0, np + 1).append("TIMBRES.TIM"), fp); - #endif - #endif + for (size_t ex = 0; timbreExt[ex] != NULL && !bankload; ex++) + { + std::string fn; + std::string nam = timbreName[nm]; + std::string ext = timbreExt[ex]; + + size_t np = filename.find_last_of("/"); + if (np == std::string::npos) + np = filename.find_last_of("\\"); + if (np == std::string::npos) + np = -1; + + if (nam.empty()) + fn = filename.substr(0, filename.length() - 3).append(ext); + else + fn = filename.substr(0, np + 1).append(nam + "." + ext); + + bankload = LoadTimbreBank(fn, fp); + + #if !defined(DOS) && !defined(WIN32) + if (bankload) + break; + + // for case-sensitive file systems + std::transform(nam.begin(), nam.end(), nam.begin(), ::toupper); + std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper); + + if (nam.empty()) + fn = filename.substr(0, filename.length() - 3).append(ext); + else + fn = filename.substr(0, np + 1).append(nam + "." + ext); + + bankload = LoadTimbreBank(fn, fp); + #endif + } } } else if (isIMS) // fetch timbre data from BNK banks { + static const char * bankName[] = { KNOWN_BNK_NAME, NULL }; + bnk_return_failure = true; + // fetch bank of the song - bankload = FetchTimbreData(filename.substr(0, filename.length() - 3).append("bnk"), fp); - #ifndef DOS - #ifndef WIN32 - if (!bankload) // for case-sensitive file systems - bankload = FetchTimbreData(filename.substr(0, filename.length() - 3).append("BNK"), fp); - #endif - #endif - if (!bankload) + for (size_t nm = 0; bankName[nm] != NULL && !InstsLoaded(); nm++) { - // fetch other default banks + std::string fn; + std::string nam = bankName[nm]; + std::string ext = "bnk"; + size_t np = filename.find_last_of("/"); if (np == std::string::npos) np = filename.find_last_of("\\"); - if (np != std::string::npos) - { - if (!InstsLoaded()) - { - // fetch IMPlay bank - bankload = FetchTimbreData(filename.substr(0, np + 1).append("implay.bnk"), fp); - #ifndef DOS - #ifndef WIN32 - if (!bankload) // for case-sensitive file systems - bankload = FetchTimbreData(filename.substr(0, np + 1).append("IMPLAY.BNK"), fp); - #endif - #endif - } - if (!InstsLoaded()) - { - // fetch standard bank - bankload = FetchTimbreData(filename.substr(0, np + 1).append("standard.bnk"), fp); - #ifndef DOS - #ifndef WIN32 - if (!bankload) // for case-sensitive file systems - bankload = FetchTimbreData(filename.substr(0, np + 1).append("STANDARD.BNK"), fp); - #endif - #endif - } - } + if (np == std::string::npos) + np = -1; + + if (nam.empty()) + fn = filename.substr(0, filename.length() - 3).append(ext); + else + fn = filename.substr(0, np + 1).append(nam + "." + ext); + + bankload = FetchTimbreData(fn, fp); + + #if !defined(DOS) && !defined(WIN32) + if (bankload) + continue; + + // for case-sensitive file systems + std::transform(nam.begin(), nam.end(), nam.begin(), ::toupper); + std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper); + + if (nam.empty()) + fn = filename.substr(0, filename.length() - 3).append(ext); + else + fn = filename.substr(0, np + 1).append(nam + "." + ext); + + bankload = FetchTimbreData(fn, fp); + #endif } } - drv = new CadlibDriver(opl); rewind(0); return true; } @@ -227,7 +238,7 @@ bool CmusPlayer::InstsLoaded() { if (!insts) return false; for (int i = 0; i < nrTimbre; i++) - if (!insts[i].loaded) + if (insts[i].backend_index < 0) return false; return true; } @@ -257,8 +268,8 @@ bool CmusPlayer::LoadTimbreBank(const std::string fname, const CFileProvider &fp uint16_t offsetDef = static_cast(f->readInt(2)); // validate header and data size if (vMaj != 1 || vMin != 0 || - offsetDef != SND_HEADER_LEN + nrTimbre * TIMBRE_NAME_SIZE || - fp.filesize(f) < SND_HEADER_LEN + nrTimbre * TIMBRE_NAME_SIZE + nrTimbre * TIMBRE_DEF_SIZE) + offsetDef != SND_HEADER_LEN + nrTimbre * INS_MAX_NAME_SIZE || + fp.filesize(f) < SND_HEADER_LEN + nrTimbre * INS_MAX_NAME_SIZE + nrTimbre * TIMBRE_DEF_SIZE) { nrTimbre = 0; fp.close(f); @@ -271,14 +282,19 @@ bool CmusPlayer::LoadTimbreBank(const std::string fname, const CFileProvider &fp // read timbre names for (int i = 0; i < nrTimbre; i++) { - f->readString(insts[i].name, TIMBRE_NAME_SIZE); - insts[i].name[TIMBRE_NAME_SIZE - 1] = 0; + f->readString(insts[i].name, INS_MAX_NAME_SIZE); + insts[i].name[INS_MAX_NAME_SIZE - 1] = 0; } // read timbre data for (int i = 0; i < nrTimbre; i++) { - f->readString((char *)insts[i].data, TIMBRE_DEF_LEN * 2); - insts[i].loaded = true; + uint8_t data[ADLIB_INST_LEN]; + for (unsigned int j = 0; j < sizeof(data); j++) + { + uint16_t val = static_cast(f->readInt(2)); + data[j] = val & 0xFF; + } + insts[i].backend_index = load_instrument_data(&data[0], sizeof(data)); } fp.close(f); return true; @@ -286,123 +302,42 @@ bool CmusPlayer::LoadTimbreBank(const std::string fname, const CFileProvider &fp bool CmusPlayer::FetchTimbreData(const std::string fname, const CFileProvider &fp) { - binistream *f = fp.open(fname); - if (!f) { + SBnkHeader bnk_header; + binistream *bnk_file = fp.open(fname.c_str()); + if (!bnk_file) { #ifdef DEBUG AdPlug_LogWrite("Instrument bank not found: %s\n", fname.c_str()); #endif return false; } - // file validation - if (fp.filesize(f) < BNK_HEADER_SIZE) - { - fp.close(f); - #ifdef DEBUG - AdPlug_LogWrite("Instrument bank size is wrong."); - #endif - return false; - } - // check bank version - if (f->readInt(1) != 1 || - f->readInt(1) != 0) - { - fp.close(f); - #ifdef DEBUG - AdPlug_LogWrite("Instrument bank version is wrong."); - #endif - return false; - } - // check bank signature - char signature[BNK_SIGNATURE_LEN + 1]; - signature[BNK_SIGNATURE_LEN] = 0; - f->readString(signature, BNK_SIGNATURE_LEN); - if (strcmp(signature, "ADLIB-")) - { - fp.close(f); - #ifdef DEBUG - AdPlug_LogWrite("Instrument bank signature is wrong."); - #endif - return false; - } - uint16_t numUsed = static_cast(f->readInt(2)); - uint16_t numInst = static_cast(f->readInt(2)); - uint32_t offsetName = static_cast(f->readInt(4)); - uint32_t offsetData = static_cast(f->readInt(4)); - // validate header and data size - if (numUsed == 0 || - numInst == 0 || - numUsed > numInst || - offsetName == 0 || - offsetData == 0 || - offsetName > offsetData || - offsetName > BNK_HEADER_SIZE || /* often 0x1C, also can be 0x14 */ - fp.filesize(f) < offsetData + numInst * BNK_INST_SIZE) - { - fp.close(f); - #ifdef DEBUG - AdPlug_LogWrite("Instrument bank format is incorrect."); - #endif - return false; - } - f->seek(offsetName); - uint8_t * names = new uint8_t[numInst * BNK_NAME_SIZE]; - f->readString((char *)names, numInst * BNK_NAME_SIZE); - - f->seek(offsetData); - uint8_t * instData = new uint8_t[numInst * BNK_INST_SIZE]; - f->readString((char *)instData, numInst * BNK_INST_SIZE); - - fp.close(f); - uint16_t index; - bool match; - for (int i = 0; i < numUsed; i++) + load_bnk_info(bnk_file, bnk_header); + for (int i = 0; i < nrTimbre; i++) { - index = *(uint16_t *)(names + i * BNK_NAME_SIZE); - for (int j = 0; j < nrTimbre; j++) - { - match = true; - for (int k = 0; k < TIMBRE_NAME_SIZE; k++) - { - if (k > 0 && insts[j].name[k - 1] == 0) - break; - if (tolower(insts[j].name[k]) != tolower(names[i * BNK_NAME_SIZE + 3 + k])) - { - match = false; - break; - } - } - if (match && !insts[j].loaded && index < numInst) - { - for (int k = 0; k < TIMBRE_DEF_LEN; k++) - { - insts[j].data[k] = instData[index * BNK_INST_SIZE + 2 + k]; - } - insts[j].loaded = true; - } - } - if (InstsLoaded()) - break; + if (insts[i].backend_index >= 0) + continue; + + insts[i].backend_index = load_bnk_instrument(bnk_file, bnk_header, std::string(insts[i].name)); } - delete[] names; - delete[] instData; + fp.close(bnk_file); return true; } -void CmusPlayer::rewind(int subsong) +void CmusPlayer::frontend_rewind(int subsong) { SetTempo(basicTempo, tickBeat); pos = 0; songend = false; - opl->init(); - if (drv) drv->SoundWarmInit(); + + SetRhythmMode(soundMode); + SetPitchRange(pitchBRange); for (int i = 0; i < MAX_VOICES; i++) + { volume[i] = 0; + SetDefaultInstrument(i); + } counter = 0; ticks = 0; - - if (drv) drv->SetMode(soundMode); - if (drv) drv->SetPitchRange(pitchBRange); } /* @@ -484,69 +419,75 @@ void CmusPlayer::executeCommand() case NOTE_ON_BYTE: haut = data[pos++]; vol = data[pos++]; - if (voice > MAX_VOICES - 1) + if (voice >= MAX_VOICES) break; if (!vol) { - if (drv) drv->NoteOff(voice); + NoteOff(voice); } else { if (vol != volume[voice]) { - if (drv) drv->SetVoiceVolume(voice, vol); + SetVolume(voice, vol); volume[voice] = vol; } - if (drv) drv->NoteOn(voice, haut); + NoteOn(voice, haut); } break; case NOTE_OFF_BYTE: haut = data[pos++]; vol = data[pos++]; - if (voice > MAX_VOICES - 1) + if (voice >= MAX_VOICES) break; - if (drv) drv->NoteOff(voice); + NoteOff(voice); if (isIMS && vol) { if (vol != volume[voice]) { - if (drv) drv->SetVoiceVolume(voice, vol); + SetVolume(voice, vol); volume[voice] = vol; } - if (drv) drv->NoteOn(voice, haut); + NoteOn(voice, haut); } break; case AFTER_TOUCH_BYTE: vol = data[pos++]; - if (voice > MAX_VOICES - 1) + if (voice >= MAX_VOICES) break; if (vol != volume[voice]) { - if (drv) drv->SetVoiceVolume(voice, vol); + SetVolume(voice, vol); volume[voice] = vol; } break; case PROG_CHANGE_BYTE: timbre = data[pos++]; - if (voice > MAX_VOICES - 1) + if (voice >= MAX_VOICES) break; - if (insts && - timbre < nrTimbre && - insts[timbre].loaded) + if (insts) { - if (drv) drv->SetVoiceTimbre(voice, &insts[timbre].data[0]); + if (timbre < nrTimbre && insts[timbre].backend_index >= 0) + SetInstrument(voice, insts[timbre].backend_index); + else + { + #ifdef DEBUG + AdPlug_LogWrite("Timbre not found: %d\n", timbre); + #endif + SetDefaultInstrument(voice); + } } #ifdef DEBUG else - AdPlug_LogWrite("Timbre not found: %d\n", timbre); + AdPlug_LogWrite("Unexpected: insts is NULL, timbre %d\n", timbre); #endif break; case PITCH_BEND_BYTE: pitch = data[pos++]; pitch |= data[pos++] << 7; - if (voice > MAX_VOICES - 1) + if (voice >= MAX_VOICES) break; - if (drv) drv->SetVoicePitch(voice, pitch); + ChangePitch(voice, pitch); break; case CONTROL_CHANGE_BYTE: /* unused */ diff --git a/plugins/adplug/adplug/mus.h b/plugins/adplug/adplug/mus.h index 968b7c53db..49bcce216d 100644 --- a/plugins/adplug/adplug/mus.h +++ b/plugins/adplug/adplug/mus.h @@ -32,8 +32,7 @@ #ifndef H_ADPLUG_MUSPLAYER #define H_ADPLUG_MUSPLAYER -#include "player.h" -#include "adlib.h" +#include "composer.h" #define SYSTEM_XOR_BYTE 0xF0 #define EOX_BYTE 0xF7 @@ -53,38 +52,35 @@ #define TUNE_NAME_SIZE 30 #define FILLER_SIZE 8 -#define TIMBRE_NAME_SIZE 9 -#define TIMBRE_DEF_LEN ADLIB_INST_LEN -#define TIMBRE_DEF_SIZE (TIMBRE_DEF_LEN * sizeof(int16_t)) +#define TIMBRE_DEF_SIZE (ADLIB_INST_LEN * sizeof(int16_t)) #define OVERFLOW_TICKS 240 #define MAX_SEC_DELAY 10.0f /* Wraithverge: changed this to float, to avoid casting */ #define HEADER_LEN 70 #define SND_HEADER_LEN 6 #define IMS_SIGNATURE 0x7777 -#define BNK_HEADER_SIZE 28 -#define BNK_SIGNATURE_LEN 6 -#define BNK_NAME_SIZE 12 -#define BNK_INST_SIZE 30 +#define KNOWN_MUS_EXT "mus", "mdy", "ims" +#define KNOWN_SND_EXT "snd", "tim", "tbr" +#define KNOWN_SND_NAME "", "timbres" +#define KNOWN_BNK_NAME "", "implay", "standard" -class CmusPlayer: public CPlayer +class CmusPlayer: public CcomposerBackend { public: static CPlayer *factory(Copl *newopl); CmusPlayer(Copl *newopl) - : CPlayer(newopl), drv(0), data(0), insts(0) + : CcomposerBackend(newopl), data(0), insts(0) { } ~CmusPlayer() { if (data) delete [] data; if (insts) delete[] insts; - if (drv) drv->~CadlibDriver(); }; bool load(const std::string &filename, const CFileProvider &fp); bool update(); - void rewind(int subsong); + void frontend_rewind(int subsong); float getrefresh() { @@ -105,7 +101,11 @@ class CmusPlayer: public CPlayer std::string getinstrument(unsigned int n) { - return insts && n < nrTimbre ? (insts[n].loaded ? std::string(insts[n].name) : std::string("[N/A] ").append(insts[n].name)) : std::string(); + return insts && n < nrTimbre ? + (insts[n].backend_index >= 0 ? + std::string(insts[n].name) : + std::string(insts[n].name).append(" (missing)") + ) : std::string(); }; private: @@ -115,7 +115,6 @@ class CmusPlayer: public CPlayer void SetTempo(uint16_t tempo, uint8_t tickBeat); uint32_t GetTicks(); void executeCommand(); - CadlibDriver *drv; protected: /* variables for playback */ @@ -143,9 +142,8 @@ class CmusPlayer: public CPlayer /* variables for timbre bank */ struct mus_inst { - char name[TIMBRE_NAME_SIZE]; - bool loaded; - int16_t data[TIMBRE_DEF_LEN]; + char name[INS_MAX_NAME_SIZE]; + int backend_index; }; uint16_t nrTimbre; /* # of definitions in bank. */ diff --git a/plugins/adplug/adplug/nemuopl.cpp b/plugins/adplug/adplug/nemuopl.cpp index 22806202ff..3f89cd8351 100644 --- a/plugins/adplug/adplug/nemuopl.cpp +++ b/plugins/adplug/adplug/nemuopl.cpp @@ -29,7 +29,8 @@ CNemuopl::CNemuopl(int rate) { opl = new opl3_chip(); OPL3_Reset(opl, rate); - currType = TYPE_OPL2; + currType = TYPE_OPL3; + samplerate = rate; } CNemuopl::~CNemuopl() @@ -47,4 +48,8 @@ void CNemuopl::write(int reg, int val) OPL3_WriteRegBuffered(opl, (currChip << 8) | reg, val); } -void CNemuopl::init() {} +void CNemuopl::init() +{ + OPL3_Reset(opl, samplerate); + currChip = 0; +} diff --git a/plugins/adplug/adplug/nemuopl.h b/plugins/adplug/adplug/nemuopl.h index 8b4848c659..27085e84b9 100644 --- a/plugins/adplug/adplug/nemuopl.h +++ b/plugins/adplug/adplug/nemuopl.h @@ -40,6 +40,7 @@ class CNemuopl: public Copl private: opl3_chip* opl; + int samplerate; }; #endif diff --git a/plugins/adplug/adplug/nukedopl.c b/plugins/adplug/adplug/nukedopl.c index 7e642996e2..2840e08742 100644 --- a/plugins/adplug/adplug/nukedopl.c +++ b/plugins/adplug/adplug/nukedopl.c @@ -1,40 +1,62 @@ -// -// Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT) -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Nuked OPL3 emulator. -// Thanks: -// MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): -// Feedback and Rhythm part calculation information. -// forums.submarine.org.uk(carbon14, opl3): -// Tremolo and phase generator calculation information. -// OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): -// OPL2 ROMs. -// -// version: 1.7.4 -// +/* Nuked OPL3 + * Copyright (C) 2013-2020 Nuke.YKT + * + * This file is part of Nuked OPL3. + * + * Nuked OPL3 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 + * of the License, or (at your option) any later version. + * + * Nuked OPL3 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Nuked OPL3. If not, see . + + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * + * version: 1.8 + */ #include #include #include + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define inline __inline +#endif + #include "nukedopl.h" +#if OPL_ENABLE_STEREOEXT && !defined OPL_SIN +#ifndef _USE_MATH_DEFINES +#define _USE_MATH_DEFINES 1 +#endif +#include +/* input: [0, 256), output: [0, 65536] */ +#define OPL_SIN(x) ((int32_t)(sin((x) * M_PI / 512.0) * 65536.0)) +#endif + +/* Quirk: Some FM channels are output one sample later on the left side than the right. */ +#ifndef OPL_QUIRK_CHANNELSAMPLEDELAY +#define OPL_QUIRK_CHANNELSAMPLEDELAY (!OPL_ENABLE_STEREOEXT) +#endif + #define RSM_FRAC 10 -// Channel types +/* Channel types */ enum { ch_2op = 0, @@ -43,7 +65,7 @@ enum { ch_drum = 3 }; -// Envelope key types +/* Envelope key types */ enum { egk_norm = 0x01, @@ -51,11 +73,11 @@ enum { }; -// -// logsin table -// +/* + logsin table +*/ -static const Bit16u logsinrom[256] = { +static const uint16_t logsinrom[256] = { 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, @@ -90,152 +112,139 @@ static const Bit16u logsinrom[256] = { 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 }; -// -// exp table -// - -static const Bit16u exprom[256] = { - 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, - 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, - 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, - 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, - 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, - 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, - 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, - 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, - 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, - 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, - 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, - 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, - 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, - 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, - 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, - 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, - 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, - 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, - 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, - 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, - 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, - 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, - 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, - 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, - 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, - 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, - 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, - 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, - 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, - 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, - 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, - 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +/* + exp table +*/ + +static const uint16_t exprom[256] = { + 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, + 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, + 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, + 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, + 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, + 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, + 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, + 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, + 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, + 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, + 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, + 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, + 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, + 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, + 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, + 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, + 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, + 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, + 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, + 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, + 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, + 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, + 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, + 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, + 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, + 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, + 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, + 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, + 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, + 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, + 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, + 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 }; -// -// freq mult table multiplied by 2 -// -// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 -// +/* + freq mult table multiplied by 2 -static const Bit8u mt[16] = { + 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 +*/ + +static const uint8_t mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; -// -// ksl table -// +/* + ksl table +*/ -static const Bit8u kslrom[16] = { +static const uint8_t kslrom[16] = { 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 }; -static const Bit8u kslshift[4] = { +static const uint8_t kslshift[4] = { 8, 1, 2, 0 }; -// -// envelope generator constants -// +/* + envelope generator constants +*/ -static const Bit8u eg_incstep[3][4][8] = { - { - { 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0 } - }, - { - { 0, 1, 0, 1, 0, 1, 0, 1 }, - { 0, 1, 0, 1, 1, 1, 0, 1 }, - { 0, 1, 1, 1, 0, 1, 1, 1 }, - { 0, 1, 1, 1, 1, 1, 1, 1 } - }, - { - { 1, 1, 1, 1, 1, 1, 1, 1 }, - { 2, 2, 1, 1, 1, 1, 1, 1 }, - { 2, 2, 1, 1, 2, 2, 1, 1 }, - { 2, 2, 2, 2, 2, 2, 1, 1 } - } -}; - -static const Bit8u eg_incdesc[16] = { - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 +static const uint8_t eg_incstep[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } }; -static const Bit8s eg_incsh[16] = { - 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2 -}; +/* + address decoding +*/ -// -// address decoding -// - -static const Bit8s ad_slot[0x20] = { +static const int8_t ad_slot[0x20] = { 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; -static const Bit8u ch_slot[18] = { +static const uint8_t ch_slot[18] = { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; -// -// Envelope generator -// +#if OPL_ENABLE_STEREOEXT +/* + stereo extension panning table +*/ + +static int32_t panpot_lut[256]; +static uint8_t panpot_lut_build = 0; +#endif + +/* + Envelope generator +*/ -typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); +typedef int16_t(*envelope_sinfunc)(uint16_t phase, uint16_t envelope); typedef void(*envelope_genfunc)(opl3_slot *slott); -static Bit16s OPL3_EnvelopeCalcExp(Bit32u level) +static int16_t OPL3_EnvelopeCalcExp(uint32_t level) { if (level > 0x1fff) { level = 0x1fff; } - return ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 1) >> (level >> 8); + return (exprom[level & 0xffu] << 1) >> (level >> 8); } -static Bit16s OPL3_EnvelopeCalcSin0(Bit16u phase, Bit16u envelope) +static int16_t OPL3_EnvelopeCalcSin0(uint16_t phase, uint16_t envelope) { - Bit16u out = 0; - Bit16u neg = 0; + uint16_t out = 0; + uint16_t neg = 0; phase &= 0x3ff; if (phase & 0x200) { - neg = ~0; + neg = 0xffff; } if (phase & 0x100) { - out = logsinrom[(phase & 0xff) ^ 0xff]; + out = logsinrom[(phase & 0xffu) ^ 0xffu]; } else { - out = logsinrom[phase & 0xff]; + out = logsinrom[phase & 0xffu]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; } -static Bit16s OPL3_EnvelopeCalcSin1(Bit16u phase, Bit16u envelope) +static int16_t OPL3_EnvelopeCalcSin1(uint16_t phase, uint16_t envelope) { - Bit16u out = 0; + uint16_t out = 0; phase &= 0x3ff; if (phase & 0x200) { @@ -243,33 +252,33 @@ static Bit16s OPL3_EnvelopeCalcSin1(Bit16u phase, Bit16u envelope) } else if (phase & 0x100) { - out = logsinrom[(phase & 0xff) ^ 0xff]; + out = logsinrom[(phase & 0xffu) ^ 0xffu]; } else { - out = logsinrom[phase & 0xff]; + out = logsinrom[phase & 0xffu]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)); } -static Bit16s OPL3_EnvelopeCalcSin2(Bit16u phase, Bit16u envelope) +static int16_t OPL3_EnvelopeCalcSin2(uint16_t phase, uint16_t envelope) { - Bit16u out = 0; + uint16_t out = 0; phase &= 0x3ff; if (phase & 0x100) { - out = logsinrom[(phase & 0xff) ^ 0xff]; + out = logsinrom[(phase & 0xffu) ^ 0xffu]; } else { - out = logsinrom[phase & 0xff]; + out = logsinrom[phase & 0xffu]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)); } -static Bit16s OPL3_EnvelopeCalcSin3(Bit16u phase, Bit16u envelope) +static int16_t OPL3_EnvelopeCalcSin3(uint16_t phase, uint16_t envelope) { - Bit16u out = 0; + uint16_t out = 0; phase &= 0x3ff; if (phase & 0x100) { @@ -277,19 +286,19 @@ static Bit16s OPL3_EnvelopeCalcSin3(Bit16u phase, Bit16u envelope) } else { - out = logsinrom[phase & 0xff]; + out = logsinrom[phase & 0xffu]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)); } -static Bit16s OPL3_EnvelopeCalcSin4(Bit16u phase, Bit16u envelope) +static int16_t OPL3_EnvelopeCalcSin4(uint16_t phase, uint16_t envelope) { - Bit16u out = 0; - Bit16u neg = 0; + uint16_t out = 0; + uint16_t neg = 0; phase &= 0x3ff; if ((phase & 0x300) == 0x100) { - neg = ~0; + neg = 0xffff; } if (phase & 0x200) { @@ -297,18 +306,18 @@ static Bit16s OPL3_EnvelopeCalcSin4(Bit16u phase, Bit16u envelope) } else if (phase & 0x80) { - out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + out = logsinrom[((phase ^ 0xffu) << 1u) & 0xffu]; } else { - out = logsinrom[(phase << 1) & 0xff]; + out = logsinrom[(phase << 1u) & 0xffu]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; } -static Bit16s OPL3_EnvelopeCalcSin5(Bit16u phase, Bit16u envelope) +static int16_t OPL3_EnvelopeCalcSin5(uint16_t phase, uint16_t envelope) { - Bit16u out = 0; + uint16_t out = 0; phase &= 0x3ff; if (phase & 0x200) { @@ -316,34 +325,34 @@ static Bit16s OPL3_EnvelopeCalcSin5(Bit16u phase, Bit16u envelope) } else if (phase & 0x80) { - out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + out = logsinrom[((phase ^ 0xffu) << 1u) & 0xffu]; } else { - out = logsinrom[(phase << 1) & 0xff]; + out = logsinrom[(phase << 1u) & 0xffu]; } return OPL3_EnvelopeCalcExp(out + (envelope << 3)); } -static Bit16s OPL3_EnvelopeCalcSin6(Bit16u phase, Bit16u envelope) +static int16_t OPL3_EnvelopeCalcSin6(uint16_t phase, uint16_t envelope) { - Bit16u neg = 0; + uint16_t neg = 0; phase &= 0x3ff; if (phase & 0x200) { - neg = ~0; + neg = 0xffff; } return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; } -static Bit16s OPL3_EnvelopeCalcSin7(Bit16u phase, Bit16u envelope) +static int16_t OPL3_EnvelopeCalcSin7(uint16_t phase, uint16_t envelope) { - Bit16u out = 0; - Bit16u neg = 0; + uint16_t out = 0; + uint16_t neg = 0; phase &= 0x3ff; if (phase & 0x200) { - neg = ~0; + neg = 0xffff; phase = (phase & 0x1ff) ^ 0x1ff; } out = phase << 3; @@ -361,194 +370,201 @@ static const envelope_sinfunc envelope_sin[8] = { OPL3_EnvelopeCalcSin7 }; -static void OPL3_EnvelopeGenOff(opl3_slot *slot); -static void OPL3_EnvelopeGenAttack(opl3_slot *slot); -static void OPL3_EnvelopeGenDecay(opl3_slot *slot); -static void OPL3_EnvelopeGenSustain(opl3_slot *slot); -static void OPL3_EnvelopeGenRelease(opl3_slot *slot); - -envelope_genfunc envelope_gen[5] = { - OPL3_EnvelopeGenOff, - OPL3_EnvelopeGenAttack, - OPL3_EnvelopeGenDecay, - OPL3_EnvelopeGenSustain, - OPL3_EnvelopeGenRelease -}; - enum envelope_gen_num { - envelope_gen_num_off = 0, - envelope_gen_num_attack = 1, - envelope_gen_num_decay = 2, - envelope_gen_num_sustain = 3, - envelope_gen_num_release = 4 + envelope_gen_num_attack = 0, + envelope_gen_num_decay = 1, + envelope_gen_num_sustain = 2, + envelope_gen_num_release = 3 }; -static Bit8u OPL3_EnvelopeCalcRate(opl3_slot *slot, Bit8u reg_rate) -{ - Bit8u rate; - if (reg_rate == 0x00) - { - return 0x00; - } - rate = (reg_rate << 2) - + (slot->reg_ksr ? slot->channel->ksv : (slot->channel->ksv >> 2)); - if (rate > 0x3c) - { - rate = 0x3c; - } - return rate; -} - static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) { - Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 2) + int16_t ksl = (kslrom[slot->channel->f_num >> 6u] << 2) - ((0x08 - slot->channel->block) << 5); if (ksl < 0) { ksl = 0; } - slot->eg_ksl = (Bit8u)ksl; + slot->eg_ksl = (uint8_t)ksl; } -static void OPL3_EnvelopeUpdateRate(opl3_slot *slot) +static void OPL3_EnvelopeCalc(opl3_slot *slot) { - switch (slot->eg_gen) + uint8_t nonzero; + uint8_t rate; + uint8_t rate_hi; + uint8_t rate_lo; + uint8_t reg_rate = 0; + uint8_t ks; + uint8_t eg_shift, shift; + uint16_t eg_rout; + int16_t eg_inc; + uint8_t eg_off; + uint8_t reset = 0; + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + if (slot->key && slot->eg_gen == envelope_gen_num_release) { - case envelope_gen_num_off: - case envelope_gen_num_attack: - slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_ar); - break; - case envelope_gen_num_decay: - slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_dr); - break; - case envelope_gen_num_sustain: - case envelope_gen_num_release: - slot->eg_rate = OPL3_EnvelopeCalcRate(slot, slot->reg_rr); - break; + reset = 1; + reg_rate = slot->reg_ar; } -} - -static void OPL3_EnvelopeGenOff(opl3_slot *slot) -{ - slot->eg_rout = 0x1ff; -} - -static void OPL3_EnvelopeGenAttack(opl3_slot *slot) -{ - if (slot->eg_rout == 0x00) + else { - slot->eg_gen = envelope_gen_num_decay; - OPL3_EnvelopeUpdateRate(slot); - return; + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + reg_rate = slot->reg_ar; + break; + case envelope_gen_num_decay: + reg_rate = slot->reg_dr; + break; + case envelope_gen_num_sustain: + if (!slot->reg_type) + { + reg_rate = slot->reg_rr; + } + break; + case envelope_gen_num_release: + reg_rate = slot->reg_rr; + break; + } } - slot->eg_rout += ((~slot->eg_rout) * slot->eg_inc) >> 3; - if (slot->eg_rout < 0x00) + slot->pg_reset = reset; + ks = slot->channel->ksv >> ((slot->reg_ksr ^ 1) << 1); + nonzero = (reg_rate != 0); + rate = ks + (reg_rate << 2); + rate_hi = rate >> 2; + rate_lo = rate & 0x03; + if (rate_hi & 0x10) { - slot->eg_rout = 0x00; + rate_hi = 0x0f; } -} - -static void OPL3_EnvelopeGenDecay(opl3_slot *slot) -{ - if (slot->eg_rout >= slot->reg_sl << 4) + eg_shift = rate_hi + slot->chip->eg_add; + shift = 0; + if (nonzero) { - slot->eg_gen = envelope_gen_num_sustain; - OPL3_EnvelopeUpdateRate(slot); - return; + if (rate_hi < 12) + { + if (slot->chip->eg_state) + { + switch (eg_shift) + { + case 12: + shift = 1; + break; + case 13: + shift = (rate_lo >> 1) & 0x01; + break; + case 14: + shift = rate_lo & 0x01; + break; + default: + break; + } + } + } + else + { + shift = (rate_hi & 0x03) + eg_incstep[rate_lo][slot->chip->timer & 0x03u]; + if (shift & 0x04) + { + shift = 0x03; + } + if (!shift) + { + shift = slot->chip->eg_state; + } + } } - slot->eg_rout += slot->eg_inc; -} - -static void OPL3_EnvelopeGenSustain(opl3_slot *slot) -{ - if (!slot->reg_type) + eg_rout = slot->eg_rout; + eg_inc = 0; + eg_off = 0; + /* Instant attack */ + if (reset && rate_hi == 0x0f) { - OPL3_EnvelopeGenRelease(slot); + eg_rout = 0x00; } -} - -static void OPL3_EnvelopeGenRelease(opl3_slot *slot) -{ - if (slot->eg_rout >= 0x1ff) + /* Envelope off */ + if ((slot->eg_rout & 0x1f8) == 0x1f8) { - slot->eg_gen = envelope_gen_num_off; - slot->eg_rout = 0x1ff; - OPL3_EnvelopeUpdateRate(slot); - return; + eg_off = 1; } - slot->eg_rout += slot->eg_inc; -} - -static void OPL3_EnvelopeCalc(opl3_slot *slot) -{ - Bit8u rate_h, rate_l; - Bit8u inc = 0; - rate_h = slot->eg_rate >> 2; - rate_l = slot->eg_rate & 3; - if (eg_incsh[rate_h] > 0) + if (slot->eg_gen != envelope_gen_num_attack && !reset && eg_off) { - if ((slot->chip->timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) + eg_rout = 0x1ff; + } + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + if (!slot->eg_rout) + { + slot->eg_gen = envelope_gen_num_decay; + } + else if (slot->key && shift > 0 && rate_hi != 0x0f) + { + eg_inc = ~slot->eg_rout >> (4 - shift); + } + break; + case envelope_gen_num_decay: + if ((slot->eg_rout >> 4) == slot->reg_sl) { - inc = eg_incstep[eg_incdesc[rate_h]][rate_l] - [((slot->chip->timer)>> eg_incsh[rate_h]) & 0x07]; + slot->eg_gen = envelope_gen_num_sustain; } + else if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; } - else + slot->eg_rout = (eg_rout + eg_inc) & 0x1ff; + /* Key off */ + if (reset) { - inc = eg_incstep[eg_incdesc[rate_h]][rate_l] - [slot->chip->timer & 0x07] << (-eg_incsh[rate_h]); + slot->eg_gen = envelope_gen_num_attack; } - slot->eg_inc = inc; - slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) - + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; - envelope_gen[slot->eg_gen](slot); -} - -static void OPL3_EnvelopeKeyOn(opl3_slot *slot, Bit8u type) -{ if (!slot->key) { - slot->eg_gen = envelope_gen_num_attack; - OPL3_EnvelopeUpdateRate(slot); - if ((slot->eg_rate >> 2) == 0x0f) - { - slot->eg_gen = envelope_gen_num_decay; - OPL3_EnvelopeUpdateRate(slot); - slot->eg_rout = 0x00; - } - slot->pg_phase = 0x00; + slot->eg_gen = envelope_gen_num_release; } +} + +static void OPL3_EnvelopeKeyOn(opl3_slot *slot, uint8_t type) +{ slot->key |= type; } -static void OPL3_EnvelopeKeyOff(opl3_slot *slot, Bit8u type) +static void OPL3_EnvelopeKeyOff(opl3_slot *slot, uint8_t type) { - if (slot->key) - { - slot->key &= (~type); - if (!slot->key) - { - slot->eg_gen = envelope_gen_num_release; - OPL3_EnvelopeUpdateRate(slot); - } - } + slot->key &= ~type; } -// -// Phase Generator -// +/* + Phase Generator +*/ static void OPL3_PhaseGenerate(opl3_slot *slot) { - Bit16u f_num; - Bit32u basefreq; - + opl3_chip *chip; + uint16_t f_num; + uint32_t basefreq; + uint8_t rm_xor, n_bit; + uint32_t noise; + uint16_t phase; + + chip = slot->chip; f_num = slot->channel->f_num; if (slot->reg_vib) { - Bit8s range; - Bit8u vibpos; + int8_t range; + uint8_t vibpos; range = (f_num >> 7) & 7; vibpos = slot->chip->vibpos; @@ -570,27 +586,65 @@ static void OPL3_PhaseGenerate(opl3_slot *slot) f_num += range; } basefreq = (f_num << slot->channel->block) >> 1; + phase = (uint16_t)(slot->pg_phase >> 9); + if (slot->pg_reset) + { + slot->pg_phase = 0; + } slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1; -} - -// -// Noise Generator -// - -static void OPL3_NoiseGenerate(opl3_chip *chip) -{ - if (chip->noise & 0x01) + /* Rhythm mode */ + noise = chip->noise; + slot->pg_phase_out = phase; + if (slot->slot_num == 13) /* hh */ { - chip->noise ^= 0x800302; + chip->rm_hh_bit2 = (phase >> 2) & 1; + chip->rm_hh_bit3 = (phase >> 3) & 1; + chip->rm_hh_bit7 = (phase >> 7) & 1; + chip->rm_hh_bit8 = (phase >> 8) & 1; + } + if (slot->slot_num == 17 && (chip->rhy & 0x20)) /* tc */ + { + chip->rm_tc_bit3 = (phase >> 3) & 1; + chip->rm_tc_bit5 = (phase >> 5) & 1; + } + if (chip->rhy & 0x20) + { + rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) + | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) + | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); + switch (slot->slot_num) + { + case 13: /* hh */ + slot->pg_phase_out = rm_xor << 9; + if (rm_xor ^ (noise & 1)) + { + slot->pg_phase_out |= 0xd0; + } + else + { + slot->pg_phase_out |= 0x34; + } + break; + case 16: /* sd */ + slot->pg_phase_out = (chip->rm_hh_bit8 << 9) + | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8); + break; + case 17: /* tc */ + slot->pg_phase_out = (rm_xor << 9) | 0x80; + break; + default: + break; + } } - chip->noise >>= 1; + n_bit = ((noise >> 14) ^ noise) & 0x01; + chip->noise = (noise >> 1) | (n_bit << 22); } -// -// Slot -// +/* + Slot +*/ -static void OPL3_SlotWrite20(opl3_slot *slot, Bit8u data) +static void OPL3_SlotWrite20(opl3_slot *slot, uint8_t data) { if ((data >> 7) & 0x01) { @@ -598,30 +652,28 @@ static void OPL3_SlotWrite20(opl3_slot *slot, Bit8u data) } else { - slot->trem = (Bit8u*)&slot->chip->zeromod; + slot->trem = (uint8_t*)&slot->chip->zeromod; } slot->reg_vib = (data >> 6) & 0x01; slot->reg_type = (data >> 5) & 0x01; slot->reg_ksr = (data >> 4) & 0x01; slot->reg_mult = data & 0x0f; - OPL3_EnvelopeUpdateRate(slot); } -static void OPL3_SlotWrite40(opl3_slot *slot, Bit8u data) +static void OPL3_SlotWrite40(opl3_slot *slot, uint8_t data) { slot->reg_ksl = (data >> 6) & 0x03; slot->reg_tl = data & 0x3f; OPL3_EnvelopeUpdateKSL(slot); } -static void OPL3_SlotWrite60(opl3_slot *slot, Bit8u data) +static void OPL3_SlotWrite60(opl3_slot *slot, uint8_t data) { slot->reg_ar = (data >> 4) & 0x0f; slot->reg_dr = data & 0x0f; - OPL3_EnvelopeUpdateRate(slot); } -static void OPL3_SlotWrite80(opl3_slot *slot, Bit8u data) +static void OPL3_SlotWrite80(opl3_slot *slot, uint8_t data) { slot->reg_sl = (data >> 4) & 0x0f; if (slot->reg_sl == 0x0f) @@ -629,10 +681,9 @@ static void OPL3_SlotWrite80(opl3_slot *slot, Bit8u data) slot->reg_sl = 0x1f; } slot->reg_rr = data & 0x0f; - OPL3_EnvelopeUpdateRate(slot); } -static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data) +static void OPL3_SlotWriteE0(opl3_slot *slot, uint8_t data) { slot->reg_wf = data & 0x07; if (slot->chip->newm == 0x00) @@ -641,19 +692,9 @@ static void OPL3_SlotWriteE0(opl3_slot *slot, Bit8u data) } } -static void OPL3_SlotGeneratePhase(opl3_slot *slot, Bit16u phase) -{ - slot->out = envelope_sin[slot->reg_wf](phase, slot->eg_out); -} - static void OPL3_SlotGenerate(opl3_slot *slot) { - OPL3_SlotGeneratePhase(slot, (Bit16u)(slot->pg_phase >> 9) + *slot->mod); -} - -static void OPL3_SlotGenerateZM(opl3_slot *slot) -{ - OPL3_SlotGeneratePhase(slot, (Bit16u)(slot->pg_phase >> 9)); + slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out); } static void OPL3_SlotCalcFB(opl3_slot *slot) @@ -669,18 +710,18 @@ static void OPL3_SlotCalcFB(opl3_slot *slot) slot->prout = slot->out; } -// -// Channel -// +/* + Channel +*/ static void OPL3_ChannelSetupAlg(opl3_channel *channel); -static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) +static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, uint8_t data) { opl3_channel *channel6; opl3_channel *channel7; opl3_channel *channel8; - Bit8u chnum; + uint8_t chnum; chip->rhy = data & 0x3f; if (chip->rhy & 0x20) @@ -688,69 +729,71 @@ static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) channel6 = &chip->channel[6]; channel7 = &chip->channel[7]; channel8 = &chip->channel[8]; - channel6->out[0] = &channel6->slots[1]->out; - channel6->out[1] = &channel6->slots[1]->out; + channel6->out[0] = &channel6->slotz[1]->out; + channel6->out[1] = &channel6->slotz[1]->out; channel6->out[2] = &chip->zeromod; channel6->out[3] = &chip->zeromod; - channel7->out[0] = &channel7->slots[0]->out; - channel7->out[1] = &channel7->slots[0]->out; - channel7->out[2] = &channel7->slots[1]->out; - channel7->out[3] = &channel7->slots[1]->out; - channel8->out[0] = &channel8->slots[0]->out; - channel8->out[1] = &channel8->slots[0]->out; - channel8->out[2] = &channel8->slots[1]->out; - channel8->out[3] = &channel8->slots[1]->out; + channel7->out[0] = &channel7->slotz[0]->out; + channel7->out[1] = &channel7->slotz[0]->out; + channel7->out[2] = &channel7->slotz[1]->out; + channel7->out[3] = &channel7->slotz[1]->out; + channel8->out[0] = &channel8->slotz[0]->out; + channel8->out[1] = &channel8->slotz[0]->out; + channel8->out[2] = &channel8->slotz[1]->out; + channel8->out[3] = &channel8->slotz[1]->out; for (chnum = 6; chnum < 9; chnum++) { chip->channel[chnum].chtype = ch_drum; } OPL3_ChannelSetupAlg(channel6); - //hh + OPL3_ChannelSetupAlg(channel7); + OPL3_ChannelSetupAlg(channel8); + /* hh */ if (chip->rhy & 0x01) { - OPL3_EnvelopeKeyOn(channel7->slots[0], egk_drum); + OPL3_EnvelopeKeyOn(channel7->slotz[0], egk_drum); } else { - OPL3_EnvelopeKeyOff(channel7->slots[0], egk_drum); + OPL3_EnvelopeKeyOff(channel7->slotz[0], egk_drum); } - //tc + /* tc */ if (chip->rhy & 0x02) { - OPL3_EnvelopeKeyOn(channel8->slots[1], egk_drum); + OPL3_EnvelopeKeyOn(channel8->slotz[1], egk_drum); } else { - OPL3_EnvelopeKeyOff(channel8->slots[1], egk_drum); + OPL3_EnvelopeKeyOff(channel8->slotz[1], egk_drum); } - //tom + /* tom */ if (chip->rhy & 0x04) { - OPL3_EnvelopeKeyOn(channel8->slots[0], egk_drum); + OPL3_EnvelopeKeyOn(channel8->slotz[0], egk_drum); } else { - OPL3_EnvelopeKeyOff(channel8->slots[0], egk_drum); + OPL3_EnvelopeKeyOff(channel8->slotz[0], egk_drum); } - //sd + /* sd */ if (chip->rhy & 0x08) { - OPL3_EnvelopeKeyOn(channel7->slots[1], egk_drum); + OPL3_EnvelopeKeyOn(channel7->slotz[1], egk_drum); } else { - OPL3_EnvelopeKeyOff(channel7->slots[1], egk_drum); + OPL3_EnvelopeKeyOff(channel7->slotz[1], egk_drum); } - //bd + /* bd */ if (chip->rhy & 0x10) { - OPL3_EnvelopeKeyOn(channel6->slots[0], egk_drum); - OPL3_EnvelopeKeyOn(channel6->slots[1], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slotz[1], egk_drum); } else { - OPL3_EnvelopeKeyOff(channel6->slots[0], egk_drum); - OPL3_EnvelopeKeyOff(channel6->slots[1], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slotz[1], egk_drum); } } else @@ -759,13 +802,13 @@ static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, Bit8u data) { chip->channel[chnum].chtype = ch_2op; OPL3_ChannelSetupAlg(&chip->channel[chnum]); - OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[0], egk_drum); - OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[1], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[0], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slotz[1], egk_drum); } } } -static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data) +static void OPL3_ChannelWriteA0(opl3_channel *channel, uint8_t data) { if (channel->chip->newm && channel->chtype == ch_4op2) { @@ -774,22 +817,18 @@ static void OPL3_ChannelWriteA0(opl3_channel *channel, Bit8u data) channel->f_num = (channel->f_num & 0x300) | data; channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); - OPL3_EnvelopeUpdateKSL(channel->slots[0]); - OPL3_EnvelopeUpdateKSL(channel->slots[1]); - OPL3_EnvelopeUpdateRate(channel->slots[0]); - OPL3_EnvelopeUpdateRate(channel->slots[1]); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); if (channel->chip->newm && channel->chtype == ch_4op) { channel->pair->f_num = channel->f_num; channel->pair->ksv = channel->ksv; - OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); - OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); - OPL3_EnvelopeUpdateRate(channel->pair->slots[0]); - OPL3_EnvelopeUpdateRate(channel->pair->slots[1]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); } } -static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data) +static void OPL3_ChannelWriteB0(opl3_channel *channel, uint8_t data) { if (channel->chip->newm && channel->chtype == ch_4op2) { @@ -799,19 +838,15 @@ static void OPL3_ChannelWriteB0(opl3_channel *channel, Bit8u data) channel->block = (data >> 2) & 0x07; channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); - OPL3_EnvelopeUpdateKSL(channel->slots[0]); - OPL3_EnvelopeUpdateKSL(channel->slots[1]); - OPL3_EnvelopeUpdateRate(channel->slots[0]); - OPL3_EnvelopeUpdateRate(channel->slots[1]); + OPL3_EnvelopeUpdateKSL(channel->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->slotz[1]); if (channel->chip->newm && channel->chtype == ch_4op) { channel->pair->f_num = channel->f_num; channel->pair->block = channel->block; channel->pair->ksv = channel->ksv; - OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); - OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); - OPL3_EnvelopeUpdateRate(channel->pair->slots[0]); - OPL3_EnvelopeUpdateRate(channel->pair->slots[1]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slotz[1]); } } @@ -819,15 +854,21 @@ static void OPL3_ChannelSetupAlg(opl3_channel *channel) { if (channel->chtype == ch_drum) { + if (channel->ch_num == 7 || channel->ch_num == 8) + { + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->chip->zeromod; + return; + } switch (channel->alg & 0x01) { case 0x00: - channel->slots[0]->mod = &channel->slots[0]->fbmod; - channel->slots[1]->mod = &channel->slots[0]->out; + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; break; case 0x01: - channel->slots[0]->mod = &channel->slots[0]->fbmod; - channel->slots[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; break; } return; @@ -845,43 +886,43 @@ static void OPL3_ChannelSetupAlg(opl3_channel *channel) switch (channel->alg & 0x03) { case 0x00: - channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; - channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; - channel->slots[0]->mod = &channel->pair->slots[1]->out; - channel->slots[1]->mod = &channel->slots[0]->out; - channel->out[0] = &channel->slots[1]->out; + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; channel->out[1] = &channel->chip->zeromod; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x01: - channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; - channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; - channel->slots[0]->mod = &channel->chip->zeromod; - channel->slots[1]->mod = &channel->slots[0]->out; - channel->out[0] = &channel->pair->slots[1]->out; - channel->out[1] = &channel->slots[1]->out; + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->pair->slotz[0]->out; + channel->slotz[0]->mod = &channel->chip->zeromod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[1]->out; + channel->out[1] = &channel->slotz[1]->out; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x02: - channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; - channel->pair->slots[1]->mod = &channel->chip->zeromod; - channel->slots[0]->mod = &channel->pair->slots[1]->out; - channel->slots[1]->mod = &channel->slots[0]->out; - channel->out[0] = &channel->pair->slots[0]->out; - channel->out[1] = &channel->slots[1]->out; + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x03: - channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; - channel->pair->slots[1]->mod = &channel->chip->zeromod; - channel->slots[0]->mod = &channel->pair->slots[1]->out; - channel->slots[1]->mod = &channel->chip->zeromod; - channel->out[0] = &channel->pair->slots[0]->out; - channel->out[1] = &channel->slots[0]->out; - channel->out[2] = &channel->slots[1]->out; + channel->pair->slotz[0]->mod = &channel->pair->slotz[0]->fbmod; + channel->pair->slotz[1]->mod = &channel->chip->zeromod; + channel->slotz[0]->mod = &channel->pair->slotz[1]->out; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slotz[0]->out; + channel->out[1] = &channel->slotz[0]->out; + channel->out[2] = &channel->slotz[1]->out; channel->out[3] = &channel->chip->zeromod; break; } @@ -891,18 +932,18 @@ static void OPL3_ChannelSetupAlg(opl3_channel *channel) switch (channel->alg & 0x01) { case 0x00: - channel->slots[0]->mod = &channel->slots[0]->fbmod; - channel->slots[1]->mod = &channel->slots[0]->out; - channel->out[0] = &channel->slots[1]->out; + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->slotz[0]->out; + channel->out[0] = &channel->slotz[1]->out; channel->out[1] = &channel->chip->zeromod; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; case 0x01: - channel->slots[0]->mod = &channel->slots[0]->fbmod; - channel->slots[1]->mod = &channel->chip->zeromod; - channel->out[0] = &channel->slots[0]->out; - channel->out[1] = &channel->slots[1]->out; + channel->slotz[0]->mod = &channel->slotz[0]->fbmod; + channel->slotz[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slotz[0]->out; + channel->out[1] = &channel->slotz[1]->out; channel->out[2] = &channel->chip->zeromod; channel->out[3] = &channel->chip->zeromod; break; @@ -910,10 +951,8 @@ static void OPL3_ChannelSetupAlg(opl3_channel *channel) } } -static void OPL3_ChannelWriteC0(opl3_channel *channel, Bit8u data) +static void OPL3_ChannelUpdateAlg(opl3_channel *channel) { - channel->fb = (data & 0x0e) >> 1; - channel->con = data & 0x01; channel->alg = channel->con; if (channel->chip->newm) { @@ -938,38 +977,67 @@ static void OPL3_ChannelWriteC0(opl3_channel *channel, Bit8u data) { OPL3_ChannelSetupAlg(channel); } +} + +static void OPL3_ChannelWriteC0(opl3_channel *channel, uint8_t data) +{ + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + OPL3_ChannelUpdateAlg(channel); if (channel->chip->newm) { channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + channel->chc = ((data >> 6) & 0x01) ? ~0 : 0; + channel->chd = ((data >> 7) & 0x01) ? ~0 : 0; } else { - channel->cha = channel->chb = ~0; + channel->cha = channel->chb = (uint16_t)~0; + // TODO: Verify on real chip if DAC2 output is disabled in compat mode + channel->chc = channel->chd = 0; + } +#if OPL_ENABLE_STEREOEXT + if (!channel->chip->stereoext) + { + channel->leftpan = channel->cha << 16; + channel->rightpan = channel->chb << 16; } +#endif } +#if OPL_ENABLE_STEREOEXT +static void OPL3_ChannelWriteD0(opl3_channel* channel, uint8_t data) +{ + if (channel->chip->stereoext) + { + channel->leftpan = panpot_lut[data ^ 0xffu]; + channel->rightpan = panpot_lut[data]; + } +} +#endif + static void OPL3_ChannelKeyOn(opl3_channel *channel) { if (channel->chip->newm) { if (channel->chtype == ch_4op) { - OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); - OPL3_EnvelopeKeyOn(channel->pair->slots[0], egk_norm); - OPL3_EnvelopeKeyOn(channel->pair->slots[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slotz[1], egk_norm); } else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { - OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); } } else { - OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slotz[1], egk_norm); } } @@ -979,28 +1047,28 @@ static void OPL3_ChannelKeyOff(opl3_channel *channel) { if (channel->chtype == ch_4op) { - OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); - OPL3_EnvelopeKeyOff(channel->pair->slots[0], egk_norm); - OPL3_EnvelopeKeyOff(channel->pair->slots[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slotz[1], egk_norm); } else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { - OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); } } else { - OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); - OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slotz[1], egk_norm); } } -static void OPL3_ChannelSet4Op(opl3_chip *chip, Bit8u data) +static void OPL3_ChannelSet4Op(opl3_chip *chip, uint8_t data) { - Bit8u bit; - Bit8u chnum; + uint8_t bit; + uint8_t chnum; for (bit = 0; bit < 6; bit++) { chnum = bit; @@ -1011,17 +1079,20 @@ static void OPL3_ChannelSet4Op(opl3_chip *chip, Bit8u data) if ((data >> bit) & 0x01) { chip->channel[chnum].chtype = ch_4op; - chip->channel[chnum + 3].chtype = ch_4op2; + chip->channel[chnum + 3u].chtype = ch_4op2; + OPL3_ChannelUpdateAlg(&chip->channel[chnum]); } else { chip->channel[chnum].chtype = ch_2op; - chip->channel[chnum + 3].chtype = ch_2op; + chip->channel[chnum + 3u].chtype = ch_2op; + OPL3_ChannelUpdateAlg(&chip->channel[chnum]); + OPL3_ChannelUpdateAlg(&chip->channel[chnum + 3u]); } } } -static Bit16s OPL3_ClipSample(Bit32s sample) +static int16_t OPL3_ClipSample(int32_t sample) { if (sample > 32767) { @@ -1031,158 +1102,94 @@ static Bit16s OPL3_ClipSample(Bit32s sample) { sample = -32768; } - return (Bit16s)sample; + return (int16_t)sample; } -static void OPL3_GenerateRhythm1(opl3_chip *chip) +static void OPL3_ProcessSlot(opl3_slot *slot) { - opl3_channel *channel6; - opl3_channel *channel7; - opl3_channel *channel8; - Bit16u phase14; - Bit16u phase17; - Bit16u phase; - Bit16u phasebit; - - channel6 = &chip->channel[6]; - channel7 = &chip->channel[7]; - channel8 = &chip->channel[8]; - OPL3_SlotGenerate(channel6->slots[0]); - phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff; - phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff; - phase = 0x00; - //hh tc phase bit - phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) - | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; - //hh - phase = (phasebit << 9) - | (0x34 << ((phasebit ^ (chip->noise & 0x01)) << 1)); - OPL3_SlotGeneratePhase(channel7->slots[0], phase); - //tt - OPL3_SlotGenerateZM(channel8->slots[0]); + OPL3_SlotCalcFB(slot); + OPL3_EnvelopeCalc(slot); + OPL3_PhaseGenerate(slot); + OPL3_SlotGenerate(slot); } -static void OPL3_GenerateRhythm2(opl3_chip *chip) +inline void OPL3_Generate4Ch(opl3_chip *chip, int16_t *buf4) { - opl3_channel *channel6; - opl3_channel *channel7; - opl3_channel *channel8; - Bit16u phase14; - Bit16u phase17; - Bit16u phase; - Bit16u phasebit; - - channel6 = &chip->channel[6]; - channel7 = &chip->channel[7]; - channel8 = &chip->channel[8]; - OPL3_SlotGenerate(channel6->slots[1]); - phase14 = (channel7->slots[0]->pg_phase >> 9) & 0x3ff; - phase17 = (channel8->slots[1]->pg_phase >> 9) & 0x3ff; - phase = 0x00; - //hh tc phase bit - phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) - | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; - //sd - phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8); - OPL3_SlotGeneratePhase(channel7->slots[1], phase); - //tc - phase = 0x100 | (phasebit << 9); - OPL3_SlotGeneratePhase(channel8->slots[1], phase); -} - -void OPL3_Generate(opl3_chip *chip, Bit16s *buf) -{ - Bit8u ii; - Bit8u jj; - Bit16s accm; - - buf[1] = OPL3_ClipSample(chip->mixbuff[1]); + opl3_channel *channel; + opl3_writebuf *writebuf; + int16_t **out; + int32_t mix[2]; + uint8_t ii; + int16_t accm; + uint8_t shift = 0; - for (ii = 0; ii < 12; ii++) - { - OPL3_SlotCalcFB(&chip->slot[ii]); - OPL3_PhaseGenerate(&chip->slot[ii]); - OPL3_EnvelopeCalc(&chip->slot[ii]); - OPL3_SlotGenerate(&chip->slot[ii]); - } + buf4[1] = OPL3_ClipSample(chip->mixbuff[1]); + buf4[3] = OPL3_ClipSample(chip->mixbuff[3]); - for (ii = 12; ii < 15; ii++) - { - OPL3_SlotCalcFB(&chip->slot[ii]); - OPL3_PhaseGenerate(&chip->slot[ii]); - OPL3_EnvelopeCalc(&chip->slot[ii]); - } - - if (chip->rhy & 0x20) - { - OPL3_GenerateRhythm1(chip); - } - else +#if OPL_QUIRK_CHANNELSAMPLEDELAY + for (ii = 0; ii < 15; ii++) +#else + for (ii = 0; ii < 36; ii++) +#endif { - OPL3_SlotGenerate(&chip->slot[12]); - OPL3_SlotGenerate(&chip->slot[13]); - OPL3_SlotGenerate(&chip->slot[14]); + OPL3_ProcessSlot(&chip->slot[ii]); } - chip->mixbuff[0] = 0; + mix[0] = mix[1] = 0; for (ii = 0; ii < 18; ii++) { - accm = 0; - for (jj = 0; jj < 4; jj++) - { - accm += *chip->channel[ii].out[jj]; - } - chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha); + channel = &chip->channel[ii]; + out = channel->out; + accm = *out[0] + *out[1] + *out[2] + *out[3]; +#if OPL_ENABLE_STEREOEXT + mix[0] += (int16_t)((accm * channel->leftpan) >> 16); +#else + mix[0] += (int16_t)(accm & channel->cha); +#endif + mix[1] += (int16_t)(accm & channel->chc); } + chip->mixbuff[0] = mix[0]; + chip->mixbuff[2] = mix[1]; +#if OPL_QUIRK_CHANNELSAMPLEDELAY for (ii = 15; ii < 18; ii++) { - OPL3_SlotCalcFB(&chip->slot[ii]); - OPL3_PhaseGenerate(&chip->slot[ii]); - OPL3_EnvelopeCalc(&chip->slot[ii]); + OPL3_ProcessSlot(&chip->slot[ii]); } +#endif - if (chip->rhy & 0x20) - { - OPL3_GenerateRhythm2(chip); - } - else - { - OPL3_SlotGenerate(&chip->slot[15]); - OPL3_SlotGenerate(&chip->slot[16]); - OPL3_SlotGenerate(&chip->slot[17]); - } - - buf[0] = OPL3_ClipSample(chip->mixbuff[0]); + buf4[0] = OPL3_ClipSample(chip->mixbuff[0]); + buf4[2] = OPL3_ClipSample(chip->mixbuff[2]); +#if OPL_QUIRK_CHANNELSAMPLEDELAY for (ii = 18; ii < 33; ii++) { - OPL3_SlotCalcFB(&chip->slot[ii]); - OPL3_PhaseGenerate(&chip->slot[ii]); - OPL3_EnvelopeCalc(&chip->slot[ii]); - OPL3_SlotGenerate(&chip->slot[ii]); + OPL3_ProcessSlot(&chip->slot[ii]); } +#endif - chip->mixbuff[1] = 0; + mix[0] = mix[1] = 0; for (ii = 0; ii < 18; ii++) { - accm = 0; - for (jj = 0; jj < 4; jj++) - { - accm += *chip->channel[ii].out[jj]; - } - chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb); + channel = &chip->channel[ii]; + out = channel->out; + accm = *out[0] + *out[1] + *out[2] + *out[3]; +#if OPL_ENABLE_STEREOEXT + mix[0] += (int16_t)((accm * channel->rightpan) >> 16); +#else + mix[0] += (int16_t)(accm & channel->chb); + #endif + mix[1] += (int16_t)(accm & channel->chd); } + chip->mixbuff[1] = mix[0]; + chip->mixbuff[3] = mix[1]; +#if OPL_QUIRK_CHANNELSAMPLEDELAY for (ii = 33; ii < 36; ii++) { - OPL3_SlotCalcFB(&chip->slot[ii]); - OPL3_PhaseGenerate(&chip->slot[ii]); - OPL3_EnvelopeCalc(&chip->slot[ii]); - OPL3_SlotGenerate(&chip->slot[ii]); + OPL3_ProcessSlot(&chip->slot[ii]); } - - OPL3_NoiseGenerate(chip); +#endif if ((chip->timer & 0x3f) == 0x3f) { @@ -1204,85 +1211,163 @@ void OPL3_Generate(opl3_chip *chip, Bit16s *buf) chip->timer++; - while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + chip->eg_add = 0; + if (chip->eg_timer) + { + while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0) + { + shift++; + } + if (shift > 12) + { + chip->eg_add = 0; + } + else + { + chip->eg_add = shift + 1; + } + } + + if (chip->eg_timerrem || chip->eg_state) { - if (!(chip->writebuf[chip->writebuf_cur].reg & 0x200)) + if (chip->eg_timer == UINT64_C(0xfffffffff)) + { + chip->eg_timer = 0; + chip->eg_timerrem = 1; + } + else + { + chip->eg_timer++; + chip->eg_timerrem = 0; + } + } + + chip->eg_state ^= 1; + + while ((writebuf = &chip->writebuf[chip->writebuf_cur]), writebuf->time <= chip->writebuf_samplecnt) + { + if (!(writebuf->reg & 0x200)) { break; } - chip->writebuf[chip->writebuf_cur].reg &= 0x1ff; - OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_cur].reg, - chip->writebuf[chip->writebuf_cur].data); + writebuf->reg &= 0x1ff; + OPL3_WriteReg(chip, writebuf->reg, writebuf->data); chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; } chip->writebuf_samplecnt++; } -void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf) +void OPL3_Generate(opl3_chip *chip, int16_t *buf) +{ + int16_t samples[4]; + OPL3_Generate4Ch(chip, samples); + buf[0] = samples[0]; + buf[1] = samples[1]; +} + +void OPL3_Generate4ChResampled(opl3_chip *chip, int16_t *buf4) { while (chip->samplecnt >= chip->rateratio) { chip->oldsamples[0] = chip->samples[0]; chip->oldsamples[1] = chip->samples[1]; - OPL3_Generate(chip, chip->samples); + chip->oldsamples[2] = chip->samples[2]; + chip->oldsamples[3] = chip->samples[3]; + OPL3_Generate4Ch(chip, chip->samples); chip->samplecnt -= chip->rateratio; } - buf[0] = (Bit16s)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) - + chip->samples[0] * chip->samplecnt) / chip->rateratio); - buf[1] = (Bit16s)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) - + chip->samples[1] * chip->samplecnt) / chip->rateratio); + buf4[0] = (int16_t)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf4[1] = (int16_t)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + buf4[2] = (int16_t)((chip->oldsamples[2] * (chip->rateratio - chip->samplecnt) + + chip->samples[2] * chip->samplecnt) / chip->rateratio); + buf4[3] = (int16_t)((chip->oldsamples[3] * (chip->rateratio - chip->samplecnt) + + chip->samples[3] * chip->samplecnt) / chip->rateratio); chip->samplecnt += 1 << RSM_FRAC; } -void OPL3_Reset(opl3_chip *chip, Bit32u samplerate) +void OPL3_GenerateResampled(opl3_chip *chip, int16_t *buf) { - Bit8u slotnum; - Bit8u channum; + int16_t samples[4]; + OPL3_Generate4ChResampled(chip, samples); + buf[0] = samples[0]; + buf[1] = samples[1]; +} + +void OPL3_Reset(opl3_chip *chip, uint32_t samplerate) +{ + opl3_slot *slot; + opl3_channel *channel; + uint8_t slotnum; + uint8_t channum; + uint8_t local_ch_slot; memset(chip, 0, sizeof(opl3_chip)); for (slotnum = 0; slotnum < 36; slotnum++) { - chip->slot[slotnum].chip = chip; - chip->slot[slotnum].mod = &chip->zeromod; - chip->slot[slotnum].eg_rout = 0x1ff; - chip->slot[slotnum].eg_out = 0x1ff; - chip->slot[slotnum].eg_gen = envelope_gen_num_off; - chip->slot[slotnum].trem = (Bit8u*)&chip->zeromod; + slot = &chip->slot[slotnum]; + slot->chip = chip; + slot->mod = &chip->zeromod; + slot->eg_rout = 0x1ff; + slot->eg_out = 0x1ff; + slot->eg_gen = envelope_gen_num_release; + slot->trem = (uint8_t*)&chip->zeromod; + slot->slot_num = slotnum; } for (channum = 0; channum < 18; channum++) { - chip->channel[channum].slots[0] = &chip->slot[ch_slot[channum]]; - chip->channel[channum].slots[1] = &chip->slot[ch_slot[channum] + 3]; - chip->slot[ch_slot[channum]].channel = &chip->channel[channum]; - chip->slot[ch_slot[channum] + 3].channel = &chip->channel[channum]; + channel = &chip->channel[channum]; + local_ch_slot = ch_slot[channum]; + channel->slotz[0] = &chip->slot[local_ch_slot]; + channel->slotz[1] = &chip->slot[local_ch_slot + 3u]; + chip->slot[local_ch_slot].channel = channel; + chip->slot[local_ch_slot + 3u].channel = channel; if ((channum % 9) < 3) { - chip->channel[channum].pair = &chip->channel[channum + 3]; + channel->pair = &chip->channel[channum + 3u]; } else if ((channum % 9) < 6) { - chip->channel[channum].pair = &chip->channel[channum - 3]; + channel->pair = &chip->channel[channum - 3u]; } - chip->channel[channum].chip = chip; - chip->channel[channum].out[0] = &chip->zeromod; - chip->channel[channum].out[1] = &chip->zeromod; - chip->channel[channum].out[2] = &chip->zeromod; - chip->channel[channum].out[3] = &chip->zeromod; - chip->channel[channum].chtype = ch_2op; - chip->channel[channum].cha = ~0; - chip->channel[channum].chb = ~0; - OPL3_ChannelSetupAlg(&chip->channel[channum]); - } - chip->noise = 0x306600; + channel->chip = chip; + channel->out[0] = &chip->zeromod; + channel->out[1] = &chip->zeromod; + channel->out[2] = &chip->zeromod; + channel->out[3] = &chip->zeromod; + channel->chtype = ch_2op; + channel->cha = 0xffff; + channel->chb = 0xffff; +#if OPL_ENABLE_STEREOEXT + channel->leftpan = 0x10000; + channel->rightpan = 0x10000; +#endif + channel->ch_num = channum; + OPL3_ChannelSetupAlg(channel); + } + chip->noise = 1; chip->rateratio = (samplerate << RSM_FRAC) / 49716; chip->tremoloshift = 4; chip->vibshift = 1; + +#if OPL_ENABLE_STEREOEXT + if (!panpot_lut_build) + { + int32_t i; + for (i = 0; i < 256; i++) + { + panpot_lut[i] = OPL_SIN(i); + } + panpot_lut_build = 1; + } +#endif } -void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) +void OPL3_WriteReg(opl3_chip *chip, uint16_t reg, uint8_t v) { - Bit8u high = (reg >> 8) & 0x01; - Bit8u regm = reg & 0xff; + uint8_t high = (reg >> 8) & 0x01; + uint8_t regm = reg & 0xff; switch (regm & 0xf0) { case 0x00: @@ -1295,6 +1380,9 @@ void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) break; case 0x05: chip->newm = v & 0x01; +#if OPL_ENABLE_STEREOEXT + chip->stereoext = (v >> 1) & 0x01; +#endif break; } } @@ -1310,43 +1398,43 @@ void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) break; case 0x20: case 0x30: - if (ad_slot[regm & 0x1f] >= 0) + if (ad_slot[regm & 0x1fu] >= 0) { - OPL3_SlotWrite20(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + OPL3_SlotWrite20(&chip->slot[18u * high + ad_slot[regm & 0x1fu]], v); } break; case 0x40: case 0x50: - if (ad_slot[regm & 0x1f] >= 0) + if (ad_slot[regm & 0x1fu] >= 0) { - OPL3_SlotWrite40(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + OPL3_SlotWrite40(&chip->slot[18u * high + ad_slot[regm & 0x1fu]], v); } break; case 0x60: case 0x70: - if (ad_slot[regm & 0x1f] >= 0) + if (ad_slot[regm & 0x1fu] >= 0) { - OPL3_SlotWrite60(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + OPL3_SlotWrite60(&chip->slot[18u * high + ad_slot[regm & 0x1fu]], v); } break; case 0x80: case 0x90: - if (ad_slot[regm & 0x1f] >= 0) + if (ad_slot[regm & 0x1fu] >= 0) { - OPL3_SlotWrite80(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + OPL3_SlotWrite80(&chip->slot[18u * high + ad_slot[regm & 0x1fu]], v); } break; case 0xe0: case 0xf0: - if (ad_slot[regm & 0x1f] >= 0) + if (ad_slot[regm & 0x1fu] >= 0) { - OPL3_SlotWriteE0(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + OPL3_SlotWriteE0(&chip->slot[18u * high + ad_slot[regm & 0x1fu]], v); } break; case 0xa0: if ((regm & 0x0f) < 9) { - OPL3_ChannelWriteA0(&chip->channel[9 * high + (regm & 0x0f)], v); + OPL3_ChannelWriteA0(&chip->channel[9u * high + (regm & 0x0fu)], v); } break; case 0xb0: @@ -1358,41 +1446,53 @@ void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) } else if ((regm & 0x0f) < 9) { - OPL3_ChannelWriteB0(&chip->channel[9 * high + (regm & 0x0f)], v); + OPL3_ChannelWriteB0(&chip->channel[9u * high + (regm & 0x0fu)], v); if (v & 0x20) { - OPL3_ChannelKeyOn(&chip->channel[9 * high + (regm & 0x0f)]); + OPL3_ChannelKeyOn(&chip->channel[9u * high + (regm & 0x0fu)]); } else { - OPL3_ChannelKeyOff(&chip->channel[9 * high + (regm & 0x0f)]); + OPL3_ChannelKeyOff(&chip->channel[9u * high + (regm & 0x0fu)]); } } break; case 0xc0: if ((regm & 0x0f) < 9) { - OPL3_ChannelWriteC0(&chip->channel[9 * high + (regm & 0x0f)], v); + OPL3_ChannelWriteC0(&chip->channel[9u * high + (regm & 0x0fu)], v); } break; +#if OPL_ENABLE_STEREOEXT + case 0xd0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteD0(&chip->channel[9u * high + (regm & 0x0fu)], v); + } + break; +#endif } } -void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) +void OPL3_WriteRegBuffered(opl3_chip *chip, uint16_t reg, uint8_t v) { - Bit64u time1, time2; + uint64_t time1, time2; + opl3_writebuf *writebuf; + uint32_t writebuf_last; + + writebuf_last = chip->writebuf_last; + writebuf = &chip->writebuf[writebuf_last]; - if (chip->writebuf[chip->writebuf_last].reg & 0x200) + if (writebuf->reg & 0x200) { - OPL3_WriteReg(chip, chip->writebuf[chip->writebuf_last].reg & 0x1ff, - chip->writebuf[chip->writebuf_last].data); + OPL3_WriteReg(chip, writebuf->reg & 0x1ff, writebuf->data); - chip->writebuf_cur = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; - chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + chip->writebuf_cur = (writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip->writebuf_samplecnt = writebuf->time; } - chip->writebuf[chip->writebuf_last].reg = reg | 0x200; - chip->writebuf[chip->writebuf_last].data = v; + writebuf->reg = reg | 0x200; + writebuf->data = v; time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY; time2 = chip->writebuf_samplecnt; @@ -1401,14 +1501,31 @@ void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v) time1 = time2; } - chip->writebuf[chip->writebuf_last].time = time1; + writebuf->time = time1; chip->writebuf_lasttime = time1; - chip->writebuf_last = (chip->writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip->writebuf_last = (writebuf_last + 1) % OPL_WRITEBUF_SIZE; +} + +void OPL3_Generate4ChStream(opl3_chip *chip, int16_t *sndptr1, int16_t *sndptr2, uint32_t numsamples) +{ + uint_fast32_t i; + int16_t samples[4]; + + for(i = 0; i < numsamples; i++) + { + OPL3_Generate4ChResampled(chip, samples); + sndptr1[0] = samples[0]; + sndptr1[1] = samples[1]; + sndptr2[0] = samples[2]; + sndptr2[1] = samples[3]; + sndptr1 += 2; + sndptr2 += 2; + } } -void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples) +void OPL3_GenerateStream(opl3_chip *chip, int16_t *sndptr, uint32_t numsamples) { - Bit32u i; + uint_fast32_t i; for(i = 0; i < numsamples; i++) { diff --git a/plugins/adplug/adplug/nukedopl.h b/plugins/adplug/adplug/nukedopl.h index e11d388fbc..75e268377a 100644 --- a/plugins/adplug/adplug/nukedopl.h +++ b/plugins/adplug/adplug/nukedopl.h @@ -1,51 +1,51 @@ -// -// Copyright (C) 2013-2016 Alexey Khokholov (Nuke.YKT) -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// -// Nuked OPL3 emulator. -// Thanks: -// MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): -// Feedback and Rhythm part calculation information. -// forums.submarine.org.uk(carbon14, opl3): -// Tremolo and phase generator calculation information. -// OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): -// OPL2 ROMs. -// -// version: 1.7.4 -// +/* Nuked OPL3 + * Copyright (C) 2013-2020 Nuke.YKT + * + * This file is part of Nuked OPL3. + * + * Nuked OPL3 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 + * of the License, or (at your option) any later version. + * + * Nuked OPL3 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Nuked OPL3. If not, see . + + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * + * version: 1.8 + */ #ifndef NUKEDOPL_H #define NUKEDOPL_H +#ifdef __cplusplus +extern "C" { +#endif + #include +#ifndef OPL_ENABLE_STEREOEXT +#define OPL_ENABLE_STEREOEXT 0 +#endif + #define OPL_WRITEBUF_SIZE 1024 #define OPL_WRITEBUF_DELAY 2 -typedef uintptr_t Bitu; -typedef intptr_t Bits; -typedef uint64_t Bit64u; -typedef int64_t Bit64s; -typedef uint32_t Bit32u; -typedef int32_t Bit32s; -typedef uint16_t Bit16u; -typedef int16_t Bit16s; -typedef uint8_t Bit8u; -typedef int8_t Bit8s; - typedef struct _opl3_slot opl3_slot; typedef struct _opl3_channel opl3_channel; typedef struct _opl3_chip opl3_chip; @@ -53,86 +53,120 @@ typedef struct _opl3_chip opl3_chip; struct _opl3_slot { opl3_channel *channel; opl3_chip *chip; - Bit16s out; - Bit16s fbmod; - Bit16s *mod; - Bit16s prout; - Bit16s eg_rout; - Bit16s eg_out; - Bit8u eg_inc; - Bit8u eg_gen; - Bit8u eg_rate; - Bit8u eg_ksl; - Bit8u *trem; - Bit8u reg_vib; - Bit8u reg_type; - Bit8u reg_ksr; - Bit8u reg_mult; - Bit8u reg_ksl; - Bit8u reg_tl; - Bit8u reg_ar; - Bit8u reg_dr; - Bit8u reg_sl; - Bit8u reg_rr; - Bit8u reg_wf; - Bit8u key; - Bit32u pg_phase; - Bit32u timer; + int16_t out; + int16_t fbmod; + int16_t *mod; + int16_t prout; + uint16_t eg_rout; + uint16_t eg_out; + uint8_t eg_inc; + uint8_t eg_gen; + uint8_t eg_rate; + uint8_t eg_ksl; + uint8_t *trem; + uint8_t reg_vib; + uint8_t reg_type; + uint8_t reg_ksr; + uint8_t reg_mult; + uint8_t reg_ksl; + uint8_t reg_tl; + uint8_t reg_ar; + uint8_t reg_dr; + uint8_t reg_sl; + uint8_t reg_rr; + uint8_t reg_wf; + uint8_t key; + uint32_t pg_reset; + uint32_t pg_phase; + uint16_t pg_phase_out; + uint8_t slot_num; }; struct _opl3_channel { - opl3_slot *slots[2]; + opl3_slot *slotz[2];/*Don't use "slots" keyword to avoid conflict with Qt applications*/ opl3_channel *pair; opl3_chip *chip; - Bit16s *out[4]; - Bit8u chtype; - Bit16u f_num; - Bit8u block; - Bit8u fb; - Bit8u con; - Bit8u alg; - Bit8u ksv; - Bit16u cha, chb; + int16_t *out[4]; + +#if OPL_ENABLE_STEREOEXT + int32_t leftpan; + int32_t rightpan; +#endif + + uint8_t chtype; + uint16_t f_num; + uint8_t block; + uint8_t fb; + uint8_t con; + uint8_t alg; + uint8_t ksv; + uint16_t cha, chb; + uint16_t chc, chd; + uint8_t ch_num; }; typedef struct _opl3_writebuf { - Bit64u time; - Bit16u reg; - Bit8u data; + uint64_t time; + uint16_t reg; + uint8_t data; } opl3_writebuf; struct _opl3_chip { opl3_channel channel[18]; opl3_slot slot[36]; - Bit16u timer; - Bit8u newm; - Bit8u nts; - Bit8u rhy; - Bit8u vibpos; - Bit8u vibshift; - Bit8u tremolo; - Bit8u tremolopos; - Bit8u tremoloshift; - Bit32u noise; - Bit16s zeromod; - Bit32s mixbuff[2]; - //OPL3L - Bit32s rateratio; - Bit32s samplecnt; - Bit16s oldsamples[2]; - Bit16s samples[2]; - - Bit64u writebuf_samplecnt; - Bit32u writebuf_cur; - Bit32u writebuf_last; - Bit64u writebuf_lasttime; + uint16_t timer; + uint64_t eg_timer; + uint8_t eg_timerrem; + uint8_t eg_state; + uint8_t eg_add; + uint8_t newm; + uint8_t nts; + uint8_t rhy; + uint8_t vibpos; + uint8_t vibshift; + uint8_t tremolo; + uint8_t tremolopos; + uint8_t tremoloshift; + uint32_t noise; + int16_t zeromod; + int32_t mixbuff[4]; + uint8_t rm_hh_bit2; + uint8_t rm_hh_bit3; + uint8_t rm_hh_bit7; + uint8_t rm_hh_bit8; + uint8_t rm_tc_bit3; + uint8_t rm_tc_bit5; + +#if OPL_ENABLE_STEREOEXT + uint8_t stereoext; +#endif + + /* OPL3L */ + int32_t rateratio; + int32_t samplecnt; + int16_t oldsamples[4]; + int16_t samples[4]; + + uint64_t writebuf_samplecnt; + uint32_t writebuf_cur; + uint32_t writebuf_last; + uint64_t writebuf_lasttime; opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; }; -void OPL3_Generate(opl3_chip *chip, Bit16s *buf); -void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf); -void OPL3_Reset(opl3_chip *chip, Bit32u samplerate); -void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v); -void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v); -void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); +void OPL3_Generate(opl3_chip *chip, int16_t *buf); +void OPL3_GenerateResampled(opl3_chip *chip, int16_t *buf); +void OPL3_Reset(opl3_chip *chip, uint32_t samplerate); +void OPL3_WriteReg(opl3_chip *chip, uint16_t reg, uint8_t v); +void OPL3_WriteRegBuffered(opl3_chip *chip, uint16_t reg, uint8_t v); +void OPL3_GenerateStream(opl3_chip *chip, int16_t *sndptr, uint32_t numsamples); + +void OPL3_Generate4Ch(opl3_chip *chip, int16_t *buf4); +void OPL3_Generate4ChResampled(opl3_chip *chip, int16_t *buf4); +void OPL3_Generate4ChStream(opl3_chip *chip, int16_t *sndptr1, int16_t *sndptr2, uint32_t numsamples); + +#ifdef __cplusplus +} +#endif + #endif diff --git a/plugins/adplug/adplug/opl.h b/plugins/adplug/adplug/opl.h index 0a4eac5597..d3e61d50fb 100644 --- a/plugins/adplug/adplug/opl.h +++ b/plugins/adplug/adplug/opl.h @@ -41,7 +41,7 @@ class Copl virtual void write(int reg, int val) = 0; // combined register select + data write virtual void setchip(int n) // select OPL chip { - if(n < 2) + if(n >= 0 && n < 2) currChip = n; } @@ -60,6 +60,9 @@ class Copl // Emulation only: fill buffer virtual void update(short *buf, int samples) {} + + // Set surroundopl offset + virtual void set_offset(double offset) {} protected: int currChip; // currently selected OPL chip number diff --git a/plugins/adplug/adplug/pch.h b/plugins/adplug/adplug/pch.h new file mode 100644 index 0000000000..9436d8d8e5 --- /dev/null +++ b/plugins/adplug/adplug/pch.h @@ -0,0 +1,11 @@ +/* This file is here the sole purpose of CMake precompiled header feature */ +#include +#include + +#include + +#include "player.h" + +#ifdef DEBUG +#include "debug.h" +#endif diff --git a/plugins/adplug/adplug/pis.cpp b/plugins/adplug/adplug/pis.cpp new file mode 100644 index 0000000000..a732de0e71 --- /dev/null +++ b/plugins/adplug/adplug/pis.cpp @@ -0,0 +1,697 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2023 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * pis.cpp - PIS Player (Beni Tracker) by Jonas Santoso + * adapted from 'pisplay' by Dmitry Smagin + * + * REFERENCES: + * https://github.com/klubderkluebe/pisplay/ + * + */ + +#include "pis.h" +#include + +#define PIS_NONE -1 + +#define OPL_NOTE_FREQUENCY_LO_B 0x143 +#define OPL_NOTE_FREQUENCY_LO_C 0x157 +#define OPL_NOTE_FREQUENCY_HI_B 0x287 +#define OPL_NOTE_FREQUENCY_HI_C 0x2ae + +#define PIS_DEFAULT_SPEED 6 + +#define replay_reset_voice(v) replay_set_voice_volatiles(v, 0, 0, 0); +#define EFFECT_HI(r) ((r)->effect >> 8) +#define EFFECT_LO(r) ((r)->effect & 0xff) +#define EFFECT_MIDNIB(r) (((r)->effect >> 4) & 15) +#define EFFECT_LONIB(r) ((r)->effect & 15) +#define HAS_NOTE(r) ((r)->note < 12) +#define HAS_INSTRUMENT(r) ((r)->instrument > 0) +#define IS_NOTE(n) (n < 12) + +const int CpisPlayer::opl_voice_offset_into_registers[9] = { + 0,1,2,8,9,10,16,17,18 +}; + +const int CpisPlayer::frequency_table[12] = { + 0x157,0x16B,0x181,0x198,0x1B0,0x1CA, + 0x1E5,0x202,0x220,0x241,0x263,0x287 +}; + + +/*** private methods *************************************/ + +void CpisPlayer::init_replay_state(PisReplayState *pstate) { + memset(pstate, 0, sizeof(PisReplayState)); + + pstate->speed = PIS_DEFAULT_SPEED; + pstate->count = PIS_DEFAULT_SPEED - 1; + pstate->position_jump = PIS_NONE; + pstate->pattern_break = PIS_NONE; + + for (int i = 0; i < 9; i++) { + pstate->voice_state[i].instrument = PIS_NONE; + } +} + +void CpisPlayer::replay_frame_routine() { + if (is_playing) { + replay_state.count++; + if (replay_state.count >= replay_state.speed) { + + unpack_row(); + + for (int v = 0; v < 9; v++) { + replay_voice(v); + } + + advance_row(); + } else { + replay_do_per_frame_effects(); + } + } +} + +void CpisPlayer::replay_voice(int v) { + PisVoiceState *vs = &replay_state.voice_state[v]; + PisRowUnpacked r = replay_state.row_buffer[v]; + + if (EFFECT_HI(&r) == 0x03) { + // + // With portamento + // + replay_enter_row_with_portamento(v, vs, &r); + } else { + if (HAS_INSTRUMENT(&r)) { + if (HAS_NOTE(&r)) { + // + // Instrument + note + // + replay_enter_row_with_instrument_and_note(v, vs, &r); + } else { + // + // Instrument only + // + replay_enter_row_with_instrument_only(v, vs, &r); + } + } else { + if (HAS_NOTE(&r)) { + // + // Note only + // + replay_enter_row_with_note_only(v, vs, &r); + } else { + // + // Possibly effect only + // + replay_enter_row_with_possibly_effect_only(v, vs, &r); + } + } + } + + replay_handle_effect(v, vs, &r); + + if (r.effect) { + vs->previous_effect = r.effect; + } else { + vs->previous_effect = PIS_NONE; + replay_reset_voice(v); + } +} + +void CpisPlayer::replay_enter_row_with_portamento(int v, PisVoiceState *vs, PisRowUnpacked *r) { + if (HAS_INSTRUMENT(r)) { + replay_set_instrument(v, r->instrument); + if (vs->volume < 63) { + replay_set_level(v, r->instrument, PIS_NONE, 0); + } + } + if (HAS_NOTE(r)) { + vs->porta_src_freq = vs->frequency; + vs->porta_src_octave = vs->octave; + vs->porta_dest_freq = frequency_table[r->note]; + vs->porta_dest_octave = r->octave; + + if (vs->porta_dest_octave < vs->octave) { + vs->porta_sign = -1; + } else if (vs->porta_dest_octave > vs->octave) { + vs->porta_sign = 1; + } else { + vs->porta_sign = (vs->porta_dest_freq < vs->frequency) ? -1 : 1; + } + } +} + +void CpisPlayer::replay_enter_row_with_instrument_and_note(int v, PisVoiceState *vs, PisRowUnpacked *r) { + vs->previous_effect = PIS_NONE; + + opl_note_off(v); + if (EFFECT_HI(r) != 0x0c) { + // + // Volume is not set + // + if (r->instrument != vs->instrument) { + // + // Is new instrument + // + replay_set_instrument(v, r->instrument); + } else if (vs->volume < 63) { + replay_set_level(v, r->instrument, PIS_NONE, 0); + } + + } else { + // + // Volume is set + // + if (r->instrument != vs->instrument) { + // + // Is new instrument + // + replay_set_instrument(v, r->instrument); + } + replay_set_level(v, r->instrument, EFFECT_LO(r), 1); + } + // + // Trigger new note + // + replay_set_note(v, vs, r); +} + +void CpisPlayer::replay_enter_row_with_instrument_only(int v, PisVoiceState *vs, PisRowUnpacked *r) { + + if (r->instrument != vs->instrument) { + // + // Is new instrument + // + replay_set_instrument(v, r->instrument); + + // + // Set operator level according to instrument and possibly Cxx effect + // + if (EFFECT_HI(r) == 0x0c) { + replay_set_level(v, r->instrument, EFFECT_LO(r), 1); + } else if (vs->volume < 63) { + replay_set_level(v, r->instrument, PIS_NONE, 0); + } + + if ((vs->previous_effect != PIS_NONE) && ((vs->previous_effect & 0xF00) == 0)) { + // + // Reset to base tone after arpeggio + // + opl_set_pitch(v, vs->frequency, vs->octave); + } + } +} + +void CpisPlayer::replay_enter_row_with_note_only(int v, PisVoiceState *vs, PisRowUnpacked *r) { + vs->previous_effect = PIS_NONE; + + if (vs->instrument != PIS_NONE) { + // + // Set operator level according to instrument and possibly Cxx effect + // + if (EFFECT_HI(r) == 0x0c) { + replay_set_level(v, vs->instrument, EFFECT_LO(r), 1); + } else if (vs->volume < 63) { + replay_set_level(v, vs->instrument, PIS_NONE, 0); + } + } + // + // Trigger new note + // + replay_set_note(v, vs, r); +} + +void CpisPlayer::replay_enter_row_with_possibly_effect_only(int v, PisVoiceState *vs, PisRowUnpacked *r) { + // + // Set operator level according to instrument and Cxx effect + // + if (vs->instrument != PIS_NONE && EFFECT_HI(r) == 0x0c) { + replay_set_level(v, vs->instrument, EFFECT_LO(r), 1); + } + + if ((vs->previous_effect != PIS_NONE) && ((vs->previous_effect & 0xF00) == 0)) { + // + // Reset to base tone after arpeggio + // + opl_set_pitch(v, vs->frequency, vs->octave); + } +} + +void CpisPlayer::replay_handle_effect(int v, PisVoiceState *vs, PisRowUnpacked *r) { + int effect_hi = EFFECT_HI(r); + switch (effect_hi) { + case 0x00: // arpeggio + if (EFFECT_LO(r)) { + replay_handle_arpeggio(v, vs, r); + } else { + vs->arpeggio_flag = 0; + } + break; + case 0x01: // slide up + vs->slide_increment = EFFECT_LO(r); + break; + case 0x02: // slide down + vs->slide_increment = -EFFECT_LO(r); + break; + case 0x03: // tone portamento + replay_set_voice_volatiles(v, 0, 0, EFFECT_LO(r)); + break; + case 0x0b: // position jump + replay_handle_posjmp(v, r); + break; + case 0x0d: // pattern break + replay_handle_ptnbreak(v, r); + break; + case 0x0e: // Exx commands + replay_handle_exx_command(v, vs, r); + break; + case 0x0f: // set speed + replay_handle_speed(v, r); + break; + } +} + +void CpisPlayer::replay_handle_exx_command(int v, PisVoiceState *vs, PisRowUnpacked *r) { + switch (EFFECT_MIDNIB(r)) { + case 0x06: // loop + replay_handle_loop(v, r); + break; + case 0x0a: // volume slide up + case 0x0b: // volume slide down + replay_handle_volume_slide(v, vs, r); + break; + } +} + +void CpisPlayer::replay_handle_loop(int v, PisRowUnpacked *r) { + if (!replay_state.loop_flag) { + // + // Playing for the first time + // + if (EFFECT_LONIB(r) == 0) { + // + // Set loop start row + // + replay_state.loop_start_row = replay_state.row; + } else { + // + // Initialize loop counter + // + replay_state.loop_count = EFFECT_LONIB(r); + replay_state.loop_flag = 1; + } + } + + if ((replay_state.loop_flag) && (EFFECT_LONIB(r))) { + // + // Repeating + // + replay_state.loop_count--; + + if (replay_state.loop_count >= 0) { + replay_state.row = replay_state.loop_start_row - 1; + } else { + replay_state.loop_flag = 0; + } + } +} + +void CpisPlayer::replay_handle_volume_slide(int v, PisVoiceState *vs, PisRowUnpacked *r) { + int level; + + if (vs->instrument != PIS_NONE) { + level = (EFFECT_MIDNIB(r) == 0x0a) + ? (vs->volume + EFFECT_LONIB(r)) + : (vs->volume - EFFECT_LONIB(r)); + + if (level < 2) + level = 2; + else if (level > 63) + level = 63; + + replay_set_level(v, vs->instrument, level, 0); + } +} + +void CpisPlayer::replay_do_per_frame_effects() { + + replay_state.arpeggio_index++; + if (replay_state.arpeggio_index == 3) + replay_state.arpeggio_index = 0; + + for (int v = 0; v < 8; v++) { + PisVoiceState *vs = &replay_state.voice_state[v]; + if (vs->slide_increment) { + vs->frequency += vs->slide_increment; + opl_set_pitch(v, vs->frequency, vs->octave); + } else if (vs->porta_increment) { + replay_do_per_frame_portamento(v, vs); + } else if (vs->arpeggio_flag) { + int freq = vs->arpeggio_freq[replay_state.arpeggio_index]; + opl_set_pitch(v, freq, vs->arpeggio_octave[replay_state.arpeggio_index]); + } + } +} + +void CpisPlayer::replay_do_per_frame_portamento(int v, PisVoiceState *vs) { + if (vs->porta_sign == 1) { + vs->frequency += vs->porta_increment; + if ((vs->octave == vs->porta_dest_octave) && (vs->frequency > vs->porta_dest_freq)) { + vs->frequency = vs->porta_dest_freq; + vs->porta_increment = 0; + } + if (vs->frequency > OPL_NOTE_FREQUENCY_HI_B) { + vs->frequency = OPL_NOTE_FREQUENCY_LO_B + (vs->frequency - OPL_NOTE_FREQUENCY_HI_B); + vs->octave++; + } + } else { + vs->frequency -= vs->porta_increment; + if ((vs->octave == vs->porta_dest_octave) && (vs->frequency < vs->porta_dest_freq)) { + vs->frequency = vs->porta_dest_freq; + vs->porta_increment = 0; + } + if (vs->frequency < OPL_NOTE_FREQUENCY_LO_C) { + vs->frequency = OPL_NOTE_FREQUENCY_HI_C - (OPL_NOTE_FREQUENCY_LO_C - vs->frequency); + vs->octave--; + } + } + opl_set_pitch(v, vs->frequency, vs->octave); +} + +void CpisPlayer::replay_handle_arpeggio(int v, PisVoiceState *vs, PisRowUnpacked *r) { + int an1, an2; + if (EFFECT_LO(r) != (vs->previous_effect & 0xff)) { + vs->arpeggio_freq[0] = frequency_table[vs->note]; + vs->arpeggio_octave[0] = vs->octave; + an1 = vs->note + EFFECT_MIDNIB(r); + an2 = vs->note + EFFECT_LONIB(r); + if (IS_NOTE(an1)) { + vs->arpeggio_freq[1] = frequency_table[an1]; + vs->arpeggio_octave[1] = vs->octave; + } else { + vs->arpeggio_freq[1] = frequency_table[an1 - 12]; + vs->arpeggio_octave[1] = vs->octave + 1; + } + if (IS_NOTE(an2)) { + vs->arpeggio_freq[2] = frequency_table[an2]; + vs->arpeggio_octave[2] = vs->octave; + } else { + vs->arpeggio_freq[2] = frequency_table[an2 - 12]; + vs->arpeggio_octave[2] = vs->octave + 1; + } + vs->arpeggio_flag = 1; + } + + vs->slide_increment = 0; + vs->porta_increment = 0; +} + +void CpisPlayer::replay_handle_posjmp(int v, PisRowUnpacked *r) { + replay_reset_voice(v); + replay_state.position_jump = EFFECT_LO(r); +} + +void CpisPlayer::replay_handle_ptnbreak(int v, PisRowUnpacked *r) { + replay_reset_voice(v); + replay_state.pattern_break = EFFECT_LO(r); +} + +void CpisPlayer::replay_handle_speed(int v, PisRowUnpacked *r) { + replay_reset_voice(v); + if (EFFECT_LO(r)) { + replay_state.speed = EFFECT_LO(r); + } else { + is_playing = 0; + } +} + +void CpisPlayer::replay_set_note(int v, PisVoiceState *vs, PisRowUnpacked *r) { + int frequency = frequency_table[r->note]; + opl_set_pitch(v, frequency, r->octave); + vs->note = r->note; + vs->octave = r->octave; + vs->frequency = frequency; +} + +void CpisPlayer::replay_set_instrument(int v, int instr_index) { + PisInstrument *pinstr = &module.instrument[instr_index]; + opl_set_instrument(v, pinstr); + replay_state.voice_state[v].instrument = instr_index; +} + +void CpisPlayer::replay_set_level(int v, int instr_index, int gain, int do_apply_correction) { + int base, l1, l2; + PisInstrument *instr = &module.instrument[instr_index]; + + base = do_apply_correction + ? 62 + : 64; + + if (gain == PIS_NONE) { + gain = 64; + replay_state.voice_state[v].volume = 63; + } else { + replay_state.voice_state[v].volume = gain; + } + + l1 = base - (gain * (64 - instr->lev1) >> 6); + l2 = base - (gain * (64 - instr->lev2) >> 6); + + oplout(0x40 + opl_voice_offset_into_registers[v], l1); + oplout(0x43 + opl_voice_offset_into_registers[v], l2); +} + +void CpisPlayer::replay_set_voice_volatiles(int v, int arpeggio_flag, int slide_increment, int porta_increment) { + PisVoiceState *vs = &replay_state.voice_state[v]; + vs->arpeggio_flag = arpeggio_flag; + vs->slide_increment = slide_increment; + vs->porta_increment = porta_increment; +} + +void CpisPlayer::unpack_row() { + int pattern_index; + uint32_t *pptn; + uint32_t packed; + uint8_t b1, b2, el; + + for (int v = 0; v < 9; v++) { + pattern_index = module.order[replay_state.position][v]; + pptn = module.pattern[pattern_index]; + packed = pptn[replay_state.row]; + + el = packed & 0xff; + packed >>= 8; + b2 = packed & 0xff; + packed >>= 8; + b1 = packed & 0xff; + + replay_state.row_buffer[v].note = b1 >> 4; + replay_state.row_buffer[v].octave = (b1 >> 1) & 7; + replay_state.row_buffer[v].instrument = ((b1 & 1) << 4) | (b2 >> 4); + replay_state.row_buffer[v].effect = ((b2 & 15) << 8) | el; + } +} + +void CpisPlayer::advance_row() { + if (replay_state.position_jump >= 0) { + replay_state.position = replay_state.position_jump; + is_playing = 0; // Treat pattern jump as stop + + if (replay_state.pattern_break == PIS_NONE) { + // + // Position jump without pattern break + // + replay_state.row = 0; + } else { + // + // Position jump with pattern break + // + replay_state.row = replay_state.pattern_break; + replay_state.pattern_break = PIS_NONE; + } + replay_state.position_jump = PIS_NONE; + } else if (replay_state.pattern_break >= 0) { + // + // Pattern break + // + replay_state.position++; + if (replay_state.position == module.length) { + replay_state.position = 0; + is_playing = 0; + } + replay_state.row = replay_state.pattern_break; + replay_state.pattern_break = PIS_NONE; + } else { + // + // Simple row advance + // + replay_state.row++; + if (replay_state.row == 64) { + replay_state.row = 0; + replay_state.position++; + if (replay_state.position == module.length) { + replay_state.position = 0; + is_playing = 0; + } + } + } + + replay_state.count = 0; +} + +void CpisPlayer::load_module(binistream *f, PisModule *pmodule) { + int i, j; + + memset(pmodule, 0, sizeof(PisModule)); + + pmodule->length = f->readInt(1); + pmodule->number_of_patterns = f->readInt(1); + pmodule->number_of_instruments = f->readInt(1); + + for (i = 0; i < pmodule->number_of_patterns; i++) { + pmodule->pattern_map[i] = f->readInt(1); + } + + for (i = 0; i < pmodule->number_of_instruments; i++) { + pmodule->instrument_map[i] = f->readInt(1); + } + + f->readString((char *)pmodule->order, 9 * pmodule->length); + + for (i = 0; i < pmodule->number_of_patterns; i++) { + j = pmodule->pattern_map[i]; + load_pattern(pmodule->pattern[j], f); + } + + for (i = 0; i < pmodule->number_of_instruments; i++) { + j = pmodule->instrument_map[i]; + load_instrument(&pmodule->instrument[j], f); + } +} + +void CpisPlayer::load_pattern(uint32_t *destination, binistream *f) { + int row; + uint32_t packed; + for (row = 0; row < 64; row++) { + packed = f->readInt(1); + packed <<= 8; + packed |= f->readInt(1); + packed <<= 8; + packed |= f->readInt(1); + destination[row] = packed; + } +} + +void CpisPlayer::load_instrument(PisInstrument *pinstr, binistream *f) { + pinstr->mul1 = f->readInt(1); + pinstr->mul2 = f->readInt(1); + pinstr->lev1 = f->readInt(1); + pinstr->lev2 = f->readInt(1); + pinstr->atd1 = f->readInt(1); + pinstr->atd2 = f->readInt(1); + pinstr->sur1 = f->readInt(1); + pinstr->sur2 = f->readInt(1); + pinstr->wav1 = f->readInt(1); + pinstr->wav2 = f->readInt(1); + pinstr->fbcon = f->readInt(1); +} + +void CpisPlayer::opl_set_pitch(int v, int freq, int octave) { + oplout(0xa0 + v, freq & 0xff); + oplout(0xb0 + v, 0x20 | (octave << 2) | (freq >> 8)); +} + +void CpisPlayer::opl_note_off(int v) { + oplout(0xb0 + v, 0); +} + +void CpisPlayer::opl_set_instrument(int v, PisInstrument *instr) { + int opl_register = 0x20 + opl_voice_offset_into_registers[v]; + oplout(opl_register, instr->mul1); + opl_register += 3; + oplout(opl_register, instr->mul2); + opl_register += 0x1d; + oplout(opl_register, instr->lev1); + opl_register += 3; + oplout(opl_register, instr->lev2); + opl_register += 0x1d; + oplout(opl_register, instr->atd1); + opl_register += 3; + oplout(opl_register, instr->atd2); + opl_register += 0x1d; + oplout(opl_register, instr->sur1); + opl_register += 3; + oplout(opl_register, instr->sur2); + opl_register += 0x5d; + oplout(opl_register, instr->wav1); + opl_register += 3; + oplout(opl_register, instr->wav2); + opl_register += 0x1d; + oplout(0xc0 + v, instr->fbcon); +} + +void CpisPlayer::oplout(int r, int v) { + opl->write(r, v); +} + +/*** public methods *************************************/ + +CPlayer *CpisPlayer::factory(Copl *newopl) { + return new CpisPlayer(newopl); +} + +bool CpisPlayer::load(const std::string &filename, const CFileProvider &fp) { + binistream *f = fp.open(filename); + if (!f) + return false; + + // file validation section + if (!fp.extension(filename, ".pis")) { + fp.close(f); + return false; + } + + load_module(f, &module); + + fp.close(f); + + rewind(0); + is_playing = 1; + + return true; +} + +bool CpisPlayer::update() { + replay_frame_routine(); + + return is_playing; +} + +void CpisPlayer::rewind(int subsong) { + init_replay_state(&replay_state); + + opl->init(); + opl->write(1, 0x20); // enable waveform control + is_playing = 1; +} + +float CpisPlayer::getrefresh() { + return 50.0f; +} diff --git a/plugins/adplug/adplug/pis.h b/plugins/adplug/adplug/pis.h new file mode 100644 index 0000000000..56f64f1f9d --- /dev/null +++ b/plugins/adplug/adplug/pis.h @@ -0,0 +1,153 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2005 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * pis.cpp - PIS Player (Beni Tracker) by Jonas Santoso + * adapted from 'pisplay' by Dmitry Smagin + * + * REFERENCES: + * https://github.com/klubderkluebe/pisplay/ + * + */ + +#include + +#include "player.h" + +class CpisPlayer : public CPlayer { +public: + static CPlayer *factory(Copl *newopl); + + CpisPlayer(Copl *newopl) : CPlayer(newopl){}; + ~CpisPlayer(){}; + + bool load(const std::string &filename, const CFileProvider &fp); + bool update(); + void rewind(int subsong); + float getrefresh(); + + std::string gettype() { return std::string("Beni Tracker PIS module"); }; + +private: + typedef struct { + uint8_t mul1, mul2; // multiplier + uint8_t lev1, lev2; // level + uint8_t atd1, atd2; // attack / decay + uint8_t sur1, sur2; // sustain / release + uint8_t wav1, wav2; // waveform + uint8_t fbcon; // feedback / connection_type + } PisInstrument; + + typedef struct { + uint8_t length; // length of order list + uint8_t number_of_patterns; // # of patterns stored in module + uint8_t number_of_instruments; // # of instruments stored in module + uint8_t pattern_map[128]; // maps physical to logical pattern + uint8_t instrument_map[32]; // maps physical to logical instrument + uint8_t order[256][9]; // order list for each channel + uint32_t pattern[128][64]; // pattern data + PisInstrument instrument[64]; // instrument data + } PisModule; + + typedef struct { + int note; + int octave; + int instrument; + int effect; + } PisRowUnpacked; + + typedef struct { + int instrument; + int volume; + int note; + int frequency; + int octave; + int previous_effect; + int slide_increment; + int porta_increment; + int porta_src_freq; + int porta_src_octave; + int porta_dest_freq; + int porta_dest_octave; + int porta_sign; + int arpeggio_flag; + int arpeggio_freq[3]; + int arpeggio_octave[3]; + } PisVoiceState; + + typedef struct { + int speed; + int count; + int position; + int row; + int position_jump; + int pattern_break; + int arpeggio_index; + int loop_flag; + int loop_start_row; + int loop_count; + PisVoiceState voice_state[9]; + PisRowUnpacked row_buffer[9]; + } PisReplayState; + + // Player control + void pisplay_init(); + void pisplay_shutdown(); + void pisplay_load_and_play(const char *path); + void load_module(binistream *f, PisModule *module); + void load_pattern(uint32_t *destination, binistream *f); + void load_instrument(PisInstrument *pinstr, binistream *f); + + // Replay routine + void init_replay_state(PisReplayState *pstate); + void replay_frame_routine(); + void replay_voice(int); + void unpack_row(); + void advance_row(); + void replay_enter_row_with_portamento(int v, PisVoiceState *vs, PisRowUnpacked *r); + void replay_enter_row_with_instrument_and_note(int v, PisVoiceState *vs, PisRowUnpacked *r); + void replay_enter_row_with_instrument_only(int v, PisVoiceState *vs, PisRowUnpacked *r); + void replay_enter_row_with_note_only(int v, PisVoiceState *vs, PisRowUnpacked *r); + void replay_enter_row_with_possibly_effect_only(int v, PisVoiceState *vs, PisRowUnpacked *r); + void replay_handle_effect(int v, PisVoiceState *vs, PisRowUnpacked *r); + void replay_handle_arpeggio(int v, PisVoiceState *vs, PisRowUnpacked *r); + void replay_handle_posjmp(int v, PisRowUnpacked *r); + void replay_handle_ptnbreak(int v, PisRowUnpacked *r); + void replay_handle_speed(int v, PisRowUnpacked *r); + void replay_handle_exx_command(int v, PisVoiceState *vs, PisRowUnpacked *r); + void replay_handle_loop(int v, PisRowUnpacked *r); + void replay_handle_volume_slide(int v, PisVoiceState *vs, PisRowUnpacked *r); + void replay_do_per_frame_effects(); + void replay_do_per_frame_portamento(int v, PisVoiceState *vs); + void replay_set_note(int v, PisVoiceState *vs, PisRowUnpacked *r); + void replay_set_instrument(int v, int instr_index); + void replay_set_level(int v, int instr_index, int gain, int do_apply_correction); + void replay_set_voice_volatiles(int v, int arpeggio_flag, int slide_increment, int porta_increment); + + void opl_set_pitch(int v, int freq, int octave); + void opl_note_off(int v); + void opl_set_instrument(int v, PisInstrument *instr); + void oplout(int r, int v); + + static const int opl_voice_offset_into_registers[9]; + + static const int frequency_table[12]; + + PisModule module; + PisReplayState replay_state; + int is_playing; +}; diff --git a/plugins/adplug/adplug/player.h b/plugins/adplug/adplug/player.h index 4343cf7a66..5aff188422 100644 --- a/plugins/adplug/adplug/player.h +++ b/plugins/adplug/adplug/player.h @@ -28,6 +28,10 @@ #include "opl.h" #include "database.h" +#if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf _snprintf +#endif + class CPlayer { public: diff --git a/plugins/adplug/adplug/protrack.cpp b/plugins/adplug/adplug/protrack.cpp index 41808e4c0e..d1ca3f704a 100644 --- a/plugins/adplug/adplug/protrack.cpp +++ b/plugins/adplug/adplug/protrack.cpp @@ -66,9 +66,9 @@ bool CmodPlayer::update() unsigned long row; if(!speed) // song full stop - return !songend; + return false; - // effect handling (timer dependant) + // effect handling (timer dependent) for(chan = 0; chan < nchans; chan++) { oplchan = set_opl_chip(chan); @@ -138,20 +138,23 @@ bool CmodPlayer::update() tone_portamento(chan,channel[chan].portainfo); else vibrato(chan,channel[chan].vibinfo1,channel[chan].vibinfo2); - case 10: if(del % 4) // SA2 volume slide - break; + case 10: + if(del % 4) // SA2 volume slide + break; if(info1) vol_up(chan,info1); else vol_down(chan,info2); setvolume(chan); break; - case 14: if(info1 == 3) // retrig note - if(!(del % (info2+1))) - playnote(chan); - break; - case 16: if(del % 4) // AMD volume slide + case 14: + if(info1 == 3) // retrig note + if(!(del % (info2+1))) + playnote(chan); break; + case 16: + if(del % 4) // AMD volume slide + break; if(info1) vol_up_alt(chan,info1); else @@ -243,7 +246,7 @@ bool CmodPlayer::update() if(donote) playnote(chan); - // command handling (row dependant) + // command handling (row dependent) info1 = channel[chan].info1; info2 = channel[chan].info2; if(flags & Decimal) @@ -290,7 +293,11 @@ bool CmodPlayer::update() break; case 11: // position jump - pattbreak = 1; rw = 0; if(info < ord) songend = 1; ord = info; break; + pattbreak = 1; + rw = 0; + if (info <= ord) songend = 1; + ord = info; + break; case 12: // set volume channel[chan].vol1 = info; @@ -303,7 +310,12 @@ bool CmodPlayer::update() break; case 13: // pattern break - if(!pattbreak) { pattbreak = 1; rw = info; ord++; } break; + if (!pattbreak) { + pattbreak = 1; + rw = info < nrows ? info : 0; + ord++; + } + break; case 14: // extended command switch(info1) { @@ -642,7 +654,7 @@ void CmodPlayer::setfreq(unsigned char chan) opl->write(0xa0 + oplchan, channel[chan].freq & 255); if(channel[chan].key) - opl->write(0xb0 + oplchan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2) | 32); + opl->write(0xb0 + oplchan, (((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2)) | 32); else opl->write(0xb0 + oplchan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2)); } @@ -680,22 +692,15 @@ void CmodPlayer::playnote(unsigned char chan) void CmodPlayer::setnote(unsigned char chan, int note) { - if(note > 96) { - if(note == 127) { // key off - channel[chan].key = 0; - setfreq(chan); - return; - } else - note = 96; + if(note == 127) { // key off + channel[chan].key = 0; + setfreq(chan); + return; } + if(note > 96) note = 96; + else if (note < 1) note = 1; - if(note < 13) - channel[chan].freq = notetable[note - 1]; - else - if(note % 12 > 0) - channel[chan].freq = notetable[(note % 12) - 1]; - else - channel[chan].freq = notetable[11]; + channel[chan].freq = notetable[(note - 1) % 12]; channel[chan].oct = (note - 1) / 12; channel[chan].freq += inst[channel[chan].inst].slide; // apply pre-slide } diff --git a/plugins/adplug/adplug/psi.cpp b/plugins/adplug/adplug/psi.cpp index 2a4c7393a7..5c0c6a0b21 100644 --- a/plugins/adplug/adplug/psi.cpp +++ b/plugins/adplug/adplug/psi.cpp @@ -38,126 +38,133 @@ #include "psi.h" #include "debug.h" -const unsigned char CxadpsiPlayer::psi_adlib_registers[99] = +static unsigned short le16(const unsigned char *p) { - 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xE0, 0xE3, 0xC0, - 0x21, 0x24, 0x41, 0x44, 0x61, 0x64, 0x81, 0x84, 0xE1, 0xE4, 0xC1, - 0x22, 0x25, 0x42, 0x45, 0x62, 0x65, 0x82, 0x85, 0xE2, 0xE5, 0xC2, - 0x28, 0x2B, 0x48, 0x4B, 0x68, 0x6B, 0x88, 0x8B, 0xE8, 0xEB, 0xC3, - 0x29, 0x2C, 0x49, 0x4C, 0x69, 0x6C, 0x89, 0x8C, 0xE9, 0xEC, 0xC4, - 0x2A, 0x2D, 0x4A, 0x4D, 0x6A, 0x6D, 0x8A, 0x8D, 0xEA, 0xED, 0xC5, - 0x30, 0x33, 0x50, 0x53, 0x70, 0x73, 0x90, 0x93, 0xF0, 0xF3, 0xC6, - 0x31, 0x34, 0x51, 0x54, 0x71, 0x74, 0x91, 0x94, 0xF1, 0xF4, 0xC7, - 0x32, 0x35, 0x52, 0x55, 0x72, 0x75, 0x92, 0x95, 0xF2, 0xF5, 0xC8 -}; - -const unsigned short CxadpsiPlayer::psi_notes[16] = -{ - 0x216B, 0x2181, 0x2198, 0x21B0, 0x21CA, 0x21E5, 0x2202, 0x2220, - 0x2241, 0x2263, 0x2287, 0x2364, - 0x0000, 0x0000, 0x0000, 0x0000 // by riven -}; + return p[0] | (p[1] << 8); +} CPlayer *CxadpsiPlayer::factory(Copl *newopl) { return new CxadpsiPlayer(newopl); } -void CxadpsiPlayer::xadplayer_rewind(int subsong) +bool CxadpsiPlayer::xadplayer_load() { - opl_write(0x01, 0x20); - opl_write(0x08, 0x00); - opl_write(0xBD, 0x00); + if (xad.fmt != PSI || tune_size < 4) + return false; // get header - header.instr_ptr = (tune[1] << 8) + tune[0]; - header.seq_ptr = (tune[3] << 8) + tune[2]; + header.instr_ptr = le16(&tune[0]); + header.seq_ptr = le16(&tune[2]); - // define instruments + if (header.instr_ptr + (unsigned)psi.nchannels * 2 >= tune_size || + header.seq_ptr + (unsigned)psi.nchannels * 4 >= tune_size) + return false; + + // calculate instruments & sequence tables psi.instr_table = &tune[header.instr_ptr]; + psi.seq_table = &tune[header.seq_ptr]; - for(int i=0; i<8; i++) - { - for(int j=0; j<11; j++) { - unsigned short inspos = (psi.instr_table[i * 2 + 1] << 8) + psi.instr_table[i * 2]; + // validate instrument data & sequence pointers + for (int i = 0; i < psi.nchannels * 2; i += 2) + if ((unsigned int)le16(&psi.instr_table[i]) + 11 >= tune_size) + return false; + for (int i = 0; i < psi.nchannels * 4; i += 2) + if (le16(&psi.seq_table[i]) >= tune_size) + return false; - opl_write(psi_adlib_registers[i*11 + j],tune[inspos + j]); - } + return true; +} - opl_write(0xA0+i, 0x00); - opl_write(0xB0+i, 0x00); +void CxadpsiPlayer::xadplayer_rewind(int subsong) +{ + static const unsigned char adlib_registers[][11] = { // last row unused + { 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83, 0xE0, 0xE3, 0xC0 }, + { 0x21, 0x24, 0x41, 0x44, 0x61, 0x64, 0x81, 0x84, 0xE1, 0xE4, 0xC1 }, + { 0x22, 0x25, 0x42, 0x45, 0x62, 0x65, 0x82, 0x85, 0xE2, 0xE5, 0xC2 }, + { 0x28, 0x2B, 0x48, 0x4B, 0x68, 0x6B, 0x88, 0x8B, 0xE8, 0xEB, 0xC3 }, + { 0x29, 0x2C, 0x49, 0x4C, 0x69, 0x6C, 0x89, 0x8C, 0xE9, 0xEC, 0xC4 }, + { 0x2A, 0x2D, 0x4A, 0x4D, 0x6A, 0x6D, 0x8A, 0x8D, 0xEA, 0xED, 0xC5 }, + { 0x30, 0x33, 0x50, 0x53, 0x70, 0x73, 0x90, 0x93, 0xF0, 0xF3, 0xC6 }, + { 0x31, 0x34, 0x51, 0x54, 0x71, 0x74, 0x91, 0x94, 0xF1, 0xF4, 0xC7 }, + { 0x32, 0x35, 0x52, 0x55, 0x72, 0x75, 0x92, 0x95, 0xF2, 0xF5, 0xC8 }, + }; + + opl_write(0x01, 0x20); + opl_write(0x08, 0x00); + opl_write(0xBD, 0x00); + + for (int i = 0; i < psi.nchannels; i++) { + // define instruments + unsigned short inspos = le16(&psi.instr_table[i * 2]); + for (int j = 0; j < 11; j++) + opl_write(adlib_registers[i][j], tune[inspos + j]); + + opl_write(0xA0 + i, 0x00); + opl_write(0xB0 + i, 0x00); + + // reset sequence pointers + psi.ptr[i] = le16(&psi.seq_table[i * 4]); psi.note_delay[i] = 1; psi.note_curdelay[i] = 1; - psi.looping[i] = 0; } - - // calculate sequence pointer - psi.seq_table = &tune[header.seq_ptr]; + psi.looping = 0; } void CxadpsiPlayer::xadplayer_update() { - unsigned short ptr; - - for(int i=0; i<8; i++) - { - ptr = (psi.seq_table[(i<<1) * 2 + 1] << 8) + psi.seq_table[(i<<1) * 2]; + for (int i = 0; i < psi.nchannels; i++) { + // time up for last event? + if (--psi.note_curdelay[i]) continue; - psi.note_curdelay[i]--; + opl_write(0xA0 + i, 0x00); + opl_write(0xB0 + i, 0x00); - if (!psi.note_curdelay[i]) - { - opl_write(0xA0+i, 0x00); - opl_write(0xB0+i, 0x00); - - unsigned char event = tune[ptr++]; + unsigned short &ptr = psi.ptr[i]; + unsigned char event = ptr < tune_size ? tune[ptr++] : 0; #ifdef DEBUG - AdPlug_LogWrite("channel %02X, event %02X:\n",i+1,event); + AdPlug_LogWrite("channel %02X, event %02X:\n", i+1, event); #endif - // end of sequence ? - if (!event) - { - ptr = (psi.seq_table[(i<<1) * 2 + 3] << 8) + psi.seq_table[(i<<1) * 2 + 2]; - - event = tune[ptr++]; + // end of sequence? + if (!event) { + ptr = le16(&psi.seq_table[i * 4 + 2]); + event = tune[ptr++]; // loop position is validated #ifdef DEBUG - AdPlug_LogWrite(" channel %02X, event %02X:\n",i+1,event); + AdPlug_LogWrite(" channel %02X, event %02X:\n", i+1, event); #endif - // set sequence loop flag - psi.looping[i] = 1; - - // module loop ? - plr.looping = 1; - for(int j=0; j<8; j++) - plr.looping &= psi.looping[j]; - } + // set sequence loop flag + psi.looping |= 1 << i; + // module loop (all flags set)? + plr.looping = psi.looping == (1 << psi.nchannels) - 1; + } - // new note delay ? - if (event & 0x80) - { - psi.note_delay[i] = (event & 0x7F); + // new note delay? + if (event & 0x80) { + psi.note_delay[i] = event & 0x7F; - event = tune[ptr++]; + event = ptr < tune_size ? tune[ptr++] : 0; #ifdef DEBUG - AdPlug_LogWrite(" channel %02X, event %02X:\n",i+1,event); + AdPlug_LogWrite(" channel %02X, event %02X:\n", i+1, event); #endif - } - - psi.note_curdelay[i] = psi.note_delay[i]; + } - // play note - unsigned short note = psi_notes[event & 0x0F]; + psi.note_curdelay[i] = psi.note_delay[i]; - opl_write(0xA0+i, note & 0xFF); - opl_write(0xB0+i, (note >> 8) + ((event >> 2) & 0xFC)); + // play note + struct Note { unsigned char hi, lo; }; + static const Note notes[16] = { + { 0x21, 0x6B }, { 0x21, 0x81 }, { 0x21, 0x98 }, { 0x21, 0xB0 }, + { 0x21, 0xCA }, { 0x21, 0xE5 }, { 0x22, 0x02 }, { 0x22, 0x20 }, + { 0x22, 0x41 }, { 0x22, 0x63 }, { 0x22, 0x87 }, { 0x23, 0x64 }, + // rest is zero + }; + Note note = notes[event & 0x0F]; - // save position - psi.seq_table[(i<<1) * 2] = ptr & 0xff; - psi.seq_table[(i<<1) * 2 + 1] = ptr >> 8; - } + opl_write(0xA0 + i, note.lo); + opl_write(0xB0 + i, note.hi + ((event & 0xF0) >> 2)); } } @@ -173,5 +180,5 @@ std::string CxadpsiPlayer::xadplayer_gettype() unsigned int CxadpsiPlayer::xadplayer_getinstruments() { - return 8; + return psi.nchannels; } diff --git a/plugins/adplug/adplug/psi.h b/plugins/adplug/adplug/psi.h index df149ac181..98e0c4d64b 100644 --- a/plugins/adplug/adplug/psi.h +++ b/plugins/adplug/adplug/psi.h @@ -38,27 +38,19 @@ class CxadpsiPlayer: public CxadPlayer struct { + enum { nchannels = 8 }; unsigned char *instr_table; unsigned char *seq_table; - unsigned char note_delay[9]; - unsigned char note_curdelay[9]; - unsigned char looping[9]; + unsigned char note_delay[nchannels]; + unsigned char note_curdelay[nchannels]; + unsigned short looping; + unsigned short ptr[nchannels]; } psi; // - bool xadplayer_load() - { - if(xad.fmt == PSI) - return true; - else - return false; - } + bool xadplayer_load(); void xadplayer_rewind(int subsong); void xadplayer_update(); float xadplayer_getrefresh(); std::string xadplayer_gettype(); unsigned int xadplayer_getinstruments(); - -private: - static const unsigned char psi_adlib_registers[99]; - static const unsigned short psi_notes[16]; }; diff --git a/plugins/adplug/adplug/rad.cpp b/plugins/adplug/adplug/rad.cpp deleted file mode 100644 index 1f240582f3..0000000000 --- a/plugins/adplug/adplug/rad.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2007 Simon Peter, , et al. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * rad.cpp - RAD Loader by Simon Peter - * - * BUGS: - * some volumes are dropped out - */ - -#include -#include "rad.h" - -CPlayer *CradLoader::factory(Copl *newopl) -{ - return new CradLoader(newopl); -} - -bool CradLoader::load(const std::string &filename, const CFileProvider &fp) -{ - binistream *f = fp.open(filename); if(!f) return false; - char id[16]; - unsigned char buf,ch,c,b,inp; - char bufstr[2] = "\0"; - unsigned int i,j; - unsigned short patofs[32]; - const unsigned char convfx[16] = {255,1,2,3,255,5,255,255,255,255,20,255,17,0xd,255,19}; - - // file validation section - f->readString(id, 16); version = f->readInt(1); - if(strncmp(id,"RAD by REALiTY!!",16) || version != 0x10) - { fp.close(f); return false; } - - // load section - radflags = f->readInt(1); - if(radflags & 128) { // description - memset(desc,0,80*22); - while((buf = f->readInt(1))) - if(buf == 1) - strcat(desc,"\n"); - else - if(buf >= 2 && buf <= 0x1f) - for(i=0;ireadInt(1))) { // instruments - buf--; - inst[buf].data[2] = f->readInt(1); inst[buf].data[1] = f->readInt(1); - inst[buf].data[10] = f->readInt(1); inst[buf].data[9] = f->readInt(1); - inst[buf].data[4] = f->readInt(1); inst[buf].data[3] = f->readInt(1); - inst[buf].data[6] = f->readInt(1); inst[buf].data[5] = f->readInt(1); - inst[buf].data[0] = f->readInt(1); - inst[buf].data[8] = f->readInt(1); inst[buf].data[7] = f->readInt(1); - } - length = f->readInt(1); - for(i = 0; i < length; i++) order[i] = f->readInt(1); // orderlist - for(i = 0; i < 32; i++) patofs[i] = f->readInt(2); // pattern offset table - init_trackord(); // patterns - for(i=0;i<32;i++) - if(patofs[i]) { - f->seek(patofs[i]); - do { - buf = f->readInt(1); b = buf & 127; - do { - ch = f->readInt(1); c = ch & 127; - inp = f->readInt(1); - tracks[i*9+c][b].note = inp & 127; - tracks[i*9+c][b].inst = (inp & 128) >> 3; - inp = f->readInt(1); - tracks[i*9+c][b].inst += inp >> 4; - tracks[i*9+c][b].command = inp & 15; - if(inp & 15) { - inp = f->readInt(1); - tracks[i*9+c][b].param1 = inp / 10; - tracks[i*9+c][b].param2 = inp % 10; - } - } while(!(ch & 128)); - } while(!(buf & 128)); - } else - memset(trackord[i],0,9*2); - fp.close(f); - - // convert replay data - for(i=0;i<32*9;i++) // convert patterns - for(j=0;j<64;j++) { - if(tracks[i][j].note == 15) - tracks[i][j].note = 127; - if(tracks[i][j].note > 16 && tracks[i][j].note < 127) - tracks[i][j].note -= 4 * (tracks[i][j].note >> 4); - if(tracks[i][j].note && tracks[i][j].note < 126) - tracks[i][j].note++; - tracks[i][j].command = convfx[tracks[i][j].command]; - } - restartpos = 0; initspeed = radflags & 31; - bpm = radflags & 64 ? 0 : 50; flags = Decimal; - - rewind(0); - return true; -} - -float CradLoader::getrefresh() -{ - if(tempo) - return (float) (tempo); - else - return 18.2f; -} diff --git a/plugins/adplug/adplug/rad2.cpp b/plugins/adplug/adplug/rad2.cpp new file mode 100644 index 0000000000..a087dc2b7e --- /dev/null +++ b/plugins/adplug/adplug/rad2.cpp @@ -0,0 +1,1936 @@ +/* +* Adplug - Replayer for many OPL2/OPL3 audio file formats. +* Copyright (C) 1999 - 2007 Simon Peter, , et al. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* rad2.cpp - RAD v2 replayer class +* +* Aside from the Crad2Player wrapper class, all of the code in this file is based +* directly on the public domain RAD2 replayer source by Shayde/Reality, updated +* to also support the original RAD v1 format. It is otherwise modified as little +* as possible. +*/ + +#include "rad2.h" +#include "debug.h" +#include +#include +#include + +/* + +Code to check a RAD V2 tune file is valid. That is, it will check the tune +can be played without crashing the player. It doesn't exhaustively check +the tune except where needed to prevent a possible player crash. + +Call RADValidate with a pointer to your tune and the size in bytes. It +will return either NULL for all okay, or a pointer to a null-terminated +string describing what is wrong with the data. + +*/ + + +//================================================================================================== +// The error strings are all supplied here in case you want to translate them to another language +// (or supply your own more descriptive error messages). +//================================================================================================== +static const char *g_RADNotATuneFile = "Not a RAD tune file."; +static const char *g_RADNotAVersion21Tune = "Not a version 1.0 or 2.1 file format RAD tune."; +static const char *g_RADTruncated = "Tune file has been truncated and is incomplete."; +static const char *g_RADBadFlags = "Tune file has invalid flags."; +static const char *g_RADBadBPMValue = "Tune's BPM value is out of range."; +static const char *g_RADBadInstrument = "Tune file contains a bad instrument definition."; +static const char *g_RADUnknownMIDIVersion = "Tune file contains an unknown MIDI instrument version."; +static const char *g_RADOrderListTooLarge = "Order list in tune file is an invalid size."; +static const char *g_RADBadJumpMarker = "Order list jump marker is invalid."; +static const char *g_RADBadOrderEntry = "Order list entry is invalid."; +static const char *g_RADBadPattNum = "Tune file contains a bad pattern index."; +static const char *g_RADPattTruncated = "Tune file contains a truncated pattern."; +static const char *g_RADPattExtraData = "Tune file contains a pattern with extraneous data."; +static const char *g_RADPattBadLineNum = "Tune file contains a pattern with a bad line definition."; +static const char *g_RADPattBadChanNum = "Tune file contains a pattern with a bad channel definition."; +static const char *g_RADPattBadNoteNum = "Pattern contains a bad note number."; +static const char *g_RADPattBadInstNum = "Pattern contains a bad instrument number."; +static const char *g_RADPattBadEffect = "Pattern contains a bad effect and/or parameter."; +static const char *g_RADBadRiffNum = "Tune file contains a bad riff index."; +//static const char *g_RADExtraBytes = "Tune file contains extra bytes."; + + + +//================================================================================================== +// Validate a RAD V2 (file format 2.1) tune file. Note, this uses no C++ standard library code. +//================================================================================================== +static const char *RADCheckPattern(const uint8_t *&s, const uint8_t *e, bool riff) { + + // Get pattern size + if (s + 2 > e) + return g_RADTruncated; + uint16_t pattsize = s[0] | (uint16_t(s[1]) << 8); + s += 2; + + // Calculate end of pattern + const uint8_t *pe = s + pattsize; + if (pe > e) + return g_RADTruncated; + + uint8_t linedef, chandef; + do { + + // Check line of pattern + if (s >= pe) + return g_RADPattTruncated; + linedef = *s++; + uint8_t linenum = linedef & 0x7F; + if (linenum >= 64) + return g_RADPattBadLineNum; + + do { + + // Check channel of pattern + if (s >= pe) + return g_RADPattTruncated; + chandef = *s++; + uint8_t channum = chandef & 0x0F; + if (!riff && channum >= 9) + return g_RADPattBadChanNum; + + // Check note + if (chandef & 0x40) { + if (s >= pe) + return g_RADPattTruncated; + uint8_t note = *s++; + uint8_t notenum = note & 15; + //uint8_t octave = (note >> 4) & 7; + if (notenum == 0 || notenum == 13 || notenum == 14) + return g_RADPattBadNoteNum; + } + + // Check instrument. This shouldn't be supplied if bit 7 of the note byte is set, + // but it doesn't break anything if it is so we don't check for it + if (chandef & 0x20) { + if (s >= pe) + return g_RADPattTruncated; + uint8_t inst = *s++; + if (inst == 0 || inst >= 128) + return g_RADPattBadInstNum; + } + + // Check effect. A non-existent effect could be supplied, but it'll just be + // ignored by the player so we don't care + if (chandef & 0x10) { + if (s + 2 > pe) + return g_RADPattTruncated; + uint8_t effect = *s++; + uint8_t param = *s++; + if (effect > 31 || param > 99) + return g_RADPattBadEffect; + } + + } while (!(chandef & 0x80)); + + } while (!(linedef & 0x80)); + + if (s != pe) + return g_RADPattExtraData; + + return 0; +} +//-------------------------------------------------------------------------------------------------- +static const char *RADCheckPatternOld(const uint8_t *&s, const uint8_t *e) { + + if (s > e) + return g_RADTruncated; + + uint8_t linedef, chandef; + do { + + // Check line of pattern + if (s >= e) + return g_RADPattTruncated; + linedef = *s++; + uint8_t linenum = linedef & 0x7F; + if (linenum >= 64) + return g_RADPattBadLineNum; + + do { + + // Check channel of pattern + if (s >= e) + return g_RADPattTruncated; + chandef = *s++; + uint8_t channum = chandef & 0x0F; + if (channum >= 9) + return g_RADPattBadChanNum; + + // Check note + if (s >= e) + return g_RADPattTruncated; + /*uint8_t note = *s++; */ s++; + //uint8_t notenum = note & 15; + //uint8_t octave = (note >> 4) & 7; + /* the replayer handles bad params already and some old tunes do contain them + if (notenum == 13 || notenum == 14) + return g_RADPattBadNoteNum; + */ + + // Check instrument + if (s >= e) + return g_RADPattTruncated; + uint8_t inst = *s++; + + // Check effect + if (inst & 0xf) { + if (s > e) + return g_RADPattTruncated; + /* uint8_t param = *s++; */ s++; + /* the replayer handles bad params already and some old tunes do contain them + if (param > 99) + return g_RADPattBadEffect; + */ + } + + } while (!(chandef & 0x80)); + + } while (!(linedef & 0x80)); + + return 0; +} +//-------------------------------------------------------------------------------------------------- +static const char *RADValidate(const void *data, size_t data_size) { + + const uint8_t *s = (const uint8_t *)data; + const uint8_t *e = s + data_size; + + int Version; + + // Check header + if (data_size < 16) + return g_RADNotATuneFile; + + s += 16; + + // Check version + if (s >= e || (*s != 0x10 && *s != 0x21)) + return g_RADNotAVersion21Tune; + Version = (*s) >> 4; + s++; + + // Check flags + if (s >= e) + return g_RADTruncated; + + uint8_t flags = *s++; + if (Version >= 2 && flags & 0x80) + return g_RADBadFlags; // Bit 7 is unused in v2 + if (Version == 1 && flags & 0x20) + return g_RADBadFlags; // Bit 5 is unused in v1 + + if (Version >= 2 && flags & 0x20) { + if (s + 2 > e) + return g_RADTruncated; + uint16_t bpm = s[0] | (uint16_t(s[1]) << 8); + s += 2; + if (bpm < 46 || bpm > 300) + return g_RADBadBPMValue; + } + + // Check description. This is actually freeform text so there's not a lot to check, just that + // it's a null-terminated string + if (Version >= 2 || flags & 0x80) { + do { + if (s >= e) + return g_RADTruncated; + } while (*s++); + } + + // Check instruments. We don't actually validate the individual instrument fields as the tune + // file will still play with bad instrument data. We're only concerned that the tune file + // doesn't crash the player + uint8_t last_inst = 0; + while (1) { + + // Get instrument number, or 0 for end of instrument list + if (s >= e) + return g_RADTruncated; + uint8_t inst = *s++; + if (inst == 0) + break; + + // RAD always saves the instruments out in order + if (inst > 127 || inst <= last_inst) + return g_RADBadInstrument; + last_inst = inst; + + if (Version >= 2) { + // Check the name + if (s >= e) + return g_RADTruncated; + uint8_t namelen = *s++; + s += namelen; + + // Get algorithm + if (s > e) + return g_RADTruncated; + uint8_t alg = *s; + + if ((alg & 7) == 7) { + + // MIDI instrument. We need to check the version as this can affect the following + // data size + if (s + 7 > e) + return g_RADTruncated; + if (s[2] >> 4) + return g_RADUnknownMIDIVersion; + s += 7; + + } + else { + + s += 24; + if (s > e) + return g_RADTruncated; + } + + // Riff track supplied? + if (alg & 0x80) { + + const char *err = RADCheckPattern(s, e, false); + if (err) + return err; + } + } + else { + // skip version 1 instruments + s += 11; + } + + } + + // Get the order list + if (s >= e) + return g_RADTruncated; + uint8_t order_size = *s++; + const uint8_t *order_list = s; + if (order_size > 128) + return g_RADOrderListTooLarge; + s += order_size; + + for (uint8_t i = 0; i < order_size; i++) { + uint8_t order = order_list[i]; + + if (order & 0x80) { + + // Check jump marker + order &= 0x7F; + if (order >= order_size) + return g_RADBadJumpMarker; + } + else { + + // Check pattern number. It doesn't matter if there is no pattern with this number + // defined later, as missing patterns are treated as empty + if (Version >= 2 && order >= 100) + return g_RADBadOrderEntry; + if (Version == 1 && order >= 32) + return g_RADBadOrderEntry; + } + } + + // Check the patterns + if (Version >= 2) while (1) { + // Version 2 patterns + // Get pattern number + if (s >= e) + return g_RADTruncated; + uint8_t pattnum = *s++; + + // Last pattern? + if (pattnum == 0xFF) + break; + + if (pattnum >= 100) + return g_RADBadPattNum; + + const char *err = RADCheckPattern(s, e, false); + if (err) + return err; + } + else for (int i = 0; i < 32; i++) { + // Version 1 patterns + if (s + 2 > e) + return g_RADTruncated; + + int pos = s[0] | (int(s[1]) << 8); + s += 2; + if (pos) { + const uint8_t *patt = (uint8_t*)data + pos; + const char *err = RADCheckPatternOld(patt, e); + if (err) + return err; + } + } + + // Check the riffs + if (Version >= 2) while (1) { + + // Get riff number + if (s >= e) + return g_RADTruncated; + uint8_t riffnum = *s++; + + // Last riff? + if (riffnum == 0xFF) + break; + + uint8_t riffpatt = riffnum >> 4; + uint8_t riffchan = riffnum & 15; + if (riffpatt > 9 || riffchan == 0 || riffchan > 9) + return g_RADBadRiffNum; + + const char *err = RADCheckPattern(s, e, true); + if (err) + return err; + } + + // Tune file is all good + return 0; +} + +/* + +C++ player code for Reality Adlib Tracker 2.0a (file version 2.1). + +Please note, this is just the player code. This does no checking of the tune data before +it tries to play it, as most use cases will be a known tune being used in a production. +So if you're writing an application that loads unknown tunes in at run time then you'll +want to do more validity checking. + +To use: + +- Instantiate the RADPlayer object + +- Initialise player for your tune by calling the Init() method. Supply a pointer to the +tune file and a function for writing to the OPL3 registers. + +- Call the Update() method a number of times per second as returned by GetHertz(). If +your tune is using the default BPM setting you can safely just call it 50 times a +second, unless it's a legacy "slow-timer" tune then it'll need to be 18.2 times a +second. + +- When you're done, stop calling Update() and call the Stop() method to turn off all +sound and reset the OPL3 hardware. + +*/ + +#ifndef RAD_DETECT_REPEATS +#define RAD_DETECT_REPEATS 1 +#endif + +//================================================================================================== +// RAD player class. +//================================================================================================== +class RADPlayer { + + // Various constants + enum { + kTracks = 100, + kChannels = 9, + kTrackLines = 64, + kRiffTracks = 10, + kInstruments = 127, + + cmPortamentoUp = 0x1, + cmPortamentoDwn = 0x2, + cmToneSlide = 0x3, + cmToneVolSlide = 0x5, + cmVolSlide = 0xA, + cmSetVol = 0xC, + cmJumpToLine = 0xD, + cmSetSpeed = 0xF, + cmIgnore = ('I' - 55), + cmMultiplier = ('M' - 55), + cmRiff = ('R' - 55), + cmTranspose = ('T' - 55), + cmFeedback = ('U' - 55), + cmVolume = ('V' - 55), + }; + + enum e_Source { + SNone, SRiff, SIRiff, + }; + + enum { + fKeyOn = 1 << 0, + fKeyOff = 1 << 1, + fKeyedOn = 1 << 2, + }; + + struct CInstrument { + uint8_t Feedback[2]; + uint8_t Panning[2]; + uint8_t Algorithm; + uint8_t Detune; + uint8_t Volume; + uint8_t RiffSpeed; + uint8_t * Riff; + uint8_t Operators[4][5]; + char Name[256]; + }; + + struct CEffects { + int8_t PortSlide; + int8_t VolSlide; + uint16_t ToneSlideFreq; + uint8_t ToneSlideOct; + uint8_t ToneSlideSpeed; + int8_t ToneSlideDir; + }; + + struct CChannel { + uint8_t LastInstrument; + CInstrument * Instrument; + uint8_t Volume; + uint8_t DetuneA; + uint8_t DetuneB; + uint8_t KeyFlags; + uint16_t CurrFreq; + int8_t CurrOctave; + CEffects FX; + struct CRiff { + CEffects FX; + uint8_t * Track; + uint8_t * TrackStart; + uint8_t Line; + uint8_t Speed; + uint8_t SpeedCnt; + int8_t TransposeOctave; + int8_t TransposeNote; + uint8_t LastInstrument; + } Riff, IRiff; + }; + +public: + RADPlayer() : Initialised(false) {} + void Init(const void *tune, void(*opl3)(void *, uint16_t, uint8_t), void *arg); + void Stop(); + bool Update(); + int GetVersion() const { return Version; } + const uint8_t* GetDescription() const { return Description; } + float GetHertz() const { return Hertz; } + int GetPlayTimeInSeconds() const { return PlayTime / Hertz; } + int GetTunePos() const { return Order; } + int GetTuneLength() const { return OrderListSize; } + int GetTunePattern() const { return OrderList[Order]; } + int GetTunePatterns() const { return NumTracks; } + int GetTuneLine() const { return Line; } + const char* GetTuneInst(uint8_t inst) const { return Instruments[inst].Name; } + int GetTuneInsts() const { return NumInstruments; } + void SetMasterVolume(int vol) { MasterVol = vol; } + int GetMasterVolume() const { return MasterVol; } + int GetSpeed() const { return Speed; } + +#if RAD_DETECT_REPEATS + uint32_t ComputeTotalTime(); +#endif + +private: + bool UnpackNote(uint8_t *&s, uint8_t &last_instrument); + uint8_t * GetTrack(); + uint8_t * SkipToLine(uint8_t *trk, uint8_t linenum, bool chan_riff = false); + void PlayLine(); + void PlayNote(int channum, int8_t notenum, int8_t octave, uint16_t instnum, uint8_t cmd = 0, uint8_t param = 0, e_Source src = SNone, int op = 0); + void LoadInstrumentOPL3(int channum); + void PlayNoteOPL3(int channum, int8_t octave, int8_t note); + void ResetFX(CEffects *fx); + void TickRiff(int channum, CChannel::CRiff &riff, bool chan_riff); + void ContinueFX(int channum, CEffects *fx); + void SetVolume(int channum, uint8_t vol); + void GetSlideDir(int channum, CEffects *fx); + void LoadInstMultiplierOPL3(int channum, int op, uint8_t mult); + void LoadInstVolumeOPL3(int channum, int op, uint8_t vol); + void LoadInstFeedbackOPL3(int channum, int which, uint8_t fb); + void Portamento(uint16_t channum, CEffects *fx, int8_t amount, bool toneslide); + void Transpose(int8_t note, int8_t octave); + void SetOPL3(uint16_t reg, uint8_t val) { + OPL3Regs[reg] = val; + OPL3(OPL3Arg, reg, val); + } + uint8_t GetOPL3(uint16_t reg) const { + return OPL3Regs[reg]; + } + + void(*OPL3)(void *, uint16_t, uint8_t); + void * OPL3Arg; + int Version; + bool UseOPL3; + const uint8_t* Description; + CInstrument Instruments[kInstruments]; + int NumInstruments; + CChannel Channels[kChannels]; + uint32_t PlayTime; +#if RAD_DETECT_REPEATS + uint32_t OrderMap[4]; + bool Repeating; +#endif + float Hertz; + uint8_t * OrderList; + uint8_t * Tracks[kTracks]; + int NumTracks; + uint8_t * Riffs[kRiffTracks][kChannels]; + uint8_t * Track; + bool Initialised; + uint8_t Speed; + uint8_t OrderListSize; + uint8_t SpeedCnt; + uint8_t Order; + uint8_t Line; + int8_t Entrances; + uint8_t MasterVol; + int8_t LineJump; + uint8_t OPL3Regs[512]; + + // Values exported by UnpackNote() + int8_t NoteNum; + int8_t OctaveNum; + uint8_t InstNum; + uint8_t EffectNum; + uint8_t Param; + bool LastNote; + + static const int8_t NoteSize[]; + static const uint16_t ChanOffsets3[9], Chn2Offsets3[9]; + static const uint16_t NoteFreq[]; + static const uint16_t OpOffsets2[9][2]; + static const uint16_t OpOffsets3[9][4]; + static const bool AlgCarriers[7][4]; +}; +//-------------------------------------------------------------------------------------------------- +const int8_t RADPlayer::NoteSize[] = { 0, 2, 1, 3, 1, 3, 2, 4 }; +const uint16_t RADPlayer::ChanOffsets3[9] = { 0, 1, 2, 0x100, 0x101, 0x102, 6, 7, 8 }; // OPL3 first channel +const uint16_t RADPlayer::Chn2Offsets3[9] = { 3, 4, 5, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108 }; // OPL3 second channel +const uint16_t RADPlayer::NoteFreq[] = { 0x16b,0x181,0x198,0x1b0,0x1ca,0x1e5,0x202,0x220,0x241,0x263,0x287,0x2ae }; +const uint16_t RADPlayer::OpOffsets2[9][2] = { + { 0x003, 0x000 }, + { 0x004, 0x001 }, + { 0x005, 0x002 }, + { 0x00B, 0x008 }, + { 0x00C, 0x009 }, + { 0x00D, 0x00A }, + { 0x013, 0x010 }, + { 0x014, 0x011 }, + { 0x015, 0x012 } +}; +const uint16_t RADPlayer::OpOffsets3[9][4] = { + { 0x00B, 0x008, 0x003, 0x000 }, + { 0x00C, 0x009, 0x004, 0x001 }, + { 0x00D, 0x00A, 0x005, 0x002 }, + { 0x10B, 0x108, 0x103, 0x100 }, + { 0x10C, 0x109, 0x104, 0x101 }, + { 0x10D, 0x10A, 0x105, 0x102 }, + { 0x113, 0x110, 0x013, 0x010 }, + { 0x114, 0x111, 0x014, 0x011 }, + { 0x115, 0x112, 0x015, 0x012 } +}; +const bool RADPlayer::AlgCarriers[7][4] = { + { true, false, false, false }, // 0 - 2op - op < op + { true, true, false, false }, // 1 - 2op - op + op + { true, false, false, false }, // 2 - 4op - op < op < op < op + { true, false, false, true }, // 3 - 4op - op < op < op + op + { true, false, true, false }, // 4 - 4op - op < op + op < op + { true, false, true, true }, // 5 - 4op - op < op + op + op + { true, true, true, true }, // 6 - 4op - op + op + op + op +}; + + + +//================================================================================================== +// Initialise a RAD tune for playback. This assumes the tune data is valid and does minimal data +// checking. +//================================================================================================== +void RADPlayer::Init(const void *tune, void(*opl3)(void *, uint16_t, uint8_t), void *arg) { + + Initialised = false; + + // Version check; we only support version 1.0 and 2.1 tune files + uint8_t ver = *((uint8_t *)tune + 0x10); + if (ver != 0x10 && ver != 0x21) { + Hertz = -1; + return; + } + Version = ver >> 4; + UseOPL3 = Version >= 2; + + // The OPL3 call-back + OPL3 = opl3; + OPL3Arg = arg; + + for (int i = 0; i < kTracks; i++) + Tracks[i] = 0; + + for (int i = 0; i < kRiffTracks; i++) + for (int j = 0; j < kChannels; j++) + Riffs[i][j] = 0; + + uint8_t *s = (uint8_t *)tune + 0x11; + + uint8_t flags = *s++; + Speed = flags & 0x1F; + + // Is BPM value present? + Hertz = 50.0f; + if (Version >= 2 && flags & 0x20) { + Hertz = (float)(s[0] | (int(s[1]) << 8)) * 2 / 5; + s += 2; + } + + // Slow timer tune? Return an approximate hz + if (flags & 0x40) + Hertz = 18.2f; + + // Skip any description + Description = 0; + if (Version >= 2 || flags & 0x80) { + Description = s; + while (*s) + s++; + s++; + } + + // Blank the instrument table, some files reference non-existent instruments + memset(&Instruments[0], 0, sizeof(Instruments)); + + // Unpack the instruments + NumInstruments = 0; + while (1) { + + // Instrument number, 0 indicates end of list + uint8_t inst_num = *s++; + if (inst_num == 0) + break; + if (inst_num > NumInstruments) + NumInstruments = inst_num; + + CInstrument &inst = Instruments[inst_num - 1]; + + if (Version >= 2) { + // Version 2 instrument + // Read instrument name + uint8_t inst_namelen = *s++; + for (int i = 0; i < inst_namelen; i++) + inst.Name[i] = *s++; + inst.Name[inst_namelen] = '\0'; + + uint8_t alg = *s++; + inst.Algorithm = alg & 7; + inst.Panning[0] = (alg >> 3) & 3; + inst.Panning[1] = (alg >> 5) & 3; + + if (inst.Algorithm < 7) { + + uint8_t b = *s++; + inst.Feedback[0] = b & 15; + inst.Feedback[1] = b >> 4; + + b = *s++; + inst.Detune = b >> 4; + inst.RiffSpeed = b & 15; + + inst.Volume = *s++; + + for (int i = 0; i < 4; i++) { + uint8_t *op = inst.Operators[i]; + for (int j = 0; j < 5; j++) + op[j] = *s++; + } + + } + else { + + // Ignore MIDI instrument data + s += 6; + } + + // Instrument riff? + if (alg & 0x80) { + int size = s[0] | (int(s[1]) << 8); + s += 2; + inst.Riff = s; + s += size; + } + else + inst.Riff = 0; + } + else { + // Version 1 instrument + inst.Name[0] = '\0'; + + inst.Algorithm = s[8] & 1; + inst.Panning[0] = 0; + inst.Panning[1] = 0; + + inst.Feedback[0] = (s[8] >> 1) & 0x7; + inst.Feedback[1] = 0; + + inst.Detune = 0; + inst.RiffSpeed = 0; + + inst.Volume = 64; + + inst.Operators[0][0] = s[0]; + inst.Operators[1][0] = s[1]; + inst.Operators[2][0] = 0; + inst.Operators[3][0] = 0; + inst.Operators[0][1] = s[2]; + inst.Operators[1][1] = s[3]; + inst.Operators[2][1] = 0; + inst.Operators[3][1] = 0; + inst.Operators[0][2] = s[4]; + inst.Operators[1][2] = s[5]; + inst.Operators[2][2] = 0; + inst.Operators[3][2] = 0; + inst.Operators[0][3] = s[6]; + inst.Operators[1][3] = s[7]; + inst.Operators[2][3] = 0; + inst.Operators[3][3] = 0; + inst.Operators[0][4] = s[9]; + inst.Operators[1][4] = s[10]; + inst.Operators[2][4] = 0; + inst.Operators[3][4] = 0; + + inst.Riff = 0; + + s += 11; + } + } + + // Get order list + OrderListSize = *s++; + OrderList = s; + s += OrderListSize; + + // Locate the tracks + NumTracks = 0; + if (Version >= 2) while (1) { + // Version 2 tracks + // Track number + uint8_t track_num = *s++; + if (track_num >= kTracks) + break; + if (track_num + 1 > NumTracks) + NumTracks = track_num + 1; + + // Track size in bytes + int size = s[0] | (int(s[1]) << 8); + s += 2; + + Tracks[track_num] = s; + s += size; + } + else for (int i = 0; i < 32; i++) { + // Version 1 tracks + int pos = s[0] | (int(s[1]) << 8); + s += 2; + if (pos) { + NumTracks = i + 1; + Tracks[i] = (uint8_t*)tune + pos; + } + } + + // Locate the riffs + if (Version >= 2) while (1) { + + // Riff id + uint8_t riffid = *s++; + uint8_t riffnum = riffid >> 4; + uint8_t channum = riffid & 15; + if (riffnum >= kRiffTracks || channum > kChannels) + break; + + // Track size in bytes + int size = s[0] | (int(s[1]) << 8); + s += 2; + + Riffs[riffnum][channum - 1] = s; + s += size; + } + + // Done parsing tune, now set up for play + for (int i = 0; i < 512; i++) + OPL3Regs[i] = 255; + Stop(); + + Initialised = true; +} + + + +//================================================================================================== +// Stop all sounds and reset the tune. Tune will play from the beginning again if you continue to +// Update(). +//================================================================================================== +void RADPlayer::Stop() { + + // Clear all registers + for (uint16_t reg = 0x20; reg < 0xF6; reg++) { + + // Ensure envelopes decay all the way + uint8_t val = (reg >= 0x60 && reg < 0xA0) ? 0xFF : 0; + + SetOPL3(reg, val); + SetOPL3(reg + 0x100, val); + } + + // Configure OPL3 + SetOPL3(1, 0x20); // Allow waveforms + SetOPL3(8, 0); // No split point + SetOPL3(0xbd, 0); // No drums, etc. + SetOPL3(0x104, 0); // Everything 2-op by default + SetOPL3(0x105, 1); // OPL3 mode on + +#if RAD_DETECT_REPEATS + // The order map keeps track of which patterns we've played so we can detect when the tune + // starts to repeat. Jump markers can't be reliably used for this + PlayTime = 0; + Repeating = false; + for (int i = 0; i < 4; i++) + OrderMap[i] = 0; +#endif + + // Initialise play values + SpeedCnt = 1; + Order = 0; + Track = GetTrack(); + Line = 0; + Entrances = 0; + MasterVol = 64; + + // Initialise channels + for (int i = 0; i < kChannels; i++) { + CChannel &chan = Channels[i]; + chan.LastInstrument = 0; + chan.Instrument = 0; + chan.Volume = 0; + chan.DetuneA = 0; + chan.DetuneB = 0; + chan.KeyFlags = 0; + chan.Riff.SpeedCnt = 0; + chan.IRiff.SpeedCnt = 0; + } +} + + + +//================================================================================================== +// Playback update. Call BPM * 2 / 5 times a second. Use GetHertz() for this number after the +// tune has been initialised. Returns true if tune is starting to repeat. +//================================================================================================== +bool RADPlayer::Update() { + + if (!Initialised) + return false; + + // Run riffs + for (int i = 0; i < kChannels; i++) { + CChannel &chan = Channels[i]; + TickRiff(i, chan.IRiff, false); + TickRiff(i, chan.Riff, true); + } + + // Run main track + PlayLine(); + + // Run effects + for (int i = 0; i < kChannels; i++) { + CChannel &chan = Channels[i]; + ContinueFX(i, &chan.IRiff.FX); + ContinueFX(i, &chan.Riff.FX); + ContinueFX(i, &chan.FX); + } + + // Update play time. We convert to seconds when queried + PlayTime++; + +#if RAD_DETECT_REPEATS + return Repeating; +#else + return false; +#endif +} + + + +//================================================================================================== +// Unpacks a single RAD note. +//================================================================================================== +bool RADPlayer::UnpackNote(uint8_t *&s, uint8_t &last_instrument) { + + uint8_t chanid = *s++; + + InstNum = 0; + EffectNum = 0; + Param = 0; + + uint8_t note = 0; + + if (Version >= 2) { + // Version 2 notes + // Unpack note data + if (chanid & 0x40) { + uint8_t n = *s++; + note = n & 0x7F; + + // Retrigger last instrument? + if (n & 0x80) + InstNum = last_instrument; + } + + // Do we have an instrument? + if (chanid & 0x20) { + InstNum = *s++; + last_instrument = InstNum; + } + + // Do we have an effect? + if (chanid & 0x10) { + EffectNum = *s++; + Param = *s++; + } + } + else { + // Version 1 notes + // Unpack note data + uint8_t n = *s++; + note = n & 0x7f; + if (n & 0x80) + InstNum = 16; + + // Do we have an instrument? + n = *s++; + InstNum |= n >> 4; + if (InstNum) + last_instrument = InstNum; + + // Do we have an effect? + EffectNum = n & 0xf; + if (EffectNum) + Param = *s++; + } + + NoteNum = note & 15; + OctaveNum = note >> 4; + + return ((chanid & 0x80) != 0); +} + + + +//================================================================================================== +// Get current track as indicated by order list. +//================================================================================================== +uint8_t *RADPlayer::GetTrack() { + + // If at end of tune start again from beginning + if (Order >= OrderListSize) + Order = 0; + + uint8_t track_num = OrderList[Order]; + + // Jump marker? Note, we don't recognise multiple jump markers as that could put us into an + // infinite loop + if (track_num & 0x80) { + Order = track_num & 0x7F; + track_num = OrderList[Order] & 0x7F; + } + +#if RAD_DETECT_REPEATS + // Check for tune repeat, and mark order in order map + if (Order < 128) { + int byte = Order >> 5; + uint32_t bit = uint32_t(1) << (Order & 31); + if (OrderMap[byte] & bit) + Repeating = true; + else + OrderMap[byte] |= bit; + } +#endif + + return Tracks[track_num]; +} + + + +//================================================================================================== +// Skip through track till we reach the given line or the next higher one. Returns null if none. +//================================================================================================== +uint8_t *RADPlayer::SkipToLine(uint8_t *trk, uint8_t linenum, bool chan_riff) { + + while (1) { + + uint8_t lineid = *trk; + if ((lineid & 0x7F) >= linenum) + return trk; + if (lineid & 0x80) + break; + trk++; + + // Skip channel notes + uint8_t chanid; + do { + chanid = *trk++; + if (Version >= 2) + trk += NoteSize[(chanid >> 4) & 7]; + else if (trk[1] & 0xf) + // v1 note with param + trk += 3; + else + // v1 note without param + trk += 2; + } while (!(chanid & 0x80) && !chan_riff); + } + + return 0; +} + + + +//================================================================================================== +// Plays one line of current track and advances pointers. +//================================================================================================== +void RADPlayer::PlayLine() { + + SpeedCnt--; + if (SpeedCnt > 0) + return; + SpeedCnt = Speed; + + // Reset channel effects + for (int i = 0; i < kChannels; i++) + ResetFX(&Channels[i].FX); + + LineJump = -1; + + // At the right line? + uint8_t *trk = Track; + if (trk && (*trk & 0x7F) <= Line) { + uint8_t lineid = *trk++; + + // Run through channels + bool last; + do { + int channum = *trk & 15; + CChannel &chan = Channels[channum]; + last = UnpackNote(trk, chan.LastInstrument); + PlayNote(channum, NoteNum, OctaveNum, InstNum, EffectNum, Param); + } while (!last); + + // Was this the last line? + if (lineid & 0x80) + trk = 0; + + Track = trk; + } + + // Move to next line + Line++; + if (Line >= kTrackLines || LineJump >= 0) { + + if (LineJump >= 0) + Line = LineJump; + else + Line = 0; + + // Move to next track in order list + Order++; + Track = GetTrack(); + if (Line > 0) + Track = SkipToLine(Track, Line, false); + } +} + + + +//================================================================================================== +// Play a single note. Returns the line number in the next pattern to jump to if a jump command was +// found, or -1 if none. +//================================================================================================== +void RADPlayer::PlayNote(int channum, int8_t notenum, int8_t octave, uint16_t instnum, uint8_t cmd, uint8_t param, e_Source src, int op) { + CChannel &chan = Channels[channum]; + + // Recursion detector. This is needed as riffs can trigger other riffs, and they could end up + // in a loop + if (Entrances >= 8) + return; + Entrances++; + + // Select which effects source we're using + CEffects *fx = &chan.FX; + if (src == SRiff) + fx = &chan.Riff.FX; + else if (src == SIRiff) + fx = &chan.IRiff.FX; + + bool transposing = false; + + // For tone-slides the note is the target + if (cmd == cmToneSlide) { + if (notenum > 0 && notenum <= 12) { + fx->ToneSlideOct = octave; + fx->ToneSlideFreq = NoteFreq[notenum - 1]; + } + goto toneslide; + } + + // Playing a new instrument? + if (instnum > 0) { + CInstrument *oldinst = chan.Instrument; + CInstrument *inst = &Instruments[instnum - 1]; + chan.Instrument = inst; + + if (inst->Algorithm < 7) { + + LoadInstrumentOPL3(channum); + + // Bounce the channel + chan.KeyFlags |= fKeyOff | fKeyOn; + + ResetFX(&chan.IRiff.FX); + + if (src != SIRiff || inst != oldinst) { + + // Instrument riff? + if (inst->Riff && inst->RiffSpeed > 0) { + + chan.IRiff.Track = chan.IRiff.TrackStart = inst->Riff; + chan.IRiff.Line = 0; + chan.IRiff.Speed = inst->RiffSpeed; + chan.IRiff.LastInstrument = 0; + + // Note given with riff command is used to transpose the riff + if (notenum >= 1 && notenum <= 12) { + chan.IRiff.TransposeOctave = octave; + chan.IRiff.TransposeNote = notenum; + transposing = true; + } + else { + chan.IRiff.TransposeOctave = 3; + chan.IRiff.TransposeNote = 12; + } + + // Do first tick of riff + chan.IRiff.SpeedCnt = 1; + TickRiff(channum, chan.IRiff, false); + + } + else + chan.IRiff.SpeedCnt = 0; + } + } + else + // Ignore MIDI instruments + chan.Instrument = 0; + } + + // Starting a channel riff? + if (cmd == cmRiff || cmd == cmTranspose) { + + ResetFX(&chan.Riff.FX); + + uint8_t p0 = param / 10; + uint8_t p1 = param % 10; + chan.Riff.Track = p1 > 0 ? Riffs[p0][p1 - 1] : 0; + if (chan.Riff.Track) { + + chan.Riff.TrackStart = chan.Riff.Track; + chan.Riff.Line = 0; + chan.Riff.Speed = Speed; + chan.Riff.LastInstrument = 0; + + // Note given with riff command is used to transpose the riff + if (cmd == cmTranspose && notenum >= 1 && notenum <= 12) { + chan.Riff.TransposeOctave = octave; + chan.Riff.TransposeNote = notenum; + transposing = true; + } + else { + chan.Riff.TransposeOctave = 3; + chan.Riff.TransposeNote = 12; + } + + // Do first tick of riff + chan.Riff.SpeedCnt = 1; + TickRiff(channum, chan.Riff, true); + + } + else + chan.Riff.SpeedCnt = 0; + } + + // Play the note + if (!transposing && notenum > 0) { + + // Key-off? + if (notenum == 15) + chan.KeyFlags |= fKeyOff; + + if (!chan.Instrument || chan.Instrument->Algorithm < 7) + PlayNoteOPL3(channum, octave, notenum); + } + + // Process effect + switch (cmd) { + + case cmSetVol: + SetVolume(channum, param); + break; + + case cmSetSpeed: + if (src == SNone) { + Speed = param; + SpeedCnt = param; + } + else if (src == SRiff) { + chan.Riff.Speed = param; + chan.Riff.SpeedCnt = param; + } + else if (src == SIRiff) { + chan.IRiff.Speed = param; + chan.IRiff.SpeedCnt = param; + } + break; + + case cmPortamentoUp: + fx->PortSlide = param; + break; + + case cmPortamentoDwn: + fx->PortSlide = -int8_t(param); + break; + + case cmToneVolSlide: + case cmVolSlide: { + int8_t val = param; + if (val >= 50) + val = -(val - 50); + fx->VolSlide = val; + if (cmd != cmToneVolSlide) + break; + } + // Fall through! + + case cmToneSlide: { + toneslide: + uint8_t speed = param; + if (speed) + fx->ToneSlideSpeed = speed; + GetSlideDir(channum, fx); + break; + } + + case cmJumpToLine: { + if (param >= kTrackLines) + break; + + // Note: jump commands in riffs are checked for within TickRiff() + if (src == SNone) + LineJump = param; + + break; + } + + case cmMultiplier: { + if (src == SIRiff) + LoadInstMultiplierOPL3(channum, op, param); + break; + } + + case cmVolume: { + if (src == SIRiff) + LoadInstVolumeOPL3(channum, op, param); + break; + } + + case cmFeedback: { + if (src == SIRiff) { + uint8_t which = param / 10; + uint8_t fb = param % 10; + LoadInstFeedbackOPL3(channum, which, fb); + } + break; + } + } + + Entrances--; +} + + + +//================================================================================================== +// Sets the OPL3 registers for a given instrument. +//================================================================================================== +void RADPlayer::LoadInstrumentOPL3(int channum) { + CChannel &chan = Channels[channum]; + + const CInstrument *inst = chan.Instrument; + if (!inst) + return; + + uint8_t alg = inst->Algorithm; + chan.Volume = inst->Volume; + chan.DetuneA = (inst->Detune + 1) >> 1; + chan.DetuneB = inst->Detune >> 1; + + // Turn on 4-op mode for algorithms 2 and 3 (algorithms 4 to 6 are simulated with 2-op mode) + if (UseOPL3 && channum < 6) { + uint8_t mask = 1 << channum; + SetOPL3(0x104, (GetOPL3(0x104) & ~mask) | (alg == 2 || alg == 3 ? mask : 0)); + } + + // Left/right/feedback/algorithm + if (UseOPL3) { + SetOPL3(0xC0 + ChanOffsets3[channum], ((inst->Panning[1] ^ 3) << 4) | inst->Feedback[1] << 1 | (alg == 3 || alg == 5 || alg == 6 ? 1 : 0)); + SetOPL3(0xC0 + Chn2Offsets3[channum], ((inst->Panning[0] ^ 3) << 4) | inst->Feedback[0] << 1 | (alg == 1 || alg == 6 ? 1 : 0)); + } + else { + SetOPL3(0xC0 + channum, ((inst->Panning[0] ^ 3) << 4) | inst->Feedback[0] << 1 | (alg == 1 ? 1 : 0)); + } + + // Load the operators + for (int i = 0; i < (UseOPL3 ? 4 : 2); i++) { + + static const uint8_t blank[] = { 0, 0x3F, 0, 0xF0, 0 }; + const uint8_t *op = (alg < 2 && i >= 2) ? blank : inst->Operators[i]; + uint16_t reg = UseOPL3 ? OpOffsets3[channum][i] : OpOffsets2[channum][i]; + + uint16_t vol = ~op[1] & 0x3F; + + // Do volume scaling for carriers + if (AlgCarriers[alg][i]) { + vol = vol * inst->Volume / 64; + vol = vol * MasterVol / 64; + } + + SetOPL3(reg + 0x20, op[0]); + SetOPL3(reg + 0x40, (op[1] & 0xC0) | ((vol ^ 0x3F) & 0x3F)); + SetOPL3(reg + 0x60, op[2]); + SetOPL3(reg + 0x80, op[3]); + SetOPL3(reg + 0xE0, op[4]); + } +} + + + +//================================================================================================== +// Play note on OPL3 hardware. +//================================================================================================== +void RADPlayer::PlayNoteOPL3(int channum, int8_t octave, int8_t note) { + CChannel &chan = Channels[channum]; + + uint16_t o1, o2; + if (UseOPL3) { + o1 = ChanOffsets3[channum]; + o2 = Chn2Offsets3[channum]; + } + else { + o1 = 0; + o2 = channum; + } + + // Key off the channel + if (chan.KeyFlags & fKeyOff) { + chan.KeyFlags &= ~(fKeyOff | fKeyedOn); + if (UseOPL3) + SetOPL3(0xB0 + o1, GetOPL3(0xB0 + o1) & ~0x20); + SetOPL3(0xB0 + o2, GetOPL3(0xB0 + o2) & ~0x20); + } + + if (note > 12) + return; + + bool op4 = (UseOPL3 && chan.Instrument && chan.Instrument->Algorithm >= 2); + + uint16_t freq = NoteFreq[note - 1]; + uint16_t frq2 = freq; + + chan.CurrFreq = freq; + chan.CurrOctave = octave; + + // Detune. We detune both channels in the opposite direction so the note retains its tuning + freq += chan.DetuneA; + frq2 -= chan.DetuneB; + + // Frequency low byte + if (op4) + SetOPL3(0xA0 + o1, frq2 & 0xFF); + SetOPL3(0xA0 + o2, freq & 0xFF); + + // Frequency high bits + octave + key on + if (chan.KeyFlags & fKeyOn) + chan.KeyFlags = (chan.KeyFlags & ~fKeyOn) | fKeyedOn; + if (op4) + SetOPL3(0xB0 + o1, (frq2 >> 8) | (octave << 2) | ((chan.KeyFlags & fKeyedOn) ? 0x20 : 0)); + else if (UseOPL3) + SetOPL3(0xB0 + o1, 0); + SetOPL3(0xB0 + o2, (freq >> 8) | (octave << 2) | ((chan.KeyFlags & fKeyedOn) ? 0x20 : 0)); +} + + + +//================================================================================================== +// Prepare FX for new line. +//================================================================================================== +void RADPlayer::ResetFX(CEffects *fx) { + fx->PortSlide = 0; + fx->VolSlide = 0; + fx->ToneSlideDir = 0; +} + + + +//================================================================================================== +// Tick the channel riff. +//================================================================================================== +void RADPlayer::TickRiff(int channum, CChannel::CRiff &riff, bool chan_riff) { + uint8_t lineid; + + if (riff.SpeedCnt == 0) { + ResetFX(&riff.FX); + return; + } + + riff.SpeedCnt--; + if (riff.SpeedCnt > 0) + return; + riff.SpeedCnt = riff.Speed; + + uint8_t line = riff.Line++; + if (riff.Line >= kTrackLines) + riff.SpeedCnt = 0; + + ResetFX(&riff.FX); + + // Is this the current line in track? + uint8_t *trk = riff.Track; + if (trk && (*trk & 0x7F) == line) { + lineid = *trk++; + + if (chan_riff) { + + // Channel riff: play current note + UnpackNote(trk, riff.LastInstrument); + Transpose(riff.TransposeNote, riff.TransposeOctave); + PlayNote(channum, NoteNum, OctaveNum, InstNum, EffectNum, Param, SRiff); + + } + else { + + // Instrument riff: here each track channel is an extra effect that can run, but is not + // actually a different physical channel + bool last; + do { + int col = *trk & 15; + last = UnpackNote(trk, riff.LastInstrument); + if (EffectNum != cmIgnore) + Transpose(riff.TransposeNote, riff.TransposeOctave); + PlayNote(channum, NoteNum, OctaveNum, InstNum, EffectNum, Param, SIRiff, col > 0 ? (col - 1) & 3 : 0); + } while (!last); + } + + // Last line? + if (lineid & 0x80) + trk = 0; + + riff.Track = trk; + } + + // Special case; if next line has a jump command, run it now + if (!trk || (*trk++ & 0x7F) != riff.Line) + return; + + UnpackNote(trk, lineid); // lineid is just a dummy here + if (EffectNum == cmJumpToLine && Param < kTrackLines) { + riff.Line = Param; + riff.Track = SkipToLine(riff.TrackStart, Param, chan_riff); + } +} + + + +//================================================================================================== +// This continues any effects that operate continuously (eg. slides). +//================================================================================================== +void RADPlayer::ContinueFX(int channum, CEffects *fx) { + CChannel &chan = Channels[channum]; + + if (fx->PortSlide) + Portamento(channum, fx, fx->PortSlide, false); + + if (fx->VolSlide) { + int8_t vol = chan.Volume; + vol -= fx->VolSlide; + if (vol < 0) + vol = 0; + SetVolume(channum, vol); + } + + if (fx->ToneSlideDir) + Portamento(channum, fx, fx->ToneSlideDir, true); +} + + + +//================================================================================================== +// Sets the volume of given channel. +//================================================================================================== +void RADPlayer::SetVolume(int channum, uint8_t vol) { + CChannel &chan = Channels[channum]; + + // Ensure volume is within range + if (vol > 64) + vol = 64; + + chan.Volume = vol; + + // Scale volume to master volume + vol = vol * MasterVol / 64; + + CInstrument *inst = chan.Instrument; + if (!inst) + return; + uint8_t alg = inst->Algorithm; + + // Set volume of all carriers + for (int i = 0; i < 4; i++) { + uint8_t *op = inst->Operators[i]; + + // Is this operator a carrier? + if (!AlgCarriers[alg][i]) + continue; + + uint8_t opvol = uint16_t((op[1] & 63) ^ 63) * vol / 64; + uint16_t reg = 0x40 + (UseOPL3 ? OpOffsets3[channum][i] : OpOffsets2[channum][i]); + SetOPL3(reg, (GetOPL3(reg) & 0xC0) | (opvol ^ 0x3F)); + } +} + + + +//================================================================================================== +// Starts a tone-slide. +//================================================================================================== +void RADPlayer::GetSlideDir(int channum, CEffects *fx) { + CChannel &chan = Channels[channum]; + + int8_t speed = fx->ToneSlideSpeed; + if (speed > 0) { + uint8_t oct = fx->ToneSlideOct; + uint16_t freq = fx->ToneSlideFreq; + + uint16_t oldfreq = chan.CurrFreq; + uint8_t oldoct = chan.CurrOctave; + + if (oldoct > oct) + speed = -speed; + else if (oldoct == oct) { + if (oldfreq > freq) + speed = -speed; + else if (oldfreq == freq) + speed = 0; + } + } + + fx->ToneSlideDir = speed; +} + + + +//================================================================================================== +// Load multiplier value into operator. +//================================================================================================== +void RADPlayer::LoadInstMultiplierOPL3(int channum, int op, uint8_t mult) { + // Always assume OPL3 register numbering since this is not used in v1 tunes + uint16_t reg = 0x20 + OpOffsets3[channum][op]; + SetOPL3(reg, (GetOPL3(reg) & 0xF0) | (mult & 15)); +} + + + +//================================================================================================== +// Load volume value into operator. +//================================================================================================== +void RADPlayer::LoadInstVolumeOPL3(int channum, int op, uint8_t vol) { + // Always assume OPL3 register numbering since this is not used in v1 tunes + uint16_t reg = 0x40 + OpOffsets3[channum][op]; + SetOPL3(reg, (GetOPL3(reg) & 0xC0) | ((vol & 0x3F) ^ 0x3F)); +} + + + +//================================================================================================== +// Load feedback value into instrument. +//================================================================================================== +void RADPlayer::LoadInstFeedbackOPL3(int channum, int which, uint8_t fb) { + // Always assume OPL3 register numbering since this is not used in v1 tunes + if (which == 0) { + + uint16_t reg = 0xC0 + Chn2Offsets3[channum]; + SetOPL3(reg, (GetOPL3(reg) & 0x31) | ((fb & 7) << 1)); + + } + else if (which == 1) { + + uint16_t reg = 0xC0 + ChanOffsets3[channum]; + SetOPL3(reg, (GetOPL3(reg) & 0x31) | ((fb & 7) << 1)); + } +} + + + +//================================================================================================== +// This adjusts the pitch of the given channel's note. There may also be a limiting value on the +// portamento (for tone slides). +//================================================================================================== +void RADPlayer::Portamento(uint16_t channum, CEffects *fx, int8_t amount, bool toneslide) { + CChannel &chan = Channels[channum]; + + uint16_t freq = chan.CurrFreq; + uint8_t oct = chan.CurrOctave; + + freq += amount; + + if (freq < 0x156) { + + if (oct > 0) { + oct--; + freq += 0x2AE - 0x156; + } + else + freq = 0x156; + + } + else if (freq > 0x2AE) { + + if (oct < 7) { + oct++; + freq -= 0x2AE - 0x156; + } + else + freq = 0x2AE; + } + + if (toneslide) { + + if (amount >= 0) { + + if (oct > fx->ToneSlideOct || (oct == fx->ToneSlideOct && freq >= fx->ToneSlideFreq)) { + freq = fx->ToneSlideFreq; + oct = fx->ToneSlideOct; + } + + } + else { + + if (oct < fx->ToneSlideOct || (oct == fx->ToneSlideOct && freq <= fx->ToneSlideFreq)) { + freq = fx->ToneSlideFreq; + oct = fx->ToneSlideOct; + } + } + } + + chan.CurrFreq = freq; + chan.CurrOctave = oct; + + // Apply detunes + uint16_t frq2 = freq - chan.DetuneB; + freq += chan.DetuneA; + + // Write value back to OPL3 + uint16_t chan_offset = UseOPL3 ? Chn2Offsets3[channum] : channum; + SetOPL3(0xA0 + chan_offset, freq & 0xFF); + SetOPL3(0xB0 + chan_offset, (freq >> 8 & 3) | oct << 2 | (GetOPL3(0xB0 + chan_offset) & 0xE0)); + + if (UseOPL3) { + chan_offset = ChanOffsets3[channum]; + SetOPL3(0xA0 + chan_offset, frq2 & 0xFF); + SetOPL3(0xB0 + chan_offset, (frq2 >> 8 & 3) | oct << 2 | (GetOPL3(0xB0 + chan_offset) & 0xE0)); + } +} + + + +//================================================================================================== +// Transpose the note returned by UnpackNote(). +// Note: due to RAD's wonky legacy middle C is octave 3 note number 12. +//================================================================================================== +void RADPlayer::Transpose(int8_t note, int8_t octave) { + + if (NoteNum >= 1 && NoteNum <= 12) { + + int8_t toct = octave - 3; + if (toct != 0) { + OctaveNum += toct; + if (OctaveNum < 0) + OctaveNum = 0; + else if (OctaveNum > 7) + OctaveNum = 7; + } + + int8_t tnot = note - 12; + if (tnot != 0) { + NoteNum += tnot; + if (NoteNum < 1) { + NoteNum += 12; + if (OctaveNum > 0) + OctaveNum--; + else + NoteNum = 1; + } + } + } +} + + + +//================================================================================================== +// Compute total time of tune if it didn't repeat. Note, this stops the tune so should only be done +// prior to initial playback. +//================================================================================================== +#if RAD_DETECT_REPEATS +static void RADPlayerDummyOPL3(void *arg, uint16_t reg, uint8_t data) {} +//-------------------------------------------------------------------------------------------------- +uint32_t RADPlayer::ComputeTotalTime() { + + Stop(); + void(*old_opl3)(void *, uint16_t, uint8_t) = OPL3; + OPL3 = RADPlayerDummyOPL3; + + while (!Update()) + ; + uint32_t total = PlayTime; + + Stop(); + OPL3 = old_opl3; + + return total / Hertz; +} +#endif + +/* +* +* Start of RADPlayer wrapper for AdPlug +* +*/ + +static void writeOPL(void *arg, uint16_t reg, uint8_t val) { + Copl *opl = (Copl *)arg; + int chip = reg >> 8; + if (opl->getchip() != chip) + opl->setchip(chip); + opl->write(reg & 0xFF, val); +} + +CPlayer *Crad2Player::factory(Copl *newopl) { + return new Crad2Player(newopl); +} + +Crad2Player::Crad2Player(Copl *newopl) + : CPlayer(newopl) { + + rad = new RADPlayer(); + data = 0; +} + +Crad2Player::~Crad2Player() { + delete rad; + delete[] data; +} + +bool Crad2Player::load(const std::string &filename, const CFileProvider &fp) { + char *newdata; + size_t size; + const char *err; + + binistream *file = fp.open(filename); + if (!file) return false; + + char header[17]; + header[16] = '\0'; + file->readString(header, 16); + if (strncmp(header, "RAD by REALiTY!!", 16)) { + fp.close(file); + return false; + } + + file->seek(0, binio::End); + size = file->pos(); + + // some old tunes have a truncated final note for some reason + // add an extra empty byte at the end of the file so they still "work" + newdata = new char[size + 1]; + newdata[size] = '\0'; + file->seek(0); + file->readString(newdata, size); + fp.close(file); + + if (!(err = RADValidate(newdata, size + 1))) { + rad->Init(newdata, writeOPL, opl); + // playback timer is < 0 if load failed + if (rad->GetHertz() > 0) { + delete[] data; + data = newdata; + + // read description + desc.clear(); + const unsigned char *newdesc = rad->GetDescription(); + while (newdesc && *newdesc) { + unsigned char c = *newdesc++; + + if (c >= 0x20) + desc += c; + else if (c == 0x01) + desc += '\n'; + else for (int i = 0; i < c; i++) + desc += ' '; + } + + // done + return true; + } + } + else { + AdPlug_LogWrite("Crad2Player::load(\"%s\"): %s\n", filename.c_str(), err); + } + + delete[] newdata; + return false; +} + +bool Crad2Player::update() { return !rad->Update(); } +void Crad2Player::rewind(int) { rad->Stop(); } +float Crad2Player::getrefresh() { return rad->GetHertz(); } + +std::string Crad2Player::gettype() { + char type[64]; + snprintf(type, sizeof(type), "Reality ADlib Tracker (version %d)", rad->GetVersion()); + return std::string(type); +} +unsigned int Crad2Player::getpatterns() { return rad->GetTunePatterns(); } +unsigned int Crad2Player::getpattern() { return rad->GetTunePattern(); } +unsigned int Crad2Player::getorders() { return rad->GetTuneLength(); } +unsigned int Crad2Player::getorder() { return rad->GetTunePos(); } +unsigned int Crad2Player::getrow() { return rad->GetTuneLine(); } +unsigned int Crad2Player::getspeed() { return rad->GetSpeed(); } +unsigned int Crad2Player::getinstruments() { return rad->GetTuneInsts(); } +std::string Crad2Player::getinstrument(unsigned int n) { return rad->GetTuneInst(n); } diff --git a/plugins/adplug/adplug/rad2.h b/plugins/adplug/adplug/rad2.h new file mode 100644 index 0000000000..8bd5191b42 --- /dev/null +++ b/plugins/adplug/adplug/rad2.h @@ -0,0 +1,64 @@ +/* +* Adplug - Replayer for many OPL2/OPL3 audio file formats. +* Copyright (C) 1999 - 2007 Simon Peter, , et al. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* rad2.h - RAD v2 replayer class +*/ + +#ifndef H_ADPLUG_RAD2PLAYER +#define H_ADPLUG_RAD2PLAYER + +#include "player.h" + +class RADPlayer; + +class Crad2Player: public CPlayer +{ +public: + static CPlayer *factory(Copl *newopl); + + Crad2Player(Copl *newopl); + virtual ~Crad2Player(); + + /***** Operational methods *****/ + bool load(const std::string &filename, const CFileProvider &fp = CProvider_Filesystem()); + bool update(); + void rewind(int subsong = -1); + float getrefresh(); + + /***** Informational methods *****/ + std::string gettype(); + std::string getdesc() { return desc; } + + unsigned int getpatterns(); + unsigned int getinstruments(); + unsigned int getpattern(); + unsigned int getorders(); + unsigned int getorder(); + unsigned int getrow(); + unsigned int getspeed(); + std::string getinstrument(unsigned int n); + +protected: + RADPlayer *rad; + char *data; + + std::string desc; + +}; + +#endif diff --git a/plugins/adplug/adplug/rat.cpp b/plugins/adplug/adplug/rat.cpp index 4a91600f8e..8ad99b3b21 100644 --- a/plugins/adplug/adplug/rat.cpp +++ b/plugins/adplug/adplug/rat.cpp @@ -33,18 +33,6 @@ #include "rat.h" #include "debug.h" -const unsigned char CxadratPlayer::rat_adlib_bases[18] = -{ - 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12, - 0x03, 0x04, 0x05, 0x0B, 0x0C, 0x0D, 0x13, 0x14, 0x15 -}; - -const unsigned short CxadratPlayer::rat_notes[16] = -{ - 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287, - 0x000, 0x000, 0x000, 0x000 // by riven -}; - CPlayer *CxadratPlayer::factory(Copl *newopl) { return new CxadratPlayer(newopl); @@ -52,38 +40,46 @@ CPlayer *CxadratPlayer::factory(Copl *newopl) bool CxadratPlayer::xadplayer_load() { - if(xad.fmt != RAT) + if (xad.fmt != RAT || tune_size < 0x140) return false; // load header memcpy(&rat.hdr, &tune[0], sizeof(rat_header)); - // is 'RAT'-signed ? - if (strncmp(rat.hdr.id,"RAT",3)) + // 'RAT'-signed? + if (memcmp(rat.hdr.id, "RAT", 3)) return false; - // is version 1.0 ? + // version is 1.0? if (rat.hdr.version != 0x10) return false; + // valid number of channels (1..9)? + if (rat.hdr.numchan < 1 || rat.hdr.numchan > 9) + return false; + // load order rat.order = &tune[0x40]; // load instruments rat.inst = (rat_instrument *)&tune[0x140]; + if (0x140 + rat.hdr.numinst * sizeof(rat_instrument) > tune_size) + return false; // load pattern data unsigned short patseg = (rat.hdr.patseg[1] << 8) + rat.hdr.patseg[0]; - unsigned char *event_ptr = &tune[patseg << 4]; + if (patseg * 16 + sizeof(rat_event) * rat.hdr.numpat * 64 * rat.hdr.numchan + > tune_size) + return false; - for(int i=0;i>= 6; + vol *= gvol; + vol >>= 6; + vol ^= 0x3F; + + vol |= ivol & 0xC0; + + return vol; +} + +void CxadratPlayer::xadplayer_update() +{ + unsigned char pattern = rat.order[rat.order_pos]; + if (pattern >= rat.hdr.numpat) + goto bad_pattern; // process events - for(i=0;i> 8) | ((event.note & 0xF0) >> 2) | 0x20); } } - // is effect ? - if (event.fx != 0xFF) - { + // is effect? + if (event.fx != 0xFF) { rat.channel[i].fx = event.fx; rat.channel[i].fxp = event.fxp; } @@ -203,27 +231,26 @@ void CxadratPlayer::xadplayer_update() rat.pattern_pos++; // process effects - for(i=0;i= 0x40) - { + // end of pattern? + if (rat.pattern_pos >= 0x40) { + bad_pattern: rat.pattern_pos = 0; - rat.order_pos++; - // end of module ? - if (rat.order_pos == rat.hdr.order_end) - { + // end of module? + if (rat.order_pos == rat.hdr.order_end) { rat.order_pos = rat.hdr.order_loop; - plr.looping = 1; } } @@ -256,38 +280,15 @@ float CxadratPlayer::xadplayer_getrefresh() std::string CxadratPlayer::xadplayer_gettype() { - return (std::string("xad: rat player")); + return std::string("xad: rat player"); } std::string CxadratPlayer::xadplayer_gettitle() { - return (std::string(rat.hdr.title,32)); + return std::string(rat.hdr.title, 32); } unsigned int CxadratPlayer::xadplayer_getinstruments() { return rat.hdr.numinst; } - -/* -------- Internal Functions ---------------------------- */ - -unsigned char CxadratPlayer::__rat_calc_volume(unsigned char ivol, unsigned char cvol, unsigned char gvol) -{ -#ifdef DEBUG - AdPlug_LogWrite("volumes: instrument %02X, channel %02X, global %02X:\n", ivol, cvol, gvol); -#endif - unsigned short vol; - - vol = ivol; - vol &= 0x3F; - vol ^= 0x3F; - vol *= cvol; - vol >>= 6; - vol *= gvol; - vol >>= 6; - vol ^= 0x3F; - - vol |= ivol & 0xC0; - - return vol; -} diff --git a/plugins/adplug/adplug/rat.h b/plugins/adplug/adplug/rat.h index 424a76ba42..147177a114 100644 --- a/plugins/adplug/adplug/rat.h +++ b/plugins/adplug/adplug/rat.h @@ -109,13 +109,7 @@ class CxadratPlayer: public CxadPlayer void xadplayer_rewind(int subsong); void xadplayer_update(); float xadplayer_getrefresh(); - std::string xadplayer_gettype(); + std::string xadplayer_gettype(); std::string xadplayer_gettitle(); unsigned int xadplayer_getinstruments(); - // -private: - static const unsigned char rat_adlib_bases[18]; - static const unsigned short rat_notes[16]; - - unsigned char __rat_calc_volume(unsigned char ivol, unsigned char cvol, unsigned char gvol); }; diff --git a/plugins/adplug/adplug/raw.cpp b/plugins/adplug/adplug/raw.cpp index a199182b9e..f41f552ef8 100644 --- a/plugins/adplug/adplug/raw.cpp +++ b/plugins/adplug/adplug/raw.cpp @@ -51,7 +51,9 @@ bool CrawPlayer::load(const std::string &filename, const CFileProvider &fp) // load section this->clock = f->readInt(2); // clock speed - this->length = (fp.filesize(f) - 10) / 2; // Wraithverge: total data-size. + this->length = fp.filesize(f); + if (this->length <= 10) { fp.close (f); return false; } + this->length = (this->length - 10) / 2; // Wraithverge: total data-size. this->data = new Tdata [this->length]; bool tagdata = false; title[0] = 0; @@ -133,7 +135,6 @@ bool CrawPlayer::update() do { setspeed = false; - if (this->pos >= this->length) return false; switch(this->data[this->pos].command) { @@ -144,6 +145,7 @@ bool CrawPlayer::update() case 2: if (!this->data[this->pos].param) { pos++; + if (this->pos >= this->length) return false; this->speed = this->data[this->pos].param + (this->data[this->pos].command << 8); setspeed = true; } else diff --git a/plugins/adplug/adplug/realopl.cpp b/plugins/adplug/adplug/realopl.cpp index 98c2a35284..fb2add1d61 100644 --- a/plugins/adplug/adplug/realopl.cpp +++ b/plugins/adplug/adplug/realopl.cpp @@ -46,8 +46,8 @@ # define INP inb # define OUTP(reg,val) outb(val,reg) #else // no support on other platforms - # define INP(reg) 0 - # define OUTP(reg, val) + static inline int INP(int reg) { return 0; } + static inline void OUTP(int reg, int val) { }; #endif #include "realopl.h" diff --git a/plugins/adplug/adplug/rix.cpp b/plugins/adplug/adplug/rix.cpp index d29a1d4c20..86947243f3 100644 --- a/plugins/adplug/adplug/rix.cpp +++ b/plugins/adplug/adplug/rix.cpp @@ -25,17 +25,9 @@ #include "rix.h" #include "debug.h" -#if defined(__hppa__) || \ - defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ - (defined(__MIPS__) && defined(__MISPEB__)) || \ - defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ - defined(__sparc__) - // big endian - #define RIX_SWAP32(a) (((a) << 24) | (((a) << 8) & 0x00FF0000) | (((a) >> 8) & 0x0000FF00) | ((a) >> 24)) -#else - // little endian - #define RIX_SWAP32(a) (a) -#endif +#define RIX_GET32(p, i) \ + ((uint32_t)p[4*i] | (uint32_t)p[4*i+1] << 8 | \ + (uint32_t)p[4*i+2] << 16 | (uint32_t)p[4*i + 3] << 24) const uint8_t CrixPlayer::adflag[] = {0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1}; const uint8_t CrixPlayer::reg_data[] = {0,1,2,3,4,5,8,9,10,11,12,13,16,17,18,19,20,21}; @@ -55,8 +47,6 @@ const uint8_t CrixPlayer::bd_reg_data[] = { 0x0F,0x0B,0x00,0x05,0x05,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x0F,0x0B,0x00,0x07,0x05,0x00,0x00,0x00, 0x00,0x00,0x00}; -uint8_t CrixPlayer::for40reg[] = {0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F, - 0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F}; const uint16_t CrixPlayer::mus_time = 0x4268; /*** public methods *************************************/ @@ -80,9 +70,8 @@ CrixPlayer::~CrixPlayer() bool CrixPlayer::load(const std::string &filename, const CFileProvider &fp) { binistream *f = fp.open(filename); if(!f) return false; - uint32_t i=0; - if(stricmp(filename.substr(filename.length()-4,4).c_str(),".mkf")==0) + if (fp.extension(filename, ".mkf")) { flag_mkf=1; f->seek(0); @@ -90,11 +79,10 @@ bool CrixPlayer::load(const std::string &filename, const CFileProvider &fp) f->seek(offset); } if(f->readInt(2)!=0x55aa){ fp.close(f);return false; } - file_buffer = new uint8_t [fp.filesize(f) + 1]; + length = pos = fp.filesize(f); + file_buffer = new uint8_t[length]; f->seek(0); - while(!f->eof()) - file_buffer[i++]=f->readInt(1); - length=i; + f->readString((char *)file_buffer, length); fp.close(f); if(!flag_mkf) rix_buf=file_buffer; @@ -108,8 +96,14 @@ bool CrixPlayer::update() return !play_end; } +unsigned int CrixPlayer::getsubsong() +{ + return song; +} + void CrixPlayer::rewind(int subsong) { + song = subsong; I = 0; T = 0; mus_block = 0; ins_block = 0; @@ -122,7 +116,7 @@ void CrixPlayer::rewind(int subsong) bd_modify = 0; sustain = 0; play_end = 0; - pos = index = 0; + index = 0; memset(f_buffer, 0, sizeof(f_buffer)); memset(a0b0_data2, 0, sizeof(a0b0_data2)); @@ -135,13 +129,27 @@ void CrixPlayer::rewind(int subsong) memset(reg_bufs, 0, sizeof(reg_bufs)); memset(for40reg, 0x7F, sizeof(for40reg)); - if(flag_mkf) + if (flag_mkf && subsong >= 0) { - uint32_t *buf_index=(uint32_t *)file_buffer; - int offset1=RIX_SWAP32(buf_index[subsong]),offset2; - while((offset2=RIX_SWAP32(buf_index[++subsong]))==offset1); - length=offset2-offset1+1; - rix_buf=file_buffer+offset1; + // changed to actually work and match numbering of getsubsongs() + uint32_t i, offset, next=0, table_end; + offset = RIX_GET32(file_buffer, 0); + table_end = offset / 4; + for (i = 1; i < table_end; i++) + { + next = RIX_GET32(file_buffer, i); + if (next != offset) + { + if (--subsong < 0) break; + offset = next; + } + } + // fix up bad/unknown offsets to end of file_buffer + if (offset > pos) offset = pos; + if (i >= table_end || next > pos || next < offset) next = pos; + // start and length of the subsong + rix_buf = file_buffer + offset; + length = next - offset; } opl->init(); opl->write(1,32); // go to OPL2 mode @@ -149,14 +157,14 @@ void CrixPlayer::rewind(int subsong) data_initial(); } -uint32_t CrixPlayer::getsubsongs() +unsigned int CrixPlayer::getsubsongs() { if(flag_mkf) { - uint32_t *buf_index=(uint32_t *)file_buffer; - int songs=RIX_SWAP32(buf_index[0])/4,i=0; - for(i=0;i 0; i--) + if (RIX_GET32(file_buffer, i) == + RIX_GET32(file_buffer, i-1)) songs--; return songs; } @@ -178,7 +186,7 @@ inline void CrixPlayer::set_new_int() /*----------------------------------------------------------*/ inline void CrixPlayer::Pause() { - register uint16_t i; + uint16_t i; pause_flag = 1; for(i=0;i<11;i++) switch_ad_bd(i); @@ -192,10 +200,14 @@ inline void CrixPlayer::ad_a0b0l_reg_(uint16_t index,uint16_t p2,uint16_t p3) } inline void CrixPlayer::data_initial() { - rhythm = rix_buf[2]; - mus_block = (rix_buf[0x0D]<<8)+rix_buf[0x0C]; - ins_block = (rix_buf[0x09]<<8)+rix_buf[0x08]; - I = mus_block+1; + if (0x0D < length) + { + rhythm = rix_buf[2]; + mus_block = (rix_buf[0x0D] << 8) + rix_buf[0x0C]; + ins_block = (rix_buf[0x09] << 8) + rix_buf[0x08]; + I = mus_block + 1; + } + else I = mus_block = length; // file too short; will stop playing immediately if(rhythm != 0) { // ad_a0b0_reg(6); @@ -273,7 +285,7 @@ inline uint16_t CrixPlayer::rix_proc() uint8_t ctrl = 0; if(music_on == 0||pause_flag == 1) return 0; band = 0; - while(rix_buf[I] != 0x80 && I= length) return; + int i; uint8_t *baddr = (&rix_buf[ins_block])+(band_low<<6); @@ -306,6 +320,7 @@ inline void CrixPlayer::rix_get_ins() /*--------------------------------------------------------------*/ inline void CrixPlayer::rix_90_pro(uint16_t ctrl_l) { + if (ctrl_l >= 11) return; // modify[] has size 28 if(rhythm == 0 || ctrl_l < 6) { ins_to_reg(modify[ctrl_l*2],insbuf,insbuf[26]); @@ -317,7 +332,7 @@ inline void CrixPlayer::rix_90_pro(uint16_t ctrl_l) ins_to_reg(modify[ctrl_l*2+6],insbuf,insbuf[26]); return; } - else + else // same effect as 1st case, no need to handle separately { ins_to_reg(12,insbuf,insbuf[26]); ins_to_reg(15,insbuf+13,insbuf[27]); @@ -337,9 +352,10 @@ inline void CrixPlayer::rix_A0_pro(uint16_t ctrl_l,uint16_t index) /*--------------------------------------------------------------*/ inline void CrixPlayer::prepare_a0b0(uint16_t index,uint16_t v) /* important !*/ { + if (index >= 11) return; short high = 0,low = 0; uint32_t res; int res1 = (v-0x2000)*0x19; - if(res1 == (int)0xff) return; + if(res1 == (int)0xff) return; // impossible low = res1/0x2000; if(low < 0) { @@ -367,12 +383,13 @@ inline void CrixPlayer::prepare_a0b0(uint16_t index,uint16_t v) /* important !* /*--------------------------------------------------------------*/ inline void CrixPlayer::ad_a0b0l_reg(uint16_t index,uint16_t p2,uint16_t p3) { + if (index >= 11) return; uint16_t data; uint16_t i = p2+a0b0_data2[index]; a0b0_data4[index] = p3; a0b0_data3[index] = p2; i = ((signed short)i<=0x5F?i:0x5F); i = ((signed short)i>=0?i:0); - data = f_buffer[addrs_head[i]+displace[index]/2]; + data = f_buffer[addrs_head[i]+displace[index]/2]; // sum <= 11+24*24/2 < 300 ad_bop(0xA0+index,data); data = a0b0_data5[i]*4+(p3<1?0:0x20)+((data>>8)&3); ad_bop(0xB0+index,data); @@ -380,7 +397,8 @@ inline void CrixPlayer::ad_a0b0l_reg(uint16_t index,uint16_t p2,uint16_t p3) /*--------------------------------------------------------------*/ inline void CrixPlayer::rix_B0_pro(uint16_t ctrl_l,uint16_t index) { - register int temp = 0; + if (ctrl_l >= 11) return; + int temp = 0; if(rhythm == 0 || ctrl_l < 6) temp = modify[ctrl_l*2+1]; else { @@ -393,7 +411,7 @@ inline void CrixPlayer::rix_B0_pro(uint16_t ctrl_l,uint16_t index) /*--------------------------------------------------------------*/ inline void CrixPlayer::rix_C0_pro(uint16_t ctrl_l,uint16_t index) { - register uint16_t i = index>=12?index-12:0; + uint16_t i = index>=12?index-12:0; if(ctrl_l < 6 || rhythm == 0) { ad_a0b0l_reg(ctrl_l,i,1); @@ -429,7 +447,7 @@ inline void CrixPlayer::switch_ad_bd(uint16_t index) /*--------------------------------------------------------------*/ inline void CrixPlayer::ins_to_reg(uint16_t index,uint16_t* insb,uint16_t value) { - register uint16_t i; + uint16_t i; for(i=0;i<13;i++) reg_bufs[index].v[i] = insb[i]; reg_bufs[index].v[13] = value&3; ad_bd_reg(),ad_08_reg(), @@ -507,7 +525,7 @@ inline void CrixPlayer::ad_a0b0_reg(uint16_t index) /*--------------------------------------------------------------*/ inline void CrixPlayer::music_ctrl() { - register int i; + int i; for(i=0;i<11;i++) switch_ad_bd(i); } diff --git a/plugins/adplug/adplug/rix.h b/plugins/adplug/adplug/rix.h index 79c48ccd35..d1257b23f6 100644 --- a/plugins/adplug/adplug/rix.h +++ b/plugins/adplug/adplug/rix.h @@ -34,12 +34,14 @@ class CrixPlayer: public CPlayer bool update(); void rewind(int subsong); float getrefresh(); - uint32_t getsubsongs(); + unsigned int getsubsongs(); + unsigned int getsubsong(); std::string gettype() { return std::string("Softstar RIX OPL Music Format"); }; protected: + int song = 0; typedef struct { uint8_t v[14]; } ADDT; @@ -64,7 +66,7 @@ class CrixPlayer: public CPlayer static const uint8_t ad_C0_offs[18]; static const uint8_t modify[28]; static const uint8_t bd_reg_data[124]; - static uint8_t for40reg[18]; + uint8_t for40reg[18]; static const uint16_t mus_time; uint32_t I,T; uint16_t mus_block; diff --git a/plugins/adplug/adplug/rol.cpp b/plugins/adplug/adplug/rol.cpp index 30d99e0059..116b375f01 100644 --- a/plugins/adplug/adplug/rol.cpp +++ b/plugins/adplug/adplug/rol.cpp @@ -17,8 +17,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * rol.cpp - ROL Player by OPLx - * - * Source references ADLIB.C from Adlib MSC SDK. */ #include #include @@ -26,119 +24,16 @@ #include "rol.h" #include "debug.h" -#if !defined(UINT8_MAX) - typedef signed char int8_t; - typedef short int16_t; - typedef int int32_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; -#endif - - //--------------------------------------------------------- -static uint32_t const skMidPitch = 0x2000U; -static int16_t const skNrStepPitch = 25; // 25 steps within a half-tone for pitch bend +//--------------------------------------------------------- static int16_t const skVersionMajor = 4; static int16_t const skVersionMinor = 0; -static uint8_t const skMaxVolume = 0x7FU; -static uint8_t const skMaxNotes = 96U; -static uint8_t const skCarrierOpOffset = 3U; -static uint8_t const skNumSemitonesInOctave = 12U; -//--------------------------------------------------------- -static uint8_t const skOPL2_WaveCtrlBaseAddress = 0x01U; // Test LSI / Enable waveform control -static uint8_t const skOPL2_AaMultiBaseAddress = 0x20U; // Amp Mod / Vibrato / EG type / Key Scaling / Multiple -static uint8_t const skOPL2_KSLTLBaseAddress = 0x40U; // Key scaling level / Operator output level -static uint8_t const skOPL2_ArDrBaseAddress = 0x60U; // Attack Rate / Decay Rate -static uint8_t const skOPL2_SlrrBaseAddress = 0x80U; // Sustain Level / Release Rate -static uint8_t const skOPL2_FreqLoBaseAddress = 0xA0U; // Frequency (low 8 bits) -static uint8_t const skOPL2_KeyOnFreqHiBaseAddress = 0xB0U; // Key On / Octave / Frequency (high 2 bits) -static uint8_t const skOPL2_AmVibRhythmBaseAddress = 0xBDU; // AM depth / Vibrato depth / Rhythm control -static uint8_t const skOPL2_FeedConBaseAddress = 0xC0U; // Feedback strength / Connection type -static uint8_t const skOPL2_WaveformBaseAddress = 0xE0U; // Waveform select -//--------------------------------------------------------- -static uint8_t const skOPL2_EnableWaveformSelectMask = 0x20U; -static uint8_t const skOPL2_KeyOnMask = 0x20U; -static uint8_t const skOPL2_RhythmMask = 0x20U; -static uint8_t const skOPL2_KSLMask = 0xC0U; -static uint8_t const skOPL2_TLMask = 0x3FU; -static uint8_t const skOPL2_TLMinLevel = 0x3FU; -static uint8_t const skOPL2_FNumLSBMask = 0xFFU; -static uint8_t const skOPL2_FNumMSBMask = 0x03U; -static uint8_t const skOPL2_FNumMSBShift = 0x08U; -static uint8_t const skOPL2_BlockNumberShift = 0x02U; -//--------------------------------------------------------- -static uint8_t const skNoteOctave[skMaxNotes] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 -}; -//--------------------------------------------------------- -static uint8_t const skNoteIndex[skMaxNotes] = -{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 -}; -//--------------------------------------------------------- -// Table below generated by initialize_fnum_table function (from Adlib Music SDK). -static uint16_t const skFNumNotes[skNrStepPitch][skNumSemitonesInOctave] = -{ - 343, 364, 385, 408, 433, 459, 486, 515, 546, 579, 614, 650, - 344, 365, 387, 410, 434, 460, 488, 517, 548, 581, 615, 652, - 345, 365, 387, 410, 435, 461, 489, 518, 549, 582, 617, 653, - 346, 366, 388, 411, 436, 462, 490, 519, 550, 583, 618, 655, - 346, 367, 389, 412, 437, 463, 491, 520, 551, 584, 619, 657, - 347, 368, 390, 413, 438, 464, 492, 522, 553, 586, 621, 658, - 348, 369, 391, 415, 439, 466, 493, 523, 554, 587, 622, 660, - 349, 370, 392, 415, 440, 467, 495, 524, 556, 589, 624, 661, - 350, 371, 393, 416, 441, 468, 496, 525, 557, 590, 625, 663, - 351, 372, 394, 417, 442, 469, 497, 527, 558, 592, 627, 665, - 351, 372, 395, 418, 443, 470, 498, 528, 559, 593, 628, 666, - 352, 373, 396, 419, 444, 471, 499, 529, 561, 594, 630, 668, - 353, 374, 397, 420, 445, 472, 500, 530, 562, 596, 631, 669, - 354, 375, 398, 421, 447, 473, 502, 532, 564, 597, 633, 671, - 355, 376, 398, 422, 448, 474, 503, 533, 565, 599, 634, 672, - 356, 377, 399, 423, 449, 475, 504, 534, 566, 600, 636, 674, - 356, 378, 400, 424, 450, 477, 505, 535, 567, 601, 637, 675, - 357, 379, 401, 425, 451, 478, 506, 537, 569, 603, 639, 677, - 358, 379, 402, 426, 452, 479, 507, 538, 570, 604, 640, 679, - 359, 380, 403, 427, 453, 480, 509, 539, 571, 606, 642, 680, - 360, 381, 404, 428, 454, 481, 510, 540, 572, 607, 643, 682, - 360, 382, 405, 429, 455, 482, 511, 541, 574, 608, 645, 683, - 361, 383, 406, 430, 456, 483, 512, 543, 575, 610, 646, 685, - 362, 384, 407, 431, 457, 484, 513, 544, 577, 611, 648, 687, - 363, 385, 408, 432, 458, 485, 514, 545, 578, 612, 649, 688 -}; -//--------------------------------------------------------- -static uint8_t const drum_op_table[4] = { 0x14, 0x12, 0x15, 0x11 }; //--------------------------------------------------------- static inline float fmin(int const a, int const b) { return static_cast(a < b ? a : b); } //--------------------------------------------------------- -int const CrolPlayer::kSizeofDataRecord = 30; int const CrolPlayer::kMaxTickBeat = 60; -int const CrolPlayer::kSilenceNote = -12; -int const CrolPlayer::kNumMelodicVoices = 9; -int const CrolPlayer::kNumPercussiveVoices = 11; -int const CrolPlayer::kBassDrumChannel = 6; -int const CrolPlayer::kSnareDrumChannel = 7; -int const CrolPlayer::kTomtomChannel = 8; -int const CrolPlayer::kTomTomNote = 24; -int const CrolPlayer::kTomTomToSnare = 7; // 7 half-tones between voice 7 & 8 -int const CrolPlayer::kSnareNote = CrolPlayer::kTomTomNote + CrolPlayer::kTomTomToSnare; float const CrolPlayer::kDefaultUpdateTme = 18.2f; /*** public methods **************************************/ @@ -149,27 +44,14 @@ CPlayer * CrolPlayer::factory(Copl * pNewOpl) } //--------------------------------------------------------- CrolPlayer::CrolPlayer(Copl * const pNewOpl) - : CPlayer (pNewOpl) + : CcomposerBackend (pNewOpl) , mpROLHeader (NULL) - , mpOldFNumFreqPtr (NULL) , mTempoEvents () , mVoiceData () - , mInstrumentList () - , mFNumFreqPtrList (kNumPercussiveVoices, skFNumNotes[0]) - , mHalfToneOffset (kNumPercussiveVoices, 0) - , mVolumeCache (kNumPercussiveVoices, skMaxVolume) - , mKSLTLCache (kNumPercussiveVoices, 0) - , mNoteCache (kNumPercussiveVoices, 0) - , mKOnOctFNumCache (kNumMelodicVoices, 0) - , mKeyOnCache (kNumPercussiveVoices, false) , mRefresh (kDefaultUpdateTme) - , mOldPitchBendLength(~0) - , mPitchRangeStep (skNrStepPitch) , mNextTempoEvent (0) , mCurrTick (0) , mTimeOfLastNote (0) - , mOldHalfToneOffset (0) - , mAMVibRhythmCache (0) { } //--------------------------------------------------------- @@ -290,7 +172,7 @@ bool CrolPlayer::update() return true; } //--------------------------------------------------------- -void CrolPlayer::rewind(int subsong) +void CrolPlayer::frontend_rewind(int subsong) { TVoiceData::iterator curr = mVoiceData.begin(); TVoiceData::iterator end = mVoiceData.end(); @@ -303,28 +185,10 @@ void CrolPlayer::rewind(int subsong) ++curr; } - mHalfToneOffset = TInt16Vector(kNumPercussiveVoices, 0); - mVolumeCache = TUInt8Vector(kNumPercussiveVoices, skMaxVolume); - mKSLTLCache = TUInt8Vector(kNumPercussiveVoices, 0); - mNoteCache = TUInt8Vector(kNumPercussiveVoices, 0); - mKOnOctFNumCache = TUInt8Vector(kNumMelodicVoices, 0); - mKeyOnCache = TBoolVector(kNumPercussiveVoices, false); - mNextTempoEvent = 0; mCurrTick = 0; - mAMVibRhythmCache = 0; - - opl->init(); // initialize to melodic by default - opl->write(skOPL2_WaveCtrlBaseAddress, skOPL2_EnableWaveformSelectMask); // Enable waveform select - if (mpROLHeader->mode == 0) - { - mAMVibRhythmCache = skOPL2_RhythmMask; - opl->write(skOPL2_AmVibRhythmBaseAddress, mAMVibRhythmCache); // Enable rhythm mode - - SetFreq(kTomtomChannel, kTomTomNote); - SetFreq(kSnareDrumChannel, kSnareNote); - } + SetRhythmMode(mpROLHeader->mode ^ 1); SetRefresh(1.0f); } @@ -360,7 +224,7 @@ void CrolPlayer::UpdateVoice(int const voice, CVoiceData & voiceData) { if (iEvents[voiceData.next_instrument_event].time == mCurrTick) { - send_ins_data_to_chip(voice, iEvents[voiceData.next_instrument_event].ins_index); + SetInstrument(voice, iEvents[voiceData.next_instrument_event].ins_index); ++voiceData.next_instrument_event; } } @@ -378,7 +242,7 @@ void CrolPlayer::UpdateVoice(int const voice, CVoiceData & voiceData) { SVolumeEvent const & volumeEvent = vEvents[voiceData.next_volume_event]; - uint8_t const volume = (uint8_t)(skMaxVolume * volumeEvent.multiplier); + uint8_t const volume = (uint8_t)(kMaxVolume * volumeEvent.multiplier); SetVolume(voice, volume); @@ -402,14 +266,14 @@ void CrolPlayer::UpdateVoice(int const voice, CVoiceData & voiceData) { SNoteEvent const & noteEvent = nEvents[voiceData.current_note]; - SetNote(voice, noteEvent.number); + NoteOn(voice, noteEvent.number); voiceData.current_note_duration = 0; voiceData.mNoteDuration = noteEvent.duration; voiceData.mForceNote = false; } else { - SetNote(voice, kSilenceNote); + NoteOff(voice); voiceData.mEventStatus |= CVoiceData::kES_NoteEnd; return; } @@ -434,192 +298,20 @@ void CrolPlayer::UpdateVoice(int const voice, CVoiceData & voiceData) ++voiceData.current_note_duration; } //--------------------------------------------------------- -void CrolPlayer::SetNote(int const voice, int const note) -{ - if ((voice < kBassDrumChannel) || mpROLHeader->mode) - { - SetNoteMelodic(voice, note); - } - else - { - SetNotePercussive(voice, note); - } -} -//--------------------------------------------------------- -void CrolPlayer::SetNotePercussive(int const voice, int const note) -{ - int const channel_bit_mask = 1 << (4-voice+kBassDrumChannel); - - mAMVibRhythmCache &= ~channel_bit_mask; - opl->write(skOPL2_AmVibRhythmBaseAddress, mAMVibRhythmCache); - mKeyOnCache[voice] = false; - - if (note != kSilenceNote) - { - switch(voice) - { - case kTomtomChannel: - SetFreq(kTomtomChannel, note); - SetFreq(kSnareDrumChannel, note + kTomTomToSnare); - break; - - case kBassDrumChannel: - SetFreq(voice, note); - break; - default: - // Does nothing - break; - } - - mKeyOnCache[voice] = true; - mAMVibRhythmCache |= channel_bit_mask; - opl->write(skOPL2_AmVibRhythmBaseAddress, mAMVibRhythmCache); - } -} -//--------------------------------------------------------- -void CrolPlayer::SetNoteMelodic(int const voice, int const note) -{ - opl->write(skOPL2_KeyOnFreqHiBaseAddress + voice, mKOnOctFNumCache[voice] & ~skOPL2_KeyOnMask); - mKeyOnCache[voice] = false; - - if (note != kSilenceNote) - { - SetFreq(voice, note, true); - } -} -//--------------------------------------------------------- -// From Adlib Music SDK's ADLIB.C ... -void CrolPlayer::ChangePitch(int voice, uint16_t const pitchBend) -{ - int32_t const pitchBendLength = (int32_t)(pitchBend - skMidPitch) * mPitchRangeStep; - - if (mOldPitchBendLength == pitchBendLength) - { - // optimisation ... - mFNumFreqPtrList[voice] = mpOldFNumFreqPtr; - mHalfToneOffset[voice] = mOldHalfToneOffset; - } - else - { - int16_t const pitchStepDir = pitchBendLength / skMidPitch; - int16_t delta; - if (pitchStepDir < 0) - { - int16_t const pitchStepDown = skNrStepPitch - 1 - pitchStepDir; - mOldHalfToneOffset = mHalfToneOffset[voice] = -(pitchStepDown / skNrStepPitch); - delta = (pitchStepDown - skNrStepPitch + 1) % skNrStepPitch; - if (delta) - { - delta = skNrStepPitch - delta; - } - } - else - { - mOldHalfToneOffset = mHalfToneOffset[voice] = pitchStepDir / skNrStepPitch; - delta = pitchStepDir % skNrStepPitch; - } - mpOldFNumFreqPtr = mFNumFreqPtrList[voice] = skFNumNotes[delta]; - mOldPitchBendLength = pitchBendLength; - } -} -//--------------------------------------------------------- void CrolPlayer::SetPitch(int const voice, float const variation) { - if ((voice < kBassDrumChannel) || mpROLHeader->mode) - { - uint16_t const pitchBend = (variation == 1.0f) ? skMidPitch : static_cast((0x3fff >> 1) * variation); - - ChangePitch(voice, pitchBend); - SetFreq(voice, mNoteCache[voice], mKeyOnCache[voice]); - } -} -//--------------------------------------------------------- -void CrolPlayer::SetFreq(int const voice, int const note, bool const keyOn) -{ - int const biased_note = std::max(0, std::min((skMaxNotes-1), note + mHalfToneOffset[voice])); - - uint16_t const frequency = *(mFNumFreqPtrList[voice] + skNoteIndex[biased_note]); - - mNoteCache[voice] = note; - mKeyOnCache[voice] = keyOn; - - mKOnOctFNumCache[voice] = (skNoteOctave[biased_note] << skOPL2_BlockNumberShift) | ((frequency >> skOPL2_FNumMSBShift) & skOPL2_FNumMSBMask); - - opl->write(skOPL2_FreqLoBaseAddress + voice, frequency & skOPL2_FNumLSBMask); - opl->write(skOPL2_KeyOnFreqHiBaseAddress + voice, mKOnOctFNumCache[voice] | (keyOn ? skOPL2_KeyOnMask : 0x0)); -} -//--------------------------------------------------------- -uint8_t CrolPlayer::GetKSLTL(int const voice) const -{ - uint16_t kslTL = skOPL2_TLMinLevel - (mKSLTLCache[voice] & skOPL2_TLMask); // amplitude - - kslTL = mVolumeCache[voice] * kslTL; - kslTL += kslTL + skMaxVolume; // round off to 0.5 - kslTL = skOPL2_TLMinLevel - (kslTL / (2 * skMaxVolume)); - - kslTL |= mKSLTLCache[voice] & skOPL2_KSLMask; - - return static_cast(kslTL); -} -//--------------------------------------------------------- -void CrolPlayer::SetVolume(int const voice, uint8_t const volume) -{ - uint8_t const op_offset = (voice < kSnareDrumChannel || mpROLHeader->mode) ? op_table[voice] + skCarrierOpOffset : drum_op_table[voice - kSnareDrumChannel]; - - mVolumeCache[voice] = volume; - - opl->write(skOPL2_KSLTLBaseAddress + op_offset, GetKSLTL(voice)); -} -//--------------------------------------------------------- -void CrolPlayer::send_ins_data_to_chip(int const voice, int const ins_index) -{ - SRolInstrument const & instrument = mInstrumentList[ins_index].instrument; - - send_operator(voice, instrument.modulator, instrument.carrier); -} -//--------------------------------------------------------- -void CrolPlayer::send_operator(int const voice, SOPL2Op const & modulator, SOPL2Op const & carrier) -{ - if ((voice < kSnareDrumChannel) || mpROLHeader->mode) - { - uint8_t const op_offset = op_table[voice]; - - opl->write(skOPL2_AaMultiBaseAddress + op_offset, modulator.ammulti); - opl->write(skOPL2_KSLTLBaseAddress + op_offset, modulator.ksltl); - opl->write(skOPL2_ArDrBaseAddress + op_offset, modulator.ardr); - opl->write(skOPL2_SlrrBaseAddress + op_offset, modulator.slrr); - opl->write(skOPL2_FeedConBaseAddress + voice , modulator.fbc); - opl->write(skOPL2_WaveformBaseAddress + op_offset, modulator.waveform); - - mKSLTLCache[voice] = carrier.ksltl; - - opl->write(skOPL2_AaMultiBaseAddress + op_offset + skCarrierOpOffset, carrier.ammulti); - opl->write(skOPL2_KSLTLBaseAddress + op_offset + skCarrierOpOffset, GetKSLTL(voice)); - opl->write(skOPL2_ArDrBaseAddress + op_offset + skCarrierOpOffset, carrier.ardr); - opl->write(skOPL2_SlrrBaseAddress + op_offset + skCarrierOpOffset, carrier.slrr); - opl->write(skOPL2_WaveformBaseAddress + op_offset + skCarrierOpOffset, carrier.waveform); - } - else - { - uint8_t const op_offset = drum_op_table[voice-kSnareDrumChannel]; + uint16_t const pitchBend = (variation == 1.0f) ? kMidPitch : static_cast((0x3fff >> 1) * variation); - mKSLTLCache[voice] = modulator.ksltl; - - opl->write(skOPL2_AaMultiBaseAddress + op_offset, modulator.ammulti); - opl->write(skOPL2_KSLTLBaseAddress + op_offset, GetKSLTL(voice)); - opl->write(skOPL2_ArDrBaseAddress + op_offset, modulator.ardr); - opl->write(skOPL2_SlrrBaseAddress + op_offset, modulator.slrr); - opl->write(skOPL2_WaveformBaseAddress + op_offset, modulator.waveform); - } + ChangePitch(voice, pitchBend); } //--------------------------------------------------------- void CrolPlayer::load_tempo_events(binistream *f) { - int16_t const num_tempo_events = static_cast(f->readInt(2)); + uint16_t const num_tempo_events = static_cast(f->readInt(2)); mTempoEvents.reserve(num_tempo_events); - for (int i=0; i(f->readInt(2)); event.duration = static_cast(f->readInt(2)); - event.number += kSilenceNote; // adding -12 - note_events.push_back(event); total_duration += event.duration; - } while (total_duration < time_of_last_note); + } while (total_duration < time_of_last_note && !f->error()); if (time_of_last_note > mTimeOfLastNote) { @@ -698,22 +388,23 @@ void CrolPlayer::load_note_events(binistream *f, CVoiceData & voice) void CrolPlayer::load_instrument_events(binistream *f, CVoiceData & voice, binistream *bnk_file, SBnkHeader const & bnk_header) { - int16_t const number_of_instrument_events = static_cast(f->readInt(2)); + uint16_t const number_of_instrument_events = static_cast(f->readInt(2)); TInstrumentEvents & instrument_events = voice.instrument_events; instrument_events.reserve(number_of_instrument_events); - for (int16_t i = 0; i < number_of_instrument_events; ++i) + for (uint16_t i = 0; i < number_of_instrument_events; ++i) { SInstrumentEvent event; event.time = static_cast(f->readInt(2)); - f->readString(event.name, ROL_MAX_NAME_SIZE); + f->readString(event.name, INS_MAX_NAME_SIZE); + event.name[INS_MAX_NAME_SIZE - 1] = 0; std::string event_name = event.name; if (std::find(usedInstruments.begin(), usedInstruments.end(), event_name) == usedInstruments.end()) usedInstruments.push_back(event_name); - event.ins_index = load_rol_instrument(bnk_file, bnk_header, event_name); + event.ins_index = load_bnk_instrument(bnk_file, bnk_header, event_name); instrument_events.push_back(event); @@ -725,13 +416,13 @@ void CrolPlayer::load_instrument_events(binistream *f, CVoiceData & voice, //--------------------------------------------------------- void CrolPlayer::load_volume_events(binistream *f, CVoiceData & voice) { - int16_t const number_of_volume_events = static_cast(f->readInt(2)); + uint16_t const number_of_volume_events = static_cast(f->readInt(2)); TVolumeEvents & volume_events = voice.volume_events; volume_events.reserve(number_of_volume_events); - for (int i=0; i(f->readInt(2)); @@ -745,13 +436,13 @@ void CrolPlayer::load_volume_events(binistream *f, CVoiceData & voice) //--------------------------------------------------------- void CrolPlayer::load_pitch_events(binistream *f, CVoiceData & voice) { - int16_t const number_of_pitch_events = static_cast(f->readInt(2)); + uint16_t const number_of_pitch_events = static_cast(f->readInt(2)); TPitchEvents & pitch_events = voice.pitch_events; pitch_events.reserve(number_of_pitch_events); - for (int i=0; i(f->readInt(2)); @@ -760,124 +451,3 @@ void CrolPlayer::load_pitch_events(binistream *f, CVoiceData & voice) pitch_events.push_back(event); } } -//--------------------------------------------------------- -bool CrolPlayer::load_bnk_info(binistream *f, SBnkHeader & header) -{ - header.version_major = static_cast(f->readInt(1)); - header.version_minor = static_cast(f->readInt(1)); - f->readString(header.signature, ROL_BNK_SIGNATURE_SIZE); - - header.number_of_list_entries_used = static_cast(f->readInt(2)); - header.total_number_of_list_entries = static_cast(f->readInt(2)); - - header.abs_offset_of_name_list = static_cast(f->readInt(4)); - header.abs_offset_of_data = static_cast(f->readInt(4)); - - f->seek(header.abs_offset_of_name_list, binio::Set); - - TInstrumentNames & ins_name_list = header.ins_name_list; - ins_name_list.reserve(header.number_of_list_entries_used); - - for (uint16_t i=0; i(f->readInt(2)); - instrument.record_used = static_cast(f->readInt(1)); - f->readString(instrument.name, ROL_MAX_NAME_SIZE); - - ins_name_list.push_back( instrument ); - } - - return true; -} -//--------------------------------------------------------- -int CrolPlayer::load_rol_instrument(binistream *f, SBnkHeader const & header, std::string const & name) -{ - TInstrumentNames const & ins_name_list = header.ins_name_list; - - int const ins_index = get_ins_index(name); - - if (ins_index != -1) - { - return ins_index; - } - - SInstrument usedInstrument; - usedInstrument.name = name; - - typedef TInstrumentNames::const_iterator TInsIter; - typedef std::pair TInsIterPair; - - TInsIterPair const range = std::equal_range(ins_name_list.begin(), - ins_name_list.end(), - name, - StringCompare()); - - if (range.first != range.second) - { - int const seekOffs = header.abs_offset_of_data + (range.first->index * kSizeofDataRecord); - f->seek(seekOffs, binio::Set); - - read_rol_instrument(f, usedInstrument.instrument); - } - else - { - // set up default instrument data here - memset(&usedInstrument.instrument, 0, sizeof(SRolInstrument)); - } - - mInstrumentList.push_back( usedInstrument ); - - return mInstrumentList.size()-1; -} -//--------------------------------------------------------- -int CrolPlayer::get_ins_index(std::string const & name) const -{ - for (size_t index = 0; index(f->readInt(1)); - instrument.voice_number = static_cast(f->readInt(1)); - - read_fm_operator(f, instrument.modulator); - read_fm_operator(f, instrument.carrier); - - instrument.modulator.waveform = static_cast(f->readInt(1)); - instrument.carrier.waveform = static_cast(f->readInt(1)); -} -//--------------------------------------------------------- -void CrolPlayer::read_fm_operator(binistream *f, SOPL2Op &opl2_op) -{ - SFMOperator fm_op; - - fm_op.key_scale_level = static_cast(f->readInt(1)); - fm_op.freq_multiplier = static_cast(f->readInt(1)); - fm_op.feed_back = static_cast(f->readInt(1)); - fm_op.attack_rate = static_cast(f->readInt(1)); - fm_op.sustain_level = static_cast(f->readInt(1)); - fm_op.sustaining_sound = static_cast(f->readInt(1)); - fm_op.decay_rate = static_cast(f->readInt(1)); - fm_op.release_rate = static_cast(f->readInt(1)); - fm_op.output_level = static_cast(f->readInt(1)); - fm_op.amplitude_vibrato = static_cast(f->readInt(1)); - fm_op.frequency_vibrato = static_cast(f->readInt(1)); - fm_op.envelope_scaling = static_cast(f->readInt(1)); - fm_op.fm_type = static_cast(f->readInt(1)); - - opl2_op.ammulti = fm_op.amplitude_vibrato << 7 | fm_op.frequency_vibrato << 6 | fm_op.sustaining_sound << 5 | fm_op.envelope_scaling << 4 | fm_op.freq_multiplier; - opl2_op.ksltl = fm_op.key_scale_level << 6 | fm_op.output_level; - opl2_op.ardr = fm_op.attack_rate << 4 | fm_op.decay_rate; - opl2_op.slrr = fm_op.sustain_level << 4 | fm_op.release_rate; - opl2_op.fbc = fm_op.feed_back << 1 | (fm_op.fm_type ^ 1); -} diff --git a/plugins/adplug/adplug/rol.h b/plugins/adplug/adplug/rol.h index 76212864b6..e936d705fc 100644 --- a/plugins/adplug/adplug/rol.h +++ b/plugins/adplug/adplug/rol.h @@ -17,8 +17,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * rol.h - ROL Player by OPLx - * - * Source references ADLIB.C from Adlib MSC SDK. */ #ifndef H_ROLPLAYER #define H_ROLPLAYER @@ -26,7 +24,7 @@ #include #include -#include "player.h" +#include "composer.h" // These are here since Visual C 6 doesn't support statics declared and defined in class. #define ROL_COMMENT_SIZE 40U @@ -35,55 +33,37 @@ #define ROL_FILLER0_SIZE 38U #define ROL_FILLER1_SIZE 15U #define ROL_FILLER_SIZE 15U -#define ROL_MAX_NAME_SIZE 9U #define ROL_INSTRUMENT_EVENT_FILLER_SIZE 3U // 1 for filler, 2 for unused -#define ROL_BNK_SIGNATURE_SIZE 6U -class CrolPlayer: public CPlayer +class CrolPlayer: public CcomposerBackend { public: static CPlayer *factory(Copl * pNewOpl); explicit CrolPlayer(Copl * const pNewOpl); - virtual ~CrolPlayer(); + ~CrolPlayer(); - virtual bool load (const std::string &filename, const CFileProvider &fp); - virtual bool update (); - virtual void rewind (int subsong); // rewinds to specified subsong - virtual float getrefresh(); // returns needed timer refresh rate + bool load (const std::string &filename, const CFileProvider &fp); + bool update (); + void frontend_rewind(int subsong); // rewinds to specified subsong + float getrefresh(); // returns needed timer refresh rate - virtual std::string gettype() { return std::string("AdLib Visual Composer"); } - virtual unsigned int getinstruments() + unsigned int getinstruments() { return usedInstruments.size(); }; - virtual std::string getinstrument(unsigned int n) + std::string getinstrument(unsigned int n) { - return usedInstruments[n]; + return n < usedInstruments.size() ? usedInstruments[n] : std::string(); }; - virtual std::string getdesc() + std::string getdesc() { - return std::string(mpROLHeader->comment); + return strcmp(mpROLHeader->comment, "\\roll\\default") ? std::string(mpROLHeader->comment) : std::string(); }; private: -#if !defined(UINT8_MAX) - typedef signed char int8_t; - typedef short int16_t; - typedef int int32_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; -#endif - -#ifdef __x86_64__ - typedef signed int int32; -#else - typedef signed long int int32; -#endif - typedef struct { uint16_t version_major; @@ -116,7 +96,7 @@ class CrolPlayer: public CPlayer typedef struct { int16_t time; - char name[ROL_MAX_NAME_SIZE]; + char name[INS_MAX_NAME_SIZE]; int16_t ins_index; } SInstrumentEvent; @@ -190,69 +170,6 @@ class CrolPlayer: public CPlayer bool mForceNote; }; - typedef struct - { - uint16_t index; - uint8_t record_used; - char name[ROL_MAX_NAME_SIZE]; - } SInstrumentName; - - typedef std::vector TInstrumentNames; - - typedef struct - { - uint8_t version_major; - uint8_t version_minor; - char signature[ROL_BNK_SIGNATURE_SIZE]; - uint16_t number_of_list_entries_used; - uint16_t total_number_of_list_entries; - int32 abs_offset_of_name_list; - int32 abs_offset_of_data; - - TInstrumentNames ins_name_list; - } SBnkHeader; - - typedef struct - { - uint8_t key_scale_level; - uint8_t freq_multiplier; - uint8_t feed_back; - uint8_t attack_rate; - uint8_t sustain_level; - uint8_t sustaining_sound; - uint8_t decay_rate; - uint8_t release_rate; - uint8_t output_level; - uint8_t amplitude_vibrato; - uint8_t frequency_vibrato; - uint8_t envelope_scaling; - uint8_t fm_type; - } SFMOperator; - - typedef struct - { - uint8_t ammulti; - uint8_t ksltl; - uint8_t ardr; - uint8_t slrr; - uint8_t fbc; - uint8_t waveform; - } SOPL2Op; - - typedef struct - { - uint8_t mode; - uint8_t voice_number; - SOPL2Op modulator; - SOPL2Op carrier; - } SRolInstrument; - - typedef struct - { - std::string name; - SRolInstrument instrument; - } SInstrument; - void load_tempo_events (binistream *f); bool load_voice_data (binistream *f, std::string const & bnk_filename, CFileProvider const & fp); void load_note_events (binistream *f, CVoiceData & voice); @@ -261,92 +178,24 @@ class CrolPlayer: public CPlayer void load_volume_events (binistream *f, CVoiceData & voice); void load_pitch_events (binistream *f, CVoiceData & voice); - bool load_bnk_info (binistream *f, SBnkHeader & header); - int load_rol_instrument (binistream *f, SBnkHeader const & header, std::string const & name); - void read_rol_instrument (binistream *f, SRolInstrument & ins); - void read_fm_operator (binistream *f, SOPL2Op & opl2_op); - int get_ins_index (std::string const & name) const; - void UpdateVoice(int const voice, CVoiceData & voiceData); - void SetNote(int const voice, int const note); - void SetNoteMelodic(int const voice, int const note); - void SetNotePercussive(int const voice, int const note); - void SetFreq (int const voice, int const note, bool const keyOn=false); - void ChangePitch(int voice, uint16_t const pitchBend); void SetPitch (int const voice, float const variation); - void SetVolume (int const voice, uint8_t const volume); void SetRefresh(float const multiplier); - uint8_t GetKSLTL(int const voice) const; - void send_ins_data_to_chip(int const voice, int const ins_index); - void send_operator(int const voice, SOPL2Op const & modulator, SOPL2Op const & carrier); - - class StringCompare - { - public: - bool operator()(SInstrumentName const & lhs, SInstrumentName const & rhs) const - { - return keyLess(lhs.name, rhs.name); - } - - bool operator()(SInstrumentName const & lhs, std::string const &rhs) const - { - return keyLess(lhs.name, rhs.c_str()); - } - - bool operator()(std::string const & lhs, SInstrumentName const & rhs) const - { - return keyLess(lhs.c_str(), rhs.name); - } - private: - bool keyLess(char const * const lhs, char const * const rhs) const - { - return stricmp(lhs, rhs) < 0; - } - }; - typedef uint16_t const * TUint16ConstPtr; typedef std::vector TTempoEvents; typedef std::vector TVoiceData; - typedef std::vector TInstrumentList; - typedef std::vector TUint16PtrVector; - typedef std::vector TInt16Vector; - typedef std::vector TUInt8Vector; - typedef std::vector TBoolVector; typedef std::vector TStringVector; SRolHeader * mpROLHeader; - TUint16ConstPtr mpOldFNumFreqPtr; TTempoEvents mTempoEvents; TVoiceData mVoiceData; - TInstrumentList mInstrumentList; - TUint16PtrVector mFNumFreqPtrList; - TInt16Vector mHalfToneOffset; - TUInt8Vector mVolumeCache; - TUInt8Vector mKSLTLCache; - TUInt8Vector mNoteCache; - TUInt8Vector mKOnOctFNumCache; - TBoolVector mKeyOnCache; float mRefresh; - int32_t mOldPitchBendLength; - uint16_t mPitchRangeStep; uint16_t mNextTempoEvent; int16_t mCurrTick; int16_t mTimeOfLastNote; - int16_t mOldHalfToneOffset; - uint8_t mAMVibRhythmCache; TStringVector usedInstruments; - static int const kSizeofDataRecord; static int const kMaxTickBeat; - static int const kSilenceNote; - static int const kNumMelodicVoices; - static int const kNumPercussiveVoices; - static int const kBassDrumChannel; - static int const kSnareDrumChannel; - static int const kTomtomChannel; - static int const kTomTomNote; - static int const kTomTomToSnare; - static int const kSnareNote; static float const kDefaultUpdateTme; }; diff --git a/plugins/adplug/adplug/s3m.cpp b/plugins/adplug/adplug/s3m.cpp index e1acb1f450..b123880c74 100644 --- a/plugins/adplug/adplug/s3m.cpp +++ b/plugins/adplug/adplug/s3m.cpp @@ -22,17 +22,27 @@ * Extra Fine Slides (EEx, FEx) & Fine Vibrato (Uxy) are inaccurate */ +#include #include #include "s3m.h" +#include "debug.h" -const signed char Cs3mPlayer::chnresolv[] = // S3M -> adlib channel conversion - {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,-1,-1,-1,-1,-1,-1,-1}; +// S3M -> adlib channel conversion +static const signed char chnresolv[32] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1 +}; -const unsigned short Cs3mPlayer::notetable[12] = // S3M adlib note table - {340,363,385,408,432,458,485,514,544,577,611,647}; +// S3M adlib note table +static const unsigned short notetable[12] = { + 340, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647 +}; -const unsigned char Cs3mPlayer::vibratotab[32] = // vibrato rate table - {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}; +// vibrato rate table +static const unsigned char vibratotab[32] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 +}; /*** public methods *************************************/ @@ -43,14 +53,12 @@ CPlayer *Cs3mPlayer::factory(Copl *newopl) Cs3mPlayer::Cs3mPlayer(Copl *newopl): CPlayer(newopl) { - int i,j,k; + memset(orders, 0xff, sizeof(orders)); + memset(pattern, 0xff, sizeof(pattern)); - memset(pattern,255,sizeof(pattern)); - memset(orders,255,sizeof(orders)); - - for(i=0;i<99;i++) // setup pattern - for(j=0;j<64;j++) - for(k=0;k<32;k++) { + for (int i = 0; i < 99; i++) // setup pattern + for (int j = 0; j < 64; j++) + for (int k = 0; k < 32; k++) { pattern[i][j][k].instrument = 0; pattern[i][j][k].info = 0; } @@ -60,52 +68,54 @@ bool Cs3mPlayer::load(const std::string &filename, const CFileProvider &fp) { binistream *f = fp.open(filename); if(!f) return false; unsigned short insptr[99],pattptr[99]; - int i,row; - unsigned char bufval,bufval2; - unsigned short ppatlen; - s3mheader *checkhead; - bool adlibins=false; - - // file validation section - checkhead = new s3mheader; - load_header(f, checkhead); - if(checkhead->kennung != 0x1a || checkhead->typ != 16 - || checkhead->insnum > 99) { - delete checkhead; fp.close(f); return false; - } else - if(strncmp(checkhead->scrm,"SCRM",4)) { - delete checkhead; fp.close(f); return false; - } else { // is an adlib module? - f->seek(checkhead->ordnum, binio::Add); - for(i = 0; i < checkhead->insnum; i++) - insptr[i] = f->readInt(2); - for(i=0;iinsnum;i++) { - f->seek(insptr[i]*16); - if(f->readInt(1) >= 2) { - adlibins = true; - break; - } - } - delete checkhead; - if(!adlibins) { fp.close(f); return false; } - } + int i; - // load section - f->seek(0); // rewind for load - load_header(f, &header); // read header - - // security check - if(header.ordnum > 256 || header.insnum > 99 || header.patnum > 99) { + // load header + f->readString(header.name, 28); + header.id = f->readInt(1); + header.type = f->readInt(1); + f->ignore(2); + header.ordnum = f->readInt(2); + header.insnum = f->readInt(2); + header.patnum = f->readInt(2); + header.flags = f->readInt(2); + header.cwtv = f->readInt(2); + header.ffi = f->readInt(2); + f->readString(header.scrm, 4); + header.gv = f->readInt(1); + header.is = f->readInt(1); + header.it = f->readInt(1); + header.mv = f->readInt(1); + header.uc = f->readInt(1); + header.dp = f->readInt(1); + f->ignore(8); + header.special = f->readInt(2); + for (i = 0; i < 32; i++) + header.chanset[i] = f->readInt(1); + + // validate header + if (header.id != 0x1a || header.type != 16 || + memcmp(header.scrm, "SCRM", 4) || + header.ordnum > 256 || header.insnum > 99 || header.patnum > 99) { fp.close(f); return false; } - for(i = 0; i < header.ordnum; i++) orders[i] = f->readInt(1); // read orders - for(i = 0; i < header.insnum; i++) insptr[i] = f->readInt(2); // instrument parapointers - for(i = 0; i < header.patnum; i++) pattptr[i] = f->readInt(2); // pattern parapointers - - for(i=0;iseek(insptr[i]*16); + // load section + for (i = 0; i < header.ordnum; i++) // read orders + orders[i] = f->readInt(1); + for (i = 0; i < header.insnum; i++) // instrument offsets + insptr[i] = f->readInt(2); + for (i = 0; i < header.patnum; i++) // pattern offsets + pattptr[i] = f->readInt(2); + + int adlibins = 0; + for (i = 0; i < header.insnum; i++) { // load instruments + f->seek(insptr[i] * 16); + if (f->error()) { + fp.close(f); + return false; + } inst[i].type = f->readInt(1); f->readString(inst[i].filename, 15); inst[i].d00 = f->readInt(1); inst[i].d01 = f->readInt(1); @@ -114,34 +124,33 @@ bool Cs3mPlayer::load(const std::string &filename, const CFileProvider &fp) inst[i].d06 = f->readInt(1); inst[i].d07 = f->readInt(1); inst[i].d08 = f->readInt(1); inst[i].d09 = f->readInt(1); inst[i].d0a = f->readInt(1); inst[i].d0b = f->readInt(1); - inst[i].volume = f->readInt(1); inst[i].dsk = f->readInt(1); + inst[i].volume = f->readInt(1); + inst[i].dsk = f->readInt(1); f->ignore(2); inst[i].c2spd = f->readInt(4); f->ignore(12); f->readString(inst[i].name, 28); f->readString(inst[i].scri, 4); + if (inst[i].type >= 2) { + adlibins++; + if (memcmp(inst[i].scri, "SCRI", 4)) { + fp.close(f); + return false; + } + } + } + if (!adlibins) { // no adlib instrument found + fp.close(f); + return false; } - for(i=0;iseek(pattptr[i]*16); - ppatlen = f->readInt(2); - unsigned long pattpos = f->pos(); - for(row=0;(row<64) && (pattpos-pattptr[i]*16<=ppatlen);row++) - do { - bufval = f->readInt(1); - if(bufval & 32) { - bufval2 = f->readInt(1); - pattern[i][row][bufval & 31].note = bufval2 & 15; - pattern[i][row][bufval & 31].oct = (bufval2 & 240) >> 4; - pattern[i][row][bufval & 31].instrument = f->readInt(1); - } - if(bufval & 64) - pattern[i][row][bufval & 31].volume = f->readInt(1); - if(bufval & 128) { - pattern[i][row][bufval & 31].command = f->readInt(1); - pattern[i][row][bufval & 31].info = f->readInt(1); - } - } while(bufval); + for (i = 0; i < header.patnum; i++) { // load patterns + f->seek(pattptr[i] * 16); + if (f->error()) { + fp.close(f); + return false; + } + load_pattern(i, f, f->readInt(2)); } fp.close(f); @@ -149,242 +158,309 @@ bool Cs3mPlayer::load(const std::string &filename, const CFileProvider &fp) return true; // done } +size_t Cs3mPlayer::load_pattern(int pat, binistream *f, size_t length) { + struct { // closure to keep track of amount read + binistream *f; + size_t length, count; + unsigned char read() { return count++ < length ? f->readInt(1) : 0; } + } fs = {f, length, 0}; + + // read and unpack pattern data + for (int row = 0; row < 64 && fs.count < length; row++) { + while (unsigned char token = fs.read()) { + s3mevent &ev = pattern[pat][row][token & 0x1f]; + + if (token & 0x20) { // note + instrument? + unsigned char val = fs.read(); + ev.note = val & 0x0f; + ev.oct = val >> 4; + ev.instrument = fs.read(); + } + + if (token & 0x40) // volume? + ev.volume = fs.read(); + + if (token & 0x80) { // command? + ev.command = fs.read(); + ev.info = fs.read(); + } + } + } + return fs.count; +} + bool Cs3mPlayer::update() { - unsigned char pattbreak=0,donote; // remember vars - unsigned char pattnr,chan,row,info; // cache vars - signed char realchan; - - // effect handling (timer dependant) - for(realchan=0; realchan<9; realchan++) { - info = channel[realchan].info; // fill infobyte cache - switch(channel[realchan].fx) { - case 11: - case 12: if(channel[realchan].fx == 11) // dual command: H00 and Dxy - vibrato(realchan,channel[realchan].dualinfo); - else // dual command: G00 and Dxy - tone_portamento(realchan,channel[realchan].dualinfo); - case 4: if(info <= 0x0f) { // volume slide down - if(channel[realchan].vol - info >= 0) - channel[realchan].vol -= info; - else - channel[realchan].vol = 0; + // effect handling (timer dependent) + for (int realchan = 0; realchan < 9; realchan++) { + s3mchan &c = channel[realchan]; // shortcut ref + const unsigned char info = c.info; // fill infobyte cache + + switch (c.fx) { + case 4: // volume slide + volslide: + if (info <= 0x0f) // volume slide down + c.vol = std::max(c.vol - info, 0); + if (!(info & 0x0f)) // volume slide up + c.vol = std::min((c.vol + info) >> 4, 63); + setvolume(realchan); + break; + + case 5: // slide down + if (info == 0xf0 || info <= 0xe0) { + slide_down(realchan, info); + setfreq(realchan); } - if((info & 0x0f) == 0) { // volume slide up - if(channel[realchan].vol + (info >> 4) <= 63) - channel[realchan].vol += info >> 4; - else - channel[realchan].vol = 63; + break; + + case 6: // slide up + if (info == 0xf0 || info <= 0xe0) { + slide_up(realchan, info); + setfreq(realchan); } - setvolume(realchan); break; - case 5: if(info == 0xf0 || info <= 0xe0) { // slide down - slide_down(realchan,info); - setfreq(realchan); - } + + case 7: // tone portamento + tone_portamento(realchan, c.dualinfo); break; - case 6: if(info == 0xf0 || info <= 0xe0) { // slide up - slide_up(realchan,info); - setfreq(realchan); - } + + case 8: // vibrato + vibrato(realchan, c.dualinfo); break; - case 7: tone_portamento(realchan,channel[realchan].dualinfo); break; // tone portamento - case 8: vibrato(realchan,channel[realchan].dualinfo); break; // vibrato - case 10: channel[realchan].nextfreq = channel[realchan].freq; // arpeggio - channel[realchan].nextoct = channel[realchan].oct; - switch(channel[realchan].trigger) { - case 0: channel[realchan].freq = notetable[channel[realchan].note]; break; - case 1: if(channel[realchan].note + ((info & 0xf0) >> 4) < 12) - channel[realchan].freq = notetable[channel[realchan].note + ((info & 0xf0) >> 4)]; - else { - channel[realchan].freq = notetable[channel[realchan].note + ((info & 0xf0) >> 4) - 12]; - channel[realchan].oct++; - } + + case 10: // arpeggio + c.nextfreq = c.freq; + c.nextoct = c.oct; + switch (c.trigger) { + case 0: + c.freq = notetable[c.note]; break; - case 2: if(channel[realchan].note + (info & 0x0f) < 12) - channel[realchan].freq = notetable[channel[realchan].note + (info & 0x0f)]; - else { - channel[realchan].freq = notetable[channel[realchan].note + (info & 0x0f) - 12]; - channel[realchan].oct++; - } + case 1: + c.freq = notetable[(c.note + (info >> 4)) % 12]; + c.oct += (c.note + (info >> 4)) / 12; + break; + case 2: + c.freq = notetable[(c.note + (info & 0x0f)) % 12]; + c.oct += (c.note + (info & 0x0f)) / 12; break; } - if(channel[realchan].trigger < 2) - channel[realchan].trigger++; - else - channel[realchan].trigger = 0; + if (++c.trigger > 2) c.trigger = 0; setfreq(realchan); - channel[realchan].freq = channel[realchan].nextfreq; - channel[realchan].oct = channel[realchan].nextoct; + c.freq = c.nextfreq; + c.oct = c.nextoct; + break; + + case 11: // dual command: H00 and Dxy + vibrato(realchan, c.dualinfo); + goto volslide; + + case 12: // dual command: G00 and Dxy + tone_portamento(realchan, c.dualinfo); + goto volslide; + + case 21: // fine vibrato + vibrato(realchan, info / 4); break; - case 21: vibrato(realchan,(unsigned char) (info / 4)); break; // fine vibrato } } - if(del) { // speed compensation + if (del) { // speed compensation del--; return !songend; } // arrangement handling - pattnr = orders[ord]; - if(pattnr == 0xff || ord > header.ordnum) { // "--" end of song - songend = 1; // set end-flag - ord = 0; - pattnr = orders[ord]; - if(pattnr == 0xff) - return !songend; - } - if(pattnr == 0xfe) { // "++" skip marker - ord++; pattnr = orders[ord]; + unsigned char pattnr; + for (int end = 0;;) { + pattnr = ord < header.ordnum ? orders[ord] : 0xff; + if (pattnr < header.patnum) break; // pattern is valid + + switch (pattnr) { + default: // skip invalid pattern + AdPlug_LogWrite("Invalid pattern %d number (order %d)\n", pattnr, ord); + // fallthrough; + case 0xfe: // "++" skip marker + if (ord + 1 < header.ordnum) { + ord++; + break; + } + // else fallthrough; + case 0xff: // "--" end of song + ord = 0; + songend = 1; + if (end++) return !songend; // no order is a valid pattern + } } // play row - row = crow; // fill row cache - for(chan=0;chan<32;chan++) { - if(!(header.chanset[chan] & 128)) // resolve S3M -> AdLib channels - realchan = chnresolv[header.chanset[chan] & 127]; - else - realchan = -1; // channel disabled - if(realchan != -1) { // channel playable? - // set channel values - donote = 0; - if(pattern[pattnr][row][chan].note < 14) { - // tone portamento - if(pattern[pattnr][row][chan].command == 7 || pattern[pattnr][row][chan].command == 12) { - channel[realchan].nextfreq = notetable[pattern[pattnr][row][chan].note]; - channel[realchan].nextoct = pattern[pattnr][row][chan].oct; - } else { // normal note - channel[realchan].note = pattern[pattnr][row][chan].note; - channel[realchan].freq = notetable[pattern[pattnr][row][chan].note]; - channel[realchan].oct = pattern[pattnr][row][chan].oct; - channel[realchan].key = 1; - donote = 1; - } - } - if(pattern[pattnr][row][chan].note == 14) { // key off (is 14 here, cause note is only first 4 bits) - channel[realchan].key = 0; - setfreq(realchan); + bool pattbreak = false; + const unsigned char row = crow; // fill row cache + for (int chan = 0; chan < 32; chan++) { + // resolve S3M -> AdLib channels + const int realchan = header.chanset[chan] & 0x80 ? -1 : // channel disabled? + chnresolv[header.chanset[chan] & 0x1f]; + if (realchan < 0) continue; // channel playable? + + s3mchan &c = channel[realchan]; + const s3mevent &ev = pattern[pattnr][row][chan]; + + // set channel values + bool donote = false; + + // note + if (ev.note < 12) { + if (ev.command == 7 || ev.command == 12) { // tone portamento + c.nextfreq = notetable[ev.note]; + c.nextoct = ev.oct; + } else { // normal note + c.note = ev.note; + c.freq = notetable[ev.note]; + c.oct = ev.oct; + c.key = 1; + donote = true; } - if((channel[realchan].fx != 8 && channel[realchan].fx != 11) && // vibrato begins - (pattern[pattnr][row][chan].command == 8 || pattern[pattnr][row][chan].command == 11)) { - channel[realchan].nextfreq = channel[realchan].freq; - channel[realchan].nextoct = channel[realchan].oct; - } - if(pattern[pattnr][row][chan].note >= 14) - if((channel[realchan].fx == 8 || channel[realchan].fx == 11) && // vibrato ends - (pattern[pattnr][row][chan].command != 8 && pattern[pattnr][row][chan].command != 11)) { - channel[realchan].freq = channel[realchan].nextfreq; - channel[realchan].oct = channel[realchan].nextoct; - setfreq(realchan); - } - if(pattern[pattnr][row][chan].instrument) { // set instrument - channel[realchan].inst = pattern[pattnr][row][chan].instrument - 1; - if(inst[channel[realchan].inst].volume < 64) - channel[realchan].vol = inst[channel[realchan].inst].volume; - else - channel[realchan].vol = 63; - if(pattern[pattnr][row][chan].command != 7) - donote = 1; - } - if(pattern[pattnr][row][chan].volume != 255) { - if(pattern[pattnr][row][chan].volume < 64) // set volume - channel[realchan].vol = pattern[pattnr][row][chan].volume; - else - channel[realchan].vol = 63; - } - channel[realchan].fx = pattern[pattnr][row][chan].command; // set command - if(pattern[pattnr][row][chan].info) // set infobyte - channel[realchan].info = pattern[pattnr][row][chan].info; + } + + // key off (is 14 here, cause note is only first 4 bits) + if (ev.note == 14) { + c.key = 0; + setfreq(realchan); + } - // some commands reset the infobyte memory - switch(channel[realchan].fx) { + // vibrato begins + if ((c.fx != 8 && c.fx != 11) && (ev.command == 8 || ev.command == 11)) { + c.nextfreq = c.freq; + c.nextoct = c.oct; + } + // vibrato ends + if ((ev.note >= 14) && + (c.fx == 8 || c.fx == 11) && (ev.command != 8 && ev.command != 11)) { + c.freq = c.nextfreq; + c.oct = c.nextoct; + setfreq(realchan); + } + + // set instrument + if (ev.instrument > 0 && ev.instrument <= header.insnum) { + c.inst = ev.instrument - 1; + c.vol = std::min(inst[c.inst].volume, (unsigned char)63); + if (ev.command != 7) + donote = true; + } + + // set command & infobyte + c.fx = ev.command; + if (ev.info) + c.info = ev.info; + // some commands reset the infobyte memory + switch (c.fx) { case 1: case 2: case 3: - case 20: - channel[realchan].info = pattern[pattnr][row][chan].info; - break; - } + case 20: c.info = ev.info; + } - // play note - if(donote) + // play note + if (donote) playnote(realchan); - if(pattern[pattnr][row][chan].volume != 255) // set volume - setvolume(realchan); - - // command handling (row dependant) - info = channel[realchan].info; // fill infobyte cache - switch(channel[realchan].fx) { - case 1: speed = info; break; // set speed - case 2: if(info <= ord) songend = 1; ord = info; crow = 0; pattbreak = 1; break; // jump to order - case 3: if(!pattbreak) { crow = info; ord++; pattbreak = 1; } break; // pattern break - case 4: if(info > 0xf0) { // fine volume down - if(channel[realchan].vol - (info & 0x0f) >= 0) - channel[realchan].vol -= info & 0x0f; - else - channel[realchan].vol = 0; - } - if((info & 0x0f) == 0x0f && info >= 0x1f) { // fine volume up - if(channel[realchan].vol + ((info & 0xf0) >> 4) <= 63) - channel[realchan].vol += (info & 0xf0) >> 4; - else - channel[realchan].vol = 63; - } - setvolume(realchan); - break; - case 5: if(info > 0xf0) { // fine slide down - slide_down(realchan,(unsigned char) (info & 0x0f)); + + // set volume + if (ev.volume != 0xff) { + c.vol = std::min(ev.volume, (unsigned char)63); + setvolume(realchan); + } + + // fill infobyte cache + const unsigned char info = c.info, infoL = info & 0x0f, infoH = info >> 4; + // command handling (row dependent) + switch (c.fx) { + case 1: // set speed + speed = info; + break; + + case 2: // jump to order + if (info <= ord) songend = 1; + ord = info; + crow = 0; + pattbreak = true; + break; + + case 3: // pattern break + if (!pattbreak) { + crow = info; + if (!++ord) songend = 1; + pattbreak = 1; + } + break; + + case 4: + if (info > 0xf0) // fine volume down + c.vol = std::max(c.vol - infoL, 0); + if (infoL == 0x0f && infoH) // fine volume up + c.vol = std::min(c.vol + infoH, 63); + setvolume(realchan); + break; + + case 5: + if (info > 0xf0) { // fine slide down + slide_down(realchan, infoL); setfreq(realchan); } - if(info > 0xe0 && info < 0xf0) { // extra fine slide down - slide_down(realchan,(unsigned char) ((info & 0x0f) / 4)); - setfreq(realchan); - } - break; - case 6: if(info > 0xf0) { // fine slide up - slide_up(realchan,(unsigned char) (info & 0x0f)); + if (info > 0xe0 && info < 0xf0) { // extra fine slide down + slide_down(realchan, infoL / 4); setfreq(realchan); } - if(info > 0xe0 && info < 0xf0) { // extra fine slide up - slide_up(realchan,(unsigned char) ((info & 0x0f) / 4)); - setfreq(realchan); - } - break; - case 7: // tone portamento - case 8: if((channel[realchan].fx == 7 || // vibrato (remember info for dual commands) - channel[realchan].fx == 8) && pattern[pattnr][row][chan].info) - channel[realchan].dualinfo = info; - break; - case 10: channel[realchan].trigger = 0; break; // arpeggio (set trigger) - case 19: if(info == 0xb0) // set loop start + break; + + case 6: + if (info > 0xf0) { // fine slide up + slide_up(realchan, infoL); + setfreq(realchan); + } + if(info > 0xe0 && info < 0xf0) { // extra fine slide up + slide_up(realchan, infoL / 4); + setfreq(realchan); + } + break; + + case 7: // tone portamento + case 8: // vibrato (remember info for dual commands) + if (ev.info) c.dualinfo = info; + break; + + case 10: // arpeggio (set trigger) + c.trigger = 0; + break; + + case 19: + if (info == 0xb0) { // set loop start loopstart = row; - if(info > 0xb0 && info <= 0xbf) { // pattern loop - if(!loopcnt) { - loopcnt = info & 0x0f; - crow = loopstart; - pattbreak = 1; - } else - if(--loopcnt > 0) { - crow = loopstart; - pattbreak = 1; - } + } else if (infoH == 0xb) { // pattern loop + if (!loopcnt) { + loopcnt = infoL; + crow = loopstart; + pattbreak = true; + } else if(--loopcnt > 0) { + crow = loopstart; + pattbreak = true; } - if((info & 0xf0) == 0xe0) // patterndelay - del = speed * (info & 0x0f) - 1; - break; - case 20: tempo = info; break; // set tempo } + if (infoH == 0xe) // patterndelay + del = speed * infoL - 1; + break; + + case 20: // set tempo + tempo = info; + break; } } - if(!del) + if (!del) del = speed - 1; // speed compensation - if(!pattbreak) { // next row (only if no manual advance) - crow++; - if(crow > 63) { + if (!pattbreak) { // next row (only if no manual advance) + if (++crow > 63) { crow = 0; - ord++; + if (!++ord) songend = 1; loopstart = 0; } } @@ -395,91 +471,80 @@ bool Cs3mPlayer::update() void Cs3mPlayer::rewind(int subsong) { // set basic variables - songend = 0; ord = 0; crow = 0; tempo = header.it; - speed = header.is; del = 0; loopstart = 0; loopcnt = 0; + songend = 0; + ord = 0; + crow = 0; + tempo = header.it; + speed = header.is; + del = 0; + loopstart = 0; + loopcnt = 0; - memset(channel,0,sizeof(channel)); + memset(channel, 0, sizeof(channel)); opl->init(); // reset OPL chip - opl->write(1,32); // Go to ym3812 mode + opl->write(1, 32); // Go to ym3812 mode } std::string Cs3mPlayer::gettype() { - char filever[5]; - - switch(header.cwtv) { // determine version number - case 0x1300: strcpy(filever,"3.00"); break; - case 0x1301: strcpy(filever,"3.01"); break; - case 0x1303: strcpy(filever,"3.03"); break; - case 0x1320: strcpy(filever,"3.20"); break; - default: strcpy(filever,"3.??"); - } + std::string s("Scream Tracker "); - return (std::string("Scream Tracker ") + filever); + switch (header.cwtv) { // determine version number + case 0x1300: return s + "3.00"; + case 0x1301: return s + "3.01"; + case 0x1303: return s + "3.03"; + case 0x1320: return s + "3.20"; + } + return s + "3.??"; } float Cs3mPlayer::getrefresh() { - return (float) (tempo / 2.5); + return tempo / 2.5f; } /*** private methods *************************************/ -void Cs3mPlayer::load_header(binistream *f, s3mheader *h) -{ - int i; - - f->readString(h->name, 28); - h->kennung = f->readInt(1); h->typ = f->readInt(1); - f->ignore(2); - h->ordnum = f->readInt(2); h->insnum = f->readInt(2); - h->patnum = f->readInt(2); h->flags = f->readInt(2); - h->cwtv = f->readInt(2); h->ffi = f->readInt(2); - f->readString(h->scrm, 4); - h->gv = f->readInt(1); h->is = f->readInt(1); h->it = f->readInt(1); - h->mv = f->readInt(1); h->uc = f->readInt(1); h->dp = f->readInt(1); - f->ignore(8); - h->special = f->readInt(2); - for(i = 0; i < 32; i++) h->chanset[i] = f->readInt(1); -} - void Cs3mPlayer::setvolume(unsigned char chan) { - unsigned char op = op_table[chan], insnr = channel[chan].inst; - - opl->write(0x43 + op,(int)(63-((63-(inst[insnr].d03 & 63))/63.0)*channel[chan].vol) + (inst[insnr].d03 & 192)); - if(inst[insnr].d0a & 1) - opl->write(0x40 + op,(int)(63-((63-(inst[insnr].d02 & 63))/63.0)*channel[chan].vol) + (inst[insnr].d02 & 192)); + const s3minst &instr = inst[channel[chan].inst]; + unsigned char op = op_table[chan], vol = channel[chan].vol; + + opl->write(0x43 + op, + (63*63 - (~instr.d03 & 63) * vol) / 63 + (instr.d03 & 192)); + if (instr.d0a & 1) + opl->write(0x40 + op, + (63*63 - (~instr.d02 & 63) * vol) / 63 + (instr.d02 & 192)); } void Cs3mPlayer::setfreq(unsigned char chan) { - opl->write(0xa0 + chan, channel[chan].freq & 255); - if(channel[chan].key) - opl->write(0xb0 + chan, (((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2)) | 32); - else - opl->write(0xb0 + chan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2)); + opl->write(0xa0 + chan, channel[chan].freq & 0xff); + opl->write(0xb0 + chan, (channel[chan].freq >> 8 & 0x03) + + (channel[chan].oct << 2 & 0x1c) + + (channel[chan].key ? 0x20 : 0)); } void Cs3mPlayer::playnote(unsigned char chan) { - unsigned char op = op_table[chan], insnr = channel[chan].inst; + const s3minst &instr = inst[channel[chan].inst]; + unsigned char op = op_table[chan]; opl->write(0xb0 + chan, 0); // stop old note // set instrument data - opl->write(0x20 + op, inst[insnr].d00); - opl->write(0x23 + op, inst[insnr].d01); - opl->write(0x40 + op, inst[insnr].d02); - opl->write(0x43 + op, inst[insnr].d03); - opl->write(0x60 + op, inst[insnr].d04); - opl->write(0x63 + op, inst[insnr].d05); - opl->write(0x80 + op, inst[insnr].d06); - opl->write(0x83 + op, inst[insnr].d07); - opl->write(0xe0 + op, inst[insnr].d08); - opl->write(0xe3 + op, inst[insnr].d09); - opl->write(0xc0 + chan, inst[insnr].d0a); + opl->write(0x20 + op, instr.d00); + opl->write(0x23 + op, instr.d01); + opl->write(0x40 + op, instr.d02); + opl->write(0x43 + op, instr.d03); + opl->write(0x60 + op, instr.d04); + opl->write(0x63 + op, instr.d05); + opl->write(0x80 + op, instr.d06); + opl->write(0x83 + op, instr.d07); + opl->write(0xe0 + op, instr.d08); + opl->write(0xe3 + op, instr.d09); + opl->write(0xc0 + chan, instr.d0a); // set frequency & play channel[chan].key = 1; @@ -488,56 +553,60 @@ void Cs3mPlayer::playnote(unsigned char chan) void Cs3mPlayer::slide_down(unsigned char chan, unsigned char amount) { - if(channel[chan].freq - amount > 340) - channel[chan].freq -= amount; - else - if(channel[chan].oct > 0) { - channel[chan].oct--; - channel[chan].freq = 684; - } else - channel[chan].freq = 340; + s3mchan &c = channel[chan]; + + if (c.freq > 340 + amount) { + c.freq -= amount; + } else if (c.oct > 0) { + c.oct--; + c.freq = 684; + } else { + c.freq = 340; + } } void Cs3mPlayer::slide_up(unsigned char chan, unsigned char amount) { - if(channel[chan].freq + amount < 686) - channel[chan].freq += amount; - else - if(channel[chan].oct < 7) { - channel[chan].oct++; - channel[chan].freq = 341; - } else - channel[chan].freq = 686; + s3mchan &c = channel[chan]; + + if (c.freq + amount < 686) { + c.freq += amount; + } else if (c.oct < 7) { + c.oct++; + c.freq = 341; + } else { + c.freq = 686; + } } void Cs3mPlayer::vibrato(unsigned char chan, unsigned char info) { - unsigned char i,speed,depth; - - speed = info >> 4; - depth = (info & 0x0f) / 2; - - for(i=0;i= 64) - channel[chan].trigger -= 64; - if(channel[chan].trigger >= 16 && channel[chan].trigger < 48) - slide_down(chan,(unsigned char) (vibratotab[channel[chan].trigger - 16] / (16-depth))); - if(channel[chan].trigger < 16) - slide_up(chan,(unsigned char) (vibratotab[channel[chan].trigger + 16] / (16-depth))); - if(channel[chan].trigger >= 48) - slide_up(chan,(unsigned char) (vibratotab[channel[chan].trigger - 48] / (16-depth))); + unsigned char &trigger = channel[chan].trigger; + + unsigned char speed = info >> 4; + unsigned char depth = 16 - (info & 0x0f) / 2; + + for (unsigned char i = 0; i < speed; i++) { + trigger = (trigger + 1) % 64; + + if (trigger >= 16 && trigger < 48) + slide_down(chan, vibratotab[trigger - 16] / depth); + else if (trigger < 16) + slide_up(chan, vibratotab[trigger + 16] / depth); + else // trigger >= 48 + slide_up(chan, vibratotab[trigger - 48] / depth); } setfreq(chan); } void Cs3mPlayer::tone_portamento(unsigned char chan, unsigned char info) { - if(channel[chan].freq + (channel[chan].oct << 10) < channel[chan].nextfreq + - (channel[chan].nextoct << 10)) + const s3mchan &c = channel[chan]; + + if (c.freq + (c.oct << 10) < c.nextfreq + (c.nextoct << 10)) slide_up(chan,info); - if(channel[chan].freq + (channel[chan].oct << 10) > channel[chan].nextfreq + - (channel[chan].nextoct << 10)) + if (c.freq + (c.oct << 10) > c.nextfreq + (c.nextoct << 10)) slide_down(chan,info); + setfreq(chan); } diff --git a/plugins/adplug/adplug/s3m.h b/plugins/adplug/adplug/s3m.h index b082712f8c..bd1c0ddee4 100644 --- a/plugins/adplug/adplug/s3m.h +++ b/plugins/adplug/adplug/s3m.h @@ -60,7 +60,7 @@ class Cs3mPlayer: public CPlayer protected: struct s3mheader { char name[28]; // song name - unsigned char kennung,typ,dummy[2]; + unsigned char id,type,dummy[2]; unsigned short ordnum,insnum,patnum,flags,cwtv,ffi; char scrm[4]; unsigned char gv,is,it,mv,uc,dp,dummy2[8]; @@ -76,11 +76,11 @@ class Cs3mPlayer: public CPlayer char dummy2[12], name[28],scri[4]; } inst[99]; - struct { + struct s3mevent { unsigned char note,oct,instrument,volume,command,info; } pattern[99][64][32]; - struct { + struct s3mchan { unsigned short freq,nextfreq; unsigned char oct,vol,inst,fx,info,dualinfo,key,nextoct,trigger,note; } channel[9]; @@ -89,12 +89,9 @@ class Cs3mPlayer: public CPlayer unsigned char orders[256]; unsigned char crow,ord,speed,tempo,del,songend,loopstart,loopcnt; - private: - static const signed char chnresolv[]; - static const unsigned short notetable[12]; - static const unsigned char vibratotab[32]; + size_t load_pattern(int pat, binistream *f, size_t length); - void load_header(binistream *f, s3mheader *h); + private: void setvolume(unsigned char chan); void setfreq(unsigned char chan); void playnote(unsigned char chan); diff --git a/plugins/adplug/adplug/sa2.cpp b/plugins/adplug/adplug/sa2.cpp index 436e459286..56e64fdd1d 100644 --- a/plugins/adplug/adplug/sa2.cpp +++ b/plugins/adplug/adplug/sa2.cpp @@ -21,8 +21,7 @@ */ #include -#include -#include +#include #include "sa2.h" #include "debug.h" @@ -35,19 +34,18 @@ CPlayer *Csa2Loader::factory(Copl *newopl) bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp) { binistream *f = fp.open(filename); if(!f) return false; - struct { - unsigned char data[11],arpstart,arpspeed,arppos,arpspdcnt; - } insts; unsigned char buf; - int i,j, k, notedis = 0; - const unsigned char convfx[16] = {0,1,2,3,4,5,6,255,8,255,10,11,12,13,255,15}; + unsigned int i,j, k, notedis = 0; + static const unsigned char convfx[16] = { + 0, 1, 2, 3, 4, 5, 6, 255, 8, 255, 10, 11, 12, 13, 255, 15 + }; unsigned char sat_type; enum SAT_TYPE { - HAS_ARPEGIOLIST = (1 << 7), + HAS_ARPEGGIOLIST = (1 << 7), HAS_V7PATTERNS = (1 << 6), HAS_ACTIVECHANNELS = (1 << 5), HAS_TRACKORDER = (1 << 4), - HAS_ARPEGIO = (1 << 3), + HAS_ARPEGGIO = (1 << 3), HAS_OLDBPM = (1 << 2), HAS_OLDPATTERNS = (1 << 1), HAS_UNKNOWN127 = (1 << 0) @@ -58,7 +56,10 @@ bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp) header.version = f->readInt(1); // file validation section - if(strncmp(header.sadt,"SAdT",4)) { fp.close(f); return false; } + if(memcmp(header.sadt, "SAdT", 4)) { + fp.close(f); + return false; + } switch(header.version) { case 1: notedis = +0x18; @@ -74,23 +75,23 @@ bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp) break; case 4: notedis = +0x0c; - sat_type = HAS_ARPEGIO | HAS_OLDPATTERNS | HAS_OLDBPM; + sat_type = HAS_ARPEGGIO | HAS_OLDPATTERNS | HAS_OLDBPM; break; case 5: notedis = +0x0c; - sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_OLDPATTERNS | HAS_OLDBPM; + sat_type = HAS_ARPEGGIO | HAS_ARPEGGIOLIST | HAS_OLDPATTERNS | HAS_OLDBPM; break; case 6: - sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_OLDPATTERNS | HAS_OLDBPM; + sat_type = HAS_ARPEGGIO | HAS_ARPEGGIOLIST | HAS_OLDPATTERNS | HAS_OLDBPM; break; case 7: - sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_V7PATTERNS; + sat_type = HAS_ARPEGGIO | HAS_ARPEGGIOLIST | HAS_V7PATTERNS; break; case 8: - sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_TRACKORDER; + sat_type = HAS_ARPEGGIO | HAS_ARPEGGIOLIST | HAS_TRACKORDER; break; case 9: - sat_type = HAS_ARPEGIO | HAS_ARPEGIOLIST | HAS_TRACKORDER | HAS_ACTIVECHANNELS; + sat_type = HAS_ARPEGGIO | HAS_ARPEGGIOLIST | HAS_TRACKORDER | HAS_ACTIVECHANNELS; break; default: /* unknown */ fp.close(f); @@ -99,26 +100,20 @@ bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp) // load section // instruments - for(i = 0; i < 31; i++) { - if(sat_type & HAS_ARPEGIO) { - for(j = 0; j < 11; j++) insts.data[j] = f->readInt(1); - insts.arpstart = f->readInt(1); - insts.arpspeed = f->readInt(1); - insts.arppos = f->readInt(1); - insts.arpspdcnt = f->readInt(1); - inst[i].arpstart = insts.arpstart; - inst[i].arpspeed = insts.arpspeed; - inst[i].arppos = insts.arppos; - inst[i].arpspdcnt = insts.arpspdcnt; + for (i = 0; i < 31; i++) { + for (j = 0; j < 11; j++) + inst[i].data[j] = f->readInt(1); + if (sat_type & HAS_ARPEGGIO) { + inst[i].arpstart = f->readInt(1); + inst[i].arpspeed = f->readInt(1); + inst[i].arppos = f->readInt(1); + inst[i].arpspdcnt = f->readInt(1); } else { - for(j = 0; j < 11; j++) insts.data[j] = f->readInt(1); inst[i].arpstart = 0; inst[i].arpspeed = 0; inst[i].arppos = 0; inst[i].arpspdcnt = 0; } - for(j=0;j<11;j++) - inst[i].data[j] = insts.data[j]; inst[i].misc = 0; inst[i].slide = 0; } @@ -132,6 +127,18 @@ bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp) // infos nop = f->readInt(2); length = f->readInt(1); restartpos = f->readInt(1); + // checks + if (nop < 1 || nop > 64 || + length < 1 || length > 128 || + restartpos >= length) { + fp.close(f); + return false; + } + for (i = 0; i < length; i++) // check order + if (order[i] >= nop /* or 64 */) { + fp.close(f); + return false; + } // bpm bpm = f->readInt(2); @@ -139,20 +146,20 @@ bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp) bpm = bpm * 125 / 50; // cps -> bpm } - if(sat_type & HAS_ARPEGIOLIST) { + if(sat_type & HAS_ARPEGGIOLIST) { init_specialarp(); for(i = 0; i < 256; i++) arplist[i] = f->readInt(1); // arpeggio list for(i = 0; i < 256; i++) arpcmd[i] = f->readInt(1); // arpeggio commands } - for(i=0;i<64;i++) { // track orders - for(j=0;j<9;j++) { - if(sat_type & HAS_TRACKORDER) + for (i = 0; i < 64; i++) { // track orders + for (j = 0; j < 9; j++) { + if (sat_type & HAS_TRACKORDER) { + // the value read should be < 9 * nop, but can't cause invalid accesses trackord[i][j] = f->readInt(1); - else - { - trackord[i][j] = i * 9 + j; - } + } else { + trackord[i][j] = i * 9 + j; // + 1 ??? + } } } @@ -166,7 +173,7 @@ bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp) // track data if(sat_type & HAS_OLDPATTERNS) { i = 0; - while(!f->ateof()) { + while (i < 64 * 9 && !f->ateof()) { for(j=0;j<64;j++) { for(k=0;k<9;k++) { buf = f->readInt(1); @@ -182,7 +189,7 @@ bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp) } else if(sat_type & HAS_V7PATTERNS) { i = 0; - while(!f->ateof()) { + while (i < 64 * 9 && !f->ateof()) { for(j=0;j<64;j++) { for(k=0;k<9;k++) { buf = f->readInt(1); @@ -200,7 +207,7 @@ bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp) } } else { i = 0; - while(!f->ateof()) { + while (i < 64 * 9 && !f->ateof()) { for(j=0;j<64;j++) { buf = f->readInt(1); tracks[i][j].note = buf >> 1; @@ -230,34 +237,38 @@ bool Csa2Loader::load(const std::string &filename, const CFileProvider &fp) std::string Csa2Loader::gettype() { char tmpstr[40]; - - sprintf(tmpstr,"Surprise! Adlib Tracker 2 (version %d)",header.version); + snprintf(tmpstr,sizeof(tmpstr),"Surprise! Adlib Tracker 2 (version %d)",header.version); return std::string(tmpstr); } std::string Csa2Loader::gettitle() { - char bufinst[29*17],buf[18]; - int i,ptr; + char buf[29 * 17 - 1]; + int i, j, len = 0, ptr = 0, spaces = 0; // parse instrument names for song name - memset(bufinst,'\0',29*17); - for(i=0;i<29;i++) { - buf[16] = ' '; buf[17] = '\0'; - memcpy(buf,instname[i]+1,16); - for(ptr=16;ptr>0;ptr--) - if(buf[ptr] == ' ') - buf[ptr] = '\0'; - else { - if(ptr<16) - buf[ptr+1] = ' '; - break; - } - strcat(bufinst,buf); + + for (i = 0; i < 29; i++) + for (j = 1; j < 17; j++) + if (instname[i][j] == '"') goto title_start; + + return std::string(); + + for (/*i = 0*/; i < 29; i++) { + for (j = 1; j < 17; j++) { + if (instname[i][j] == ' ') + spaces++; + else + spaces = 0; + if (instname[i][j] == '"') + len = ptr; + buf[ptr++] = instname[i][j]; + title_start:; + } + ptr -= spaces; + spaces = 1; + buf[ptr++] = ' '; } - if(strchr(bufinst,'"')) - return std::string(bufinst,strchr(bufinst,'"')-bufinst+1,strrchr(bufinst,'"')-strchr(bufinst,'"')-1); - else - return std::string(); + return std::string(buf, len); } diff --git a/plugins/adplug/adplug/sa2.h b/plugins/adplug/adplug/sa2.h index 1bfaa22aa0..a20fb61495 100644 --- a/plugins/adplug/adplug/sa2.h +++ b/plugins/adplug/adplug/sa2.h @@ -39,8 +39,8 @@ class Csa2Loader: public CmodPlayer { return 31; } std::string getinstrument(unsigned int n) { - if(n < 29) - return std::string(instname[n],1,16); + if (n < 29) + return std::string(&instname[n][1], 16); else return std::string("-broken-"); } diff --git a/plugins/adplug/adplug/sixdepack.cpp b/plugins/adplug/adplug/sixdepack.cpp new file mode 100644 index 0000000000..f9f2b04b76 --- /dev/null +++ b/plugins/adplug/adplug/sixdepack.cpp @@ -0,0 +1,237 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2007 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * sixdepack.cpp - Based on sixpack.c by Philip G. Gage, April 1991 + * + */ + +#include "sixdepack.h" + +unsigned short Sixdepak::bitvalue(unsigned short bit) +{ + assert(bit < copybits(COPYRANGES - 1)); + return 1 << bit; +} + +unsigned short Sixdepak::copybits(unsigned short range) +{ + assert(range < COPYRANGES); + return 2 * range + 4; // 4, 6, 8, 10, 12, 14 +} + +unsigned short Sixdepak::copymin(unsigned short range) +{ + assert(range < COPYRANGES); + /* + if (range > 0 ) + return bitvalue(copybits(range - 1)) + copymin(range - 1); + else + return 0; + */ + static const unsigned short table[COPYRANGES] = { + 0, 16, 80, 336, 1360, 5456}; + return table[range]; +} + +void Sixdepak::inittree() +{ + unsigned short i; + + for (i = 2; i <= TWICEMAX; i++) { + dad[i] = i / 2; + freq[i] = 1; + } + + for (i = 1; i <= MAXCHAR; i++) { + leftc[i] = 2 * i; + rghtc[i] = 2 * i + 1; + } +} + +void Sixdepak::updatefreq(unsigned short a, unsigned short b) +{ + for (;;) { + freq[dad[a]] = freq[a] + freq[b]; + a = dad[a]; + + if (a == ROOT) + break; + + if (leftc[dad[a]] == a) + b = rghtc[dad[a]]; + else + b = leftc[dad[a]]; + } + + if (freq[ROOT] == MAXFREQ) + for (a = 1; a <= TWICEMAX; a++) + freq[a] >>= 1; +} + +void Sixdepak::updatemodel(unsigned short code) +{ + unsigned short a = code + SUCCMAX, b, c, code1, code2; + + freq[a]++; + if (dad[a] != ROOT) { + code1 = dad[a]; + if (leftc[code1] == a) + updatefreq(a, rghtc[code1]); + else + updatefreq(a, leftc[code1]); + + do { + code2 = dad[code1]; + if (leftc[code2] == code1) + b = rghtc[code2]; + else + b = leftc[code2]; + + if (freq[a] > freq[b]) { + if (leftc[code2] == code1) + rghtc[code2] = a; + else + leftc[code2] = a; + + if (leftc[code1] == a) { + leftc[code1] = b; + c = rghtc[code1]; + } else { + rghtc[code1] = b; + c = leftc[code1]; + } + + dad[b] = code1; + dad[a] = code2; + updatefreq(b, c); + a = b; + } + + a = dad[a]; + code1 = dad[a]; + } while (code1 != ROOT); + } +} + +unsigned short Sixdepak::inputcode(unsigned short bits) +{ + unsigned short i, code = 0; + + for (i = 1; i <= bits; i++) { + if (!ibitcount) { + if (ibufcount == input_size) + return 0; + ibitbuffer = wdbuf[ibufcount]; + ibufcount++; + ibitcount = 15; + } else + ibitcount--; + + if (ibitbuffer & 0x8000) + code |= bitvalue(i - 1); + ibitbuffer <<= 1; + } + + return code; +} + +unsigned short Sixdepak::uncompress() +{ + unsigned short a = 1; + + do { + if (!ibitcount) { + if (ibufcount == input_size) + return TERMINATE; + ibitbuffer = wdbuf[ibufcount]; + ibufcount++; + ibitcount = 15; + } else + ibitcount--; + + if (ibitbuffer & 0x8000) + a = rghtc[a]; + else + a = leftc[a]; + ibitbuffer <<= 1; + } while (a <= MAXCHAR); + + a -= SUCCMAX; + updatemodel(a); + return a; +} + +size_t Sixdepak::do_decode() +{ + size_t obufcount = ibufcount = 0; + ibitcount = 0; + ibitbuffer = 0; + + inittree(); + + for (;;) { + unsigned short c = uncompress(); + + if (c == TERMINATE) { + return obufcount; + } else if (c < 256) { + if (obufcount == output_size) + return output_size; + + obuf[obufcount++] = (unsigned char)c; + } else { + unsigned short t = c - FIRSTCODE, + index = t / CODESPERRANGE, + len = t + MINCOPY - index * CODESPERRANGE, + dist = inputcode(copybits(index)) + copymin(index) + len; + + for (int i = 0; i < len; i++) { + if (obufcount == output_size) + return output_size; + + obuf[obufcount] = dist > obufcount ? 0 : obuf[obufcount - dist]; + obufcount++; + } + } + } +} + +Sixdepak::Sixdepak( + unsigned short *in, size_t isize, unsigned char *out, size_t osize) : input_size(isize), output_size(osize), wdbuf(in), obuf(out) +{ +} + +size_t Sixdepak::decode( + unsigned short *source, size_t srcbytes, + unsigned char *dest, size_t dstbytes) +{ + if (srcbytes < 2 || srcbytes > MAXBUF - 4096 /*why?*/ || dstbytes < 1) + return 0; + // There is no real reason to enforce upper bounds, but removing + // the checks changes behaviour for non-compliant inputs. + if (dstbytes > MAXBUF) + dstbytes = MAXBUF; + + // The constructor wants input size in words, not bytes. + Sixdepak *decoder = new Sixdepak(source, srcbytes / 2, dest, dstbytes); + + size_t out_size = decoder->do_decode(); + + delete decoder; + return out_size; +} diff --git a/plugins/adplug/adplug/sixdepack.h b/plugins/adplug/adplug/sixdepack.h new file mode 100644 index 0000000000..97113671c3 --- /dev/null +++ b/plugins/adplug/adplug/sixdepack.h @@ -0,0 +1,71 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2007 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * sixdepack.h - Based on sixpack.c by Philip G. Gage, April 1991 + * + */ + +#ifndef SIXDEPACK_H +#define SIXDEPACK_H + +#include +#include + +class Sixdepak { +public: + enum { + COPYRANGES = 6, + MINCOPY = 3, + MAXCOPY = 255, + CODESPERRANGE = MAXCOPY - MINCOPY + 1, + ROOT = 1, + TERMINATE = 256, + FIRSTCODE = 257, + MAXCHAR = FIRSTCODE + COPYRANGES * CODESPERRANGE - 1, + SUCCMAX = MAXCHAR + 1, + TWICEMAX = 2 * MAXCHAR + 1, + MAXFREQ = 2000, + MAXDISTANCE = 21839, // (1 << copybits(COPYRANGES-1)) - 1 + copymin(COPYRANGES-1) + MAXSIZE = MAXDISTANCE + MAXCOPY, + MAXBUF = 42 * 1024, + }; + + static size_t decode(unsigned short *source, size_t srcbytes, unsigned char *dest, size_t dstbytes); + +private: + static unsigned short bitvalue(unsigned short bit); + static unsigned short copybits(unsigned short range); + static unsigned short copymin(unsigned short range); + + void inittree(); + void updatefreq(unsigned short a, unsigned short b); + void updatemodel(unsigned short code); + unsigned short inputcode(unsigned short bits); + unsigned short uncompress(); + size_t do_decode(); + Sixdepak(unsigned short *in, size_t isize, unsigned char *out, size_t osize); + + unsigned short ibitcount, ibitbuffer; + unsigned short leftc[MAXCHAR + 1], rghtc[MAXCHAR + 1]; + unsigned short dad[TWICEMAX + 1], freq[TWICEMAX + 1]; + size_t ibufcount, input_size, output_size; + unsigned short *wdbuf; + unsigned char *obuf; +}; + +#endif diff --git a/plugins/adplug/adplug/sng.cpp b/plugins/adplug/adplug/sng.cpp index 0a42805684..62358bd8d3 100644 --- a/plugins/adplug/adplug/sng.cpp +++ b/plugins/adplug/adplug/sng.cpp @@ -73,7 +73,9 @@ bool CsngPlayer::update() if(!header.compressed) opl->write(data[pos].reg, data[pos].val); - if(data[pos].val) del = data[pos].val - 1; pos++; + if(data[pos].val) del = data[pos].val - 1; + pos++; + if(pos >= header.length) { songend = true; pos = header.loop; } return !songend; } diff --git a/plugins/adplug/adplug/sop.cpp b/plugins/adplug/adplug/sop.cpp index 405fd8c98c..244be30f5f 100644 --- a/plugins/adplug/adplug/sop.cpp +++ b/plugins/adplug/adplug/sop.cpp @@ -46,6 +46,7 @@ bool CsopPlayer::load(const std::string &filename, const CFileProvider &fp) fp.close(f); return false; } + f->setFlag(binio::BigEndian, false); /****** static header validation ******/ @@ -96,7 +97,7 @@ bool CsopPlayer::load(const std::string &filename, const CFileProvider &fp) nInsts = f->readInt(1); check = f->readInt(1); if (nTracks == 0 || nInsts == 0 || nTracks > SOP_MAX_TRACK || nInsts > SOP_MAX_INST || - check != 0 || fp.filesize(f) < SOP_HEAD_SIZE + nTracks) + check != 0 || fp.filesize(f) < (unsigned)SOP_HEAD_SIZE + nTracks) { fp.close(f); return false; @@ -134,7 +135,16 @@ bool CsopPlayer::load(const std::string &filename, const CFileProvider &fp) return false; } sop_sample sample; - f->readString((char *)&sample, sizeof(sample)); + sample.val1 = f->readInt(2); + sample.val2 = f->readInt(2); + sample.length = f->readInt(2); + sample.val4 = f->readInt(2); + sample.base_freq = f->readInt(2); + sample.val6 = f->readInt(2); + sample.val7 = f->readInt(2); + sample.val8 = f->readInt(1); + sample.val9 = f->readInt(2); + sample.val10 = f->readInt(2); if (fp.filesize(f) - f->pos() < sample.length) { fp.close(f); @@ -164,7 +174,8 @@ bool CsopPlayer::load(const std::string &filename, const CFileProvider &fp) } // event tracks track = new sop_trk[nTracks + 1]; - for (i = 0; i < nTracks + 1; i++) + for (i = 0; i < (unsigned)nTracks + 1; i++) track[i].data = 0; + for (i = 0; i < (unsigned)nTracks + 1; i++) { track[i].nEvents = f->readInt(2); track[i].size = f->readInt(4); @@ -597,35 +608,35 @@ void Cad262Driver::SetVoiceVolume_SOP(unsigned chan, unsigned vol) volume = 63 - VolumeTable[((63 - ((KSL_value = Ksl2[chan]) & 0x3F)) << 7) + vol]; if (chan >= 11) - SndOutput3(VolReg[chan - 11] - 3, KSL_value & 0xC0 | volume); + SndOutput3(VolReg[chan - 11] - 3, (KSL_value & 0xC0) | volume); else - SndOutput1((percussion ? VolReg[chan + 11] : VolReg[chan]) - 3, KSL_value & 0xC0 | volume); + SndOutput1((percussion ? VolReg[chan + 11] : VolReg[chan]) - 3, (KSL_value & 0xC0) | volume); if (OP4[chan]) { chan += 3; volume = 63 - VolumeTable[((63 - ((KSL_value = Ksl[chan]) & 0x3F)) << 7) + vol]; if (chan >= 11) - SndOutput3(VolReg[chan - 11], KSL_value & 0xC0 | volume); + SndOutput3(VolReg[chan - 11], (KSL_value & 0xC0) | volume); else - SndOutput1(VolReg[chan], KSL_value & 0xC0 | volume); + SndOutput1(VolReg[chan], (KSL_value & 0xC0) | volume); if (Ksl2V[chan]) { volume = 63 - VolumeTable[((63 - ((KSL_value = Ksl2[chan]) & 0x3F)) << 7) + vol]; if (chan >= 11) - SndOutput3(VolReg[chan - 11] - 3, KSL_value & 0xC0 | volume); + SndOutput3(VolReg[chan - 11] - 3, (KSL_value & 0xC0) | volume); else - SndOutput1(VolReg[chan] - 3, KSL_value & 0xC0 | volume); + SndOutput1(VolReg[chan] - 3, (KSL_value & 0xC0) | volume); } } else { volume = 63 - VolumeTable[((63 - ((KSL_value = Ksl[chan]) & 0x3F)) << 7) + vol]; if (chan >= 11) - SndOutput3(VolReg[chan - 11], KSL_value & 0xC0 | volume); + SndOutput3(VolReg[chan - 11], (KSL_value & 0xC0) | volume); else - SndOutput1((percussion ? VolReg[chan + 11] : VolReg[chan]), KSL_value & 0xC0 | volume); + SndOutput1((percussion ? VolReg[chan + 11] : VolReg[chan]), (KSL_value & 0xC0) | volume); } } else { @@ -633,26 +644,26 @@ void Cad262Driver::SetVoiceVolume_SOP(unsigned chan, unsigned vol) volume = 63 - VolumeTable[((63 - ((KSL_value = Ksl[chan + 3]) & 0x3F)) << 7) + vol]; if (chan >= 11) - SndOutput3(VolReg[chan + 3 - 11], KSL_value & 0xC0 | volume); + SndOutput3(VolReg[chan + 3 - 11], (KSL_value & 0xC0) | volume); else - SndOutput1(VolReg[chan + 3], KSL_value & 0xC0 | volume); + SndOutput1(VolReg[chan + 3], (KSL_value & 0xC0) | volume); if (Ksl2V[chan + 3]) { volume = 63 - VolumeTable[((63 - ((KSL_value = Ksl[chan]) & 0x3F)) << 7) + vol]; if (chan >= 11) - SndOutput3(VolReg[chan - 11], KSL_value & 0xC0 | volume); + SndOutput3(VolReg[chan - 11], (KSL_value & 0xC0) | volume); else - SndOutput1(VolReg[chan], KSL_value & 0xC0 | volume); + SndOutput1(VolReg[chan], (KSL_value & 0xC0) | volume); } } else { volume = 63 - VolumeTable[((63 - ((KSL_value = Ksl[chan]) & 0x3F)) << 7) + vol]; if (chan >= 11) - SndOutput3(VolReg[chan - 11], KSL_value & 0xC0 | volume); + SndOutput3(VolReg[chan - 11], (KSL_value & 0xC0) | volume); else - SndOutput1((percussion ? VolReg[chan + 11] : VolReg[chan]), KSL_value & 0xC0 | volume); + SndOutput1((percussion ? VolReg[chan + 11] : VolReg[chan]), (KSL_value & 0xC0) | volume); } } } @@ -732,7 +743,7 @@ void Cad262Driver::SetVoiceTimbre_SOP(unsigned chan, unsigned char* array) Ksl2[chan + 3] = *(array + 12); Ksl2V[chan + 3] = *(array + 16) & 1; - SndOutput1(i + 3, *(array + 16) & 0x0F | Stereo[chan]); + SndOutput1(i + 3, (*(array + 16) & 0x0F) | Stereo[chan]); } SetVoiceVolume_SOP(chan, VoiceVolume[chan]); diff --git a/plugins/adplug/adplug/sop.h b/plugins/adplug/adplug/sop.h index 4800e3144d..725fca1f14 100644 --- a/plugins/adplug/adplug/sop.h +++ b/plugins/adplug/adplug/sop.h @@ -168,7 +168,7 @@ class CsopPlayer: public CPlayer } delete[] track; } - if (drv) drv->~Cad262Driver(); + if (drv) delete drv; }; bool load(const std::string &filename, const CFileProvider &fp); @@ -193,7 +193,7 @@ class CsopPlayer: public CPlayer std::string gettype() { char type[36]; - sprintf(type, "Note Sequencer v%u.%u by sopepos", (version >> 8) & 0xFF, version & 0xFF); + snprintf(type, sizeof(type), "Note Sequencer v%u.%u by sopepos", (version >> 8) & 0xFF, version & 0xFF); return std::string(type); } @@ -269,4 +269,4 @@ class CsopPlayer: public CPlayer sop_trk * track; /* event tracks [nTracks + control track] */ }; -#endif \ No newline at end of file +#endif diff --git a/plugins/adplug/adplug/strnlen.h b/plugins/adplug/adplug/strnlen.h new file mode 100644 index 0000000000..0507059c1e --- /dev/null +++ b/plugins/adplug/adplug/strnlen.h @@ -0,0 +1,60 @@ +/* + * AdPlug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2020 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * strnlen.h - Provide strnlen() if not available in the system library, + * by Alexander Miller . + */ +#ifndef ADPLUG_STRNLEN_H +#define ADPLUG_STRNLEN_H + +/* +strnlen() is a useful function, available in GNU libc and on newer +Windows versions, but it's not part of any standard and many systems +don't support it. The C11 standard defines strnlen_s() with the +same functionality, but as with all bounds-checked functions, support +is still optional. + +This header provides strnlen() for all systems such that the first +available implementation from the following list is used: +1. the system's strlen() (defined as a macro or function), +2. the system's strlen_s() (macro or function), +3. function template from this header. +*/ + +#define __STDC_WANT_LIB_EXT1__ 1 +#include + +#ifndef strnlen +#ifndef strnlen_s +template +size_t strnlen_s(const Char *s, size_t size) +{ + size_t i = 0; + while (i < size && s[i]) i++; + return i; +} +#endif // strnlen_s + +template +static inline size_t strnlen(const Char *s, size_t size) +{ + return strnlen_s(s, size); +} +#endif // strnlen + +#endif // ADPLUG_STRNLEN_H diff --git a/plugins/adplug/adplug/surroundopl.cpp b/plugins/adplug/adplug/surroundopl.cpp index a24fb1dfaf..d0babf9371 100644 --- a/plugins/adplug/adplug/surroundopl.cpp +++ b/plugins/adplug/adplug/surroundopl.cpp @@ -27,22 +27,34 @@ #include "surroundopl.h" #include "debug.h" -CSurroundopl::CSurroundopl(Copl *a, Copl *b, bool use16bit) - : use16bit(use16bit), - bufsize(4096), - a(a), b(b) +// Convert 8-bit to 16-bit +#define CV_8_16(a) ((((unsigned short)(a) << 8) | (a)) - 32768) + +// Convert 16-bit to 8-bit +#define CV_16_8(a) (((a) >> 8) + 128) + +CSurroundopl::CSurroundopl(COPLprops *a, COPLprops *b, bool output16bit) + : oplA(*a), + oplB(*b), + bufsize(4096), + output16bit(output16bit) { - currType = TYPE_OPL2; + // Report our type as the same as the first child OPL + currType = a->opl->gettype(); + this->lbuf = new short[this->bufsize]; this->rbuf = new short[this->bufsize]; + + // Default frequency offset for surroundopl is defined by FREQ_OFFSET. + this->offset = FREQ_OFFSET; }; CSurroundopl::~CSurroundopl() { delete[] this->rbuf; delete[] this->lbuf; - delete a; - delete b; + delete this->oplA.opl; + delete this->oplB.opl; } void CSurroundopl::update(short *buf, int samples) @@ -56,24 +68,45 @@ void CSurroundopl::update(short *buf, int samples) this->rbuf = new short[this->bufsize]; } - a->update(this->lbuf, samples); - b->update(this->rbuf, samples); + this->oplA.opl->update(this->lbuf, samples); + this->oplB.opl->update(this->rbuf, samples); // Copy the two mono OPL buffers into the stereo buffer for (int i = 0; i < samples; i++) { - if (this->use16bit) { - buf[i * 2] = this->lbuf[i]; - buf[i * 2 + 1] = this->rbuf[i]; + int offsetL = i, offsetR = i; + if (this->oplA.stereo) offsetL *= 2; + if (this->oplB.stereo) { offsetR *= 2; ++offsetR; } + + short l, r; + if (this->oplA.use16bit) { + l = this->lbuf[offsetL]; } else { - ((char *)buf)[i * 2] = ((char *)this->lbuf)[i]; - ((char *)buf)[i * 2 + 1] = ((char *)this->rbuf)[i]; + l = ((unsigned char *)this->lbuf)[offsetL]; + // If the synths are 8-bit, make the values 16-bit + l = CV_8_16(l); + } + if (this->oplB.use16bit) { + r = this->rbuf[offsetR]; + } else { + r = ((unsigned char *)this->rbuf)[offsetR]; + // If the synths are 8-bit, make the values 16-bit + r = CV_8_16(r); + } + + if (this->output16bit) { + buf[i * 2] = l; + buf[i * 2 + 1] = r; + } else { + // Convert back to 8-bit + ((unsigned char *)buf)[i * 2] = CV_16_8(l); + ((unsigned char *)buf)[i * 2 + 1] = CV_16_8(r); } } } void CSurroundopl::write(int reg, int val) { - a->write(reg, val); + this->oplA.opl->write(reg, val); // Transpose the other channel to produce the harmonic effect int iChannel = -1; @@ -97,7 +130,7 @@ void CSurroundopl::write(int reg, int val) // Adjust the frequency and calculate the new FNum //double dbNewFNum = (dbOriginalFreq+(dbOriginalFreq/FREQ_OFFSET)) / (50000.0 * pow(2, iNewBlock - 20)); //#define calcFNum() ((dbOriginalFreq+(dbOriginalFreq/FREQ_OFFSET)) / (50000.0 * pow(2, iNewBlock - 20))) - #define calcFNum() ((dbOriginalFreq+(dbOriginalFreq/FREQ_OFFSET)) / (49716.0 * pow(2.0, iNewBlock - 20))) + #define calcFNum() ((dbOriginalFreq+(dbOriginalFreq/this->offset)) / (49716.0 * pow(2.0, iNewBlock - 20))) double dbNewFNum = calcFNum(); // Make sure it's in range for the OPL chip @@ -159,7 +192,7 @@ void CSurroundopl::write(int reg, int val) // Need to write out low bits uint8_t iAdditionalReg = 0xA0 + iChannel; uint8_t iAdditionalValue = iNewFNum & 0xFF; - b->write(iAdditionalReg, iAdditionalValue); + this->oplB.opl->write(iAdditionalReg, iAdditionalValue); this->iTweakedFMReg[this->currChip][iAdditionalReg] = iAdditionalValue; } } else if ((iRegister >= 0xA0) && (iRegister <= 0xA8)) { @@ -177,7 +210,7 @@ void CSurroundopl::write(int reg, int val) iChannel, iFNum, iBlock, iNewFNum, iNewBlock); // The note is already playing, so we need to adjust the upper bits too uint8_t iAdditionalReg = 0xB0 + iChannel; - b->write(iAdditionalReg, iNewB0Value); + this->oplB.opl->write(iAdditionalReg, iNewB0Value); this->iTweakedFMReg[this->currChip][iAdditionalReg] = iNewB0Value; } // else the note is not playing, the upper bits will be set when the note is next played @@ -186,14 +219,16 @@ void CSurroundopl::write(int reg, int val) } // if (a register we're interested in) // Now write to the original register with a possibly modified value - b->write(iRegister, iValue); + this->oplB.opl->write(iRegister, iValue); this->iTweakedFMReg[this->currChip][iRegister] = iValue; } void CSurroundopl::init() { - a->init(); - b->init(); + this->oplA.opl->init(); + this->oplB.opl->init(); + this->oplA.opl->setchip(0); + this->oplB.opl->setchip(0); for (int c = 0; c < 2; c++) { for (int i = 0; i < 256; i++) { this->iFMReg[c][i] = 0; @@ -208,6 +243,15 @@ void CSurroundopl::init() void CSurroundopl::setchip(int n) { - a->setchip(n); - b->setchip(n); + this->oplA.opl->setchip(n); + this->oplB.opl->setchip(n); + this->Copl::setchip(n); +} + +void CSurroundopl::set_offset(double offset) +{ + if (offset != 0) + { + this->offset = offset; + } } diff --git a/plugins/adplug/adplug/surroundopl.h b/plugins/adplug/adplug/surroundopl.h index b7d05add03..14c1bc7d5e 100644 --- a/plugins/adplug/adplug/surroundopl.h +++ b/plugins/adplug/adplug/surroundopl.h @@ -42,21 +42,29 @@ // one block is too high and the adjacent block is too low ;-) #define NEWBLOCK_LIMIT 32 +struct COPLprops { + Copl *opl; + bool use16bit; // false == 8bit + bool stereo; // false == mono +}; + class CSurroundopl: public Copl { private: - bool use16bit; + COPLprops oplA, oplB; short bufsize; short *lbuf, *rbuf; - Copl *a, *b; + bool output16bit; uint8_t iFMReg[2][256]; uint8_t iTweakedFMReg[2][256]; uint8_t iCurrentTweakedBlock[2][9]; // Current value of the Block in the tweaked OPL chip uint8_t iCurrentFNum[2][9]; // Current value of the FNum in the tweaked OPL chip + double offset; // User configurable frequency offset for surroundopl public: - CSurroundopl(Copl *a, Copl *b, bool use16bit); + // Takes ownership of a->opl and b->opl + CSurroundopl(COPLprops *a, COPLprops *b, bool output16bit); ~CSurroundopl(); void update(short *buf, int samples); @@ -64,6 +72,7 @@ class CSurroundopl: public Copl void init(); void setchip(int n); + void set_offset(double offset); }; #endif diff --git a/plugins/adplug/adplug/u6m.cpp b/plugins/adplug/adplug/u6m.cpp index e6df568e41..037952e386 100644 --- a/plugins/adplug/adplug/u6m.cpp +++ b/plugins/adplug/adplug/u6m.cpp @@ -23,11 +23,7 @@ #include "u6m.h" // Makes security checks on output buffer before writing -#define SAVE_OUTPUT_ROOT(c, d, p) \ -if(p < d.size) \ - output_root(c, d.data, p); \ -else \ - return false; +#define SAVE_OUTPUT_ROOT(c, d, p) if(!output_root(c, d, p)) return false; CPlayer *Cu6mPlayer::factory(Copl *newopl) { @@ -38,7 +34,7 @@ bool Cu6mPlayer::load(const std::string &filename, const CFileProvider &fp) { // file validation section // this section only checks a few *necessary* conditions - unsigned long filesize, decompressed_filesize; + size_t filesize, decompressed_filesize; binistream *f; f = fp.open(filename); if(!f) return false; @@ -66,6 +62,7 @@ bool Cu6mPlayer::load(const std::string &filename, const CFileProvider &fp) } // load section + delete[] song_data; song_size = 0; song_data = new unsigned char[decompressed_filesize]; unsigned char* compressed_song_data = new unsigned char[filesize-3]; @@ -74,7 +71,6 @@ bool Cu6mPlayer::load(const std::string &filename, const CFileProvider &fp) fp.close(f); // attempt to decompress the song data - // if unsuccessful, deallocate song_data[] on the spot, and return(false) data_block source, destination; source.size = filesize-4; source.data = compressed_song_data; @@ -84,13 +80,13 @@ bool Cu6mPlayer::load(const std::string &filename, const CFileProvider &fp) if (!lzw_decompress(source,destination)) { delete[] compressed_song_data; - delete[] song_data; return(false); } // deallocation section delete[] compressed_song_data; + song_size = decompressed_filesize; rewind(0); return (true); } @@ -148,7 +144,6 @@ bool Cu6mPlayer::update() void Cu6mPlayer::rewind(int subsong) { - played_ticks = 0; songend = false; // set the driver's internal variables @@ -206,21 +201,21 @@ bool Cu6mPlayer::lzw_decompress(Cu6mPlayer::data_block source, Cu6mPlayer::data_ { bool end_marker_reached = false; int codeword_size = 9; - long bits_read = 0; + unsigned long bits_read = 0; int next_free_codeword = 0x102; int dictionary_size = 0x200; MyDict dictionary = MyDict(); std::stack root_stack; - long bytes_written = 0; + size_t bytes_written = 0; int cW; - int pW = 0; + int pW=0; unsigned char C; while (!end_marker_reached) { - cW = get_next_codeword(bits_read, source.data, codeword_size); + cW = get_next_codeword(bits_read, source, codeword_size); switch (cW) { // re-init the dictionary @@ -229,13 +224,16 @@ bool Cu6mPlayer::lzw_decompress(Cu6mPlayer::data_block source, Cu6mPlayer::data_ next_free_codeword = 0x102; dictionary_size = 0x200; dictionary.reset(); - cW = get_next_codeword(bits_read, source.data, codeword_size); + cW = get_next_codeword(bits_read, source, codeword_size); SAVE_OUTPUT_ROOT((unsigned char)cW, dest, bytes_written); break; // end of compressed file has been reached case 0x101: end_marker_reached = true; break; + // no next code word available, i.e., truncated or invalid data + case -1: + return false; // (cW <> 0x100) && (cW <> 0x101) default: if (cW < next_free_codeword) // codeword is already in the dictionary @@ -313,14 +311,20 @@ bool Cu6mPlayer::lzw_decompress(Cu6mPlayer::data_block source, Cu6mPlayer::data_ // Read the next code word from the source buffer -int Cu6mPlayer::get_next_codeword (long& bits_read, unsigned char *source, int codeword_size) +int Cu6mPlayer::get_next_codeword (unsigned long& bits_read, data_block& source, + int codeword_size) { unsigned char b0,b1,b2; int codeword; - - b0 = source[bits_read/8]; - b1 = source[bits_read/8+1]; - b2 = source[bits_read/8+2]; + + if (source.size - bits_read / 8 < 2 + (bits_read % 8 + codeword_size > 16)) + { + return -1; // source exhausted + } + + b0 = source.data[bits_read/8]; + b1 = source.data[bits_read/8+1]; + b2 = bits_read % 8 + codeword_size > 16 ? source.data[bits_read/8+2] : 0; codeword = ((b2 << 16) + (b1 << 8) + b0); codeword = codeword >> (bits_read % 8); @@ -349,10 +353,17 @@ int Cu6mPlayer::get_next_codeword (long& bits_read, unsigned char *source, int c // output a root to memory -void Cu6mPlayer::output_root(unsigned char root, unsigned char *destination, long& position) +bool Cu6mPlayer::output_root(unsigned char root, data_block& destination, + size_t& position) { - destination[position] = root; + if (position >= destination.size) + { + return false; + } + + destination.data[position] = root; position++; + return true; } @@ -388,7 +399,7 @@ void Cu6mPlayer::get_string(int codeword, Cu6mPlayer::MyDict& dictionary, std::s // This function reads the song data and executes the embedded commands. void Cu6mPlayer::command_loop() { - unsigned char command_byte; // current command byte + int command_byte; // current command byte int command_nibble_hi; // command byte, bits 4-7 int command_nibble_lo; // command byte, bite 0-3 bool repeat_loop = true; // @@ -397,6 +408,11 @@ void Cu6mPlayer::command_loop() { // extract low and high command nibbles command_byte = read_song_byte(); // implicitly increments song_pos + if (command_byte < 0) // handle invalid position + { + songend = true; + break; + } command_nibble_hi = command_byte >> 4; command_nibble_lo = command_byte & 0xf; @@ -446,7 +462,10 @@ void Cu6mPlayer::command_0(int channel) freq_byte = read_song_byte(); freq_word = expand_freq_byte(freq_byte); - set_adlib_freq(channel,freq_word); + if (channel < 9) + { + set_adlib_freq(channel, freq_word); + } } @@ -457,18 +476,19 @@ void Cu6mPlayer::command_0(int channel) // --------------------------------------------------- void Cu6mPlayer::command_1(int channel) { - unsigned char freq_byte; - byte_pair freq_word; + unsigned char freq_byte = read_song_byte(); + byte_pair freq_word = expand_freq_byte(freq_byte); - vb_direction_flag[channel] = 0; - vb_current_value[channel] = 0; + if (channel < 9) + { + vb_direction_flag[channel] = 0; + vb_current_value[channel] = 0; - freq_byte = read_song_byte(); - freq_word = expand_freq_byte(freq_byte); - set_adlib_freq(channel,freq_word); + set_adlib_freq(channel, freq_word); - freq_word.hi = freq_word.hi | 0x20; // note on - set_adlib_freq(channel,freq_word); + freq_word.hi = freq_word.hi | 0x20; // note on + set_adlib_freq(channel, freq_word); + } } @@ -485,7 +505,10 @@ void Cu6mPlayer::command_2(int channel) freq_byte = read_song_byte(); freq_word = expand_freq_byte(freq_byte); freq_word.hi = freq_word.hi | 0x20; // note on - set_adlib_freq(channel,freq_word); + if (channel < 9) + { + set_adlib_freq(channel, freq_word); + } } @@ -496,11 +519,13 @@ void Cu6mPlayer::command_2(int channel) // -------------------------------------- void Cu6mPlayer::command_3(int channel) { - unsigned char mf_byte; + unsigned char mf_byte = read_song_byte(); - carrier_mf_signed_delta[channel] = 0; - mf_byte = read_song_byte(); - set_carrier_mf(channel,mf_byte); + if (channel < 9) + { + carrier_mf_signed_delta[channel] = 0; + set_carrier_mf(channel, mf_byte); // apply mask? + } } @@ -511,10 +536,12 @@ void Cu6mPlayer::command_3(int channel) // ---------------------------------------- void Cu6mPlayer::command_4(int channel) { - unsigned char mf_byte; + unsigned char mf_byte = read_song_byte(); - mf_byte = read_song_byte(); - set_modulator_mf(channel,mf_byte); + if (channel < 9) + { + set_modulator_mf(channel, mf_byte); // apply mask? + } } @@ -525,12 +552,17 @@ void Cu6mPlayer::command_4(int channel) // -------------------------------------------- void Cu6mPlayer::command_5(int channel) { - channel_freq_signed_delta[channel] = read_signed_song_byte(); + signed char delta = read_signed_song_byte(); + + if (channel < 9) + { + channel_freq_signed_delta[channel] = delta; + } } // -------------------------------------------- -// Set vibrato paramters +// Set vibrato parameters // Format: 6c mn // c = channel // m = vibrato double amplitude @@ -538,11 +570,13 @@ void Cu6mPlayer::command_5(int channel) // -------------------------------------------- void Cu6mPlayer::command_6(int channel) { - unsigned char vb_parameters; + unsigned char vb_parameters = read_song_byte(); - vb_parameters = read_song_byte(); - vb_double_amplitude[channel] = vb_parameters >> 4; // high nibble - vb_multiplier[channel] = vb_parameters & 0xF; // low nibble + if (channel < 9) + { + vb_double_amplitude[channel] = vb_parameters >> 4; // high nibble + vb_multiplier[channel] = vb_parameters & 0xF; // low nibble + } } @@ -553,18 +587,23 @@ void Cu6mPlayer::command_6(int channel) // ---------------------------------------- void Cu6mPlayer::command_7(int channel) { - int instrument_offset = instrument_offsets[read_song_byte()]; - out_adlib_opcell(channel, false, 0x20, *(song_data + instrument_offset+0)); - out_adlib_opcell(channel, false, 0x40, *(song_data + instrument_offset+1)); - out_adlib_opcell(channel, false, 0x60, *(song_data + instrument_offset+2)); - out_adlib_opcell(channel, false, 0x80, *(song_data + instrument_offset+3)); - out_adlib_opcell(channel, false, 0xE0, *(song_data + instrument_offset+4)); - out_adlib_opcell(channel, true, 0x20, *(song_data + instrument_offset+5)); - out_adlib_opcell(channel, true, 0x40, *(song_data + instrument_offset+6)); - out_adlib_opcell(channel, true, 0x60, *(song_data + instrument_offset+7)); - out_adlib_opcell(channel, true, 0x80, *(song_data + instrument_offset+8)); - out_adlib_opcell(channel, true, 0xE0, *(song_data + instrument_offset+9)); - out_adlib(0xC0+channel, *(song_data + instrument_offset+10)); + unsigned char instrument_number = read_song_byte(); + + if (channel < 9 && instrument_number < 9) + { + size_t instrument_offset = instrument_offsets[instrument_number]; + out_adlib_opcell(channel, false, 0x20, song_data[instrument_offset+0]); + out_adlib_opcell(channel, false, 0x40, song_data[instrument_offset+1]); + out_adlib_opcell(channel, false, 0x60, song_data[instrument_offset+2]); + out_adlib_opcell(channel, false, 0x80, song_data[instrument_offset+3]); + out_adlib_opcell(channel, false, 0xE0, song_data[instrument_offset+4]); + out_adlib_opcell(channel, true, 0x20, song_data[instrument_offset+5]); + out_adlib_opcell(channel, true, 0x40, song_data[instrument_offset+6]); + out_adlib_opcell(channel, true, 0x60, song_data[instrument_offset+7]); + out_adlib_opcell(channel, true, 0x80, song_data[instrument_offset+8]); + out_adlib_opcell(channel, true, 0xE0, song_data[instrument_offset+9]); + out_adlib(0xC0+channel, song_data[instrument_offset+10]); + } } @@ -580,9 +619,12 @@ void Cu6mPlayer::command_81() subsong_info new_ss_info; new_ss_info.subsong_repetitions = read_song_byte(); - new_ss_info.subsong_start = read_song_byte(); new_ss_info.subsong_start += read_song_byte() << 8; + new_ss_info.subsong_start = read_song_byte(); + new_ss_info.subsong_start += read_song_byte() << 8; new_ss_info.continue_pos = song_pos; + // Since the new position is not validated, this could jump after the + // end of song_data. Handled by the usual checks. subsong_stack.push(new_ss_info); song_pos = new_ss_info.subsong_start; } @@ -607,8 +649,12 @@ void Cu6mPlayer::command_82() void Cu6mPlayer::command_83() { unsigned char instrument_number = read_song_byte(); - instrument_offsets[instrument_number] = song_pos; - song_pos += 11; + + if (instrument_number < 9 && song_size > 11 && song_pos < song_size - 11) + { + instrument_offsets[instrument_number] = song_pos; + song_pos += 11; + } } @@ -623,9 +669,13 @@ void Cu6mPlayer::command_85() unsigned char data_byte = read_song_byte(); int channel = data_byte >> 4; // high nibble unsigned char slide_delay = data_byte & 0xF; // low nibble - carrier_mf_signed_delta[channel] = +1; - carrier_mf_mod_delay[channel] = slide_delay + 1; - carrier_mf_mod_delay_backup[channel] = slide_delay + 1; + + if (channel < 9) + { + carrier_mf_signed_delta[channel] = +1; + carrier_mf_mod_delay[channel] = slide_delay + 1; + carrier_mf_mod_delay_backup[channel] = slide_delay + 1; + } } @@ -640,9 +690,13 @@ void Cu6mPlayer::command_86() unsigned char data_byte = read_song_byte(); int channel = data_byte >> 4; // high nibble unsigned char slide_delay = data_byte & 0xF; // low nibble - carrier_mf_signed_delta[channel] = -1; - carrier_mf_mod_delay[channel] = slide_delay + 1; - carrier_mf_mod_delay_backup[channel] = slide_delay + 1; + + if (channel < 9) + { + carrier_mf_signed_delta[channel] = -1; + carrier_mf_mod_delay[channel] = slide_delay + 1; + carrier_mf_mod_delay_backup[channel] = slide_delay + 1; + } } @@ -699,31 +753,29 @@ void Cu6mPlayer::dec_clip(int& param) // Returns the byte at the current song position. // Side effect: increments song_pos. -unsigned char Cu6mPlayer::read_song_byte() +int Cu6mPlayer::read_song_byte() { - unsigned char song_byte; - song_byte = song_data[song_pos]; - song_pos++; - return(song_byte); + if (song_pos < song_size) + { + return song_data[song_pos++]; + } + else + { + return -1; + } } // Same as read_song_byte(), except that it returns a signed byte signed char Cu6mPlayer::read_signed_song_byte() { - unsigned char song_byte; - int signed_value; - song_byte = *(song_data + song_pos); - song_pos++; - if (song_byte <= 127) - { - signed_value = song_byte; - } - else + int song_byte = read_song_byte(); + + if (song_byte >= 0x80) { - signed_value = (int)song_byte - 0x100; + song_byte -= 0x100; } - return((signed char)signed_value); + return song_byte; } diff --git a/plugins/adplug/adplug/u6m.h b/plugins/adplug/adplug/u6m.h index 0f25673185..f2b199b4ad 100644 --- a/plugins/adplug/adplug/u6m.h +++ b/plugins/adplug/adplug/u6m.h @@ -32,7 +32,7 @@ class Cu6mPlayer: public CPlayer public: static CPlayer *factory(Copl *newopl); - Cu6mPlayer(Copl *newopl) : CPlayer(newopl), song_data(0) + Cu6mPlayer(Copl *newopl) : CPlayer(newopl), song_data(0), song_size(0) { }; @@ -62,20 +62,20 @@ class Cu6mPlayer: public CPlayer struct subsong_info // information about a subsong { - int continue_pos; + size_t continue_pos; + size_t subsong_start; int subsong_repetitions; - int subsong_start; }; struct dict_entry // dictionary entry { unsigned char root; - int codeword; + short int codeword; }; struct data_block // { - long size; + size_t size; unsigned char *data; }; @@ -101,17 +101,16 @@ class Cu6mPlayer: public CPlayer // class variables - long played_ticks; - unsigned char* song_data; // the uncompressed .m file (the "song") + size_t song_size; // allocated size of song_data bool driver_active; // flag to prevent reentrancy - bool songend; // indicates song end - int song_pos; // current offset within the song - int loop_position; // position of the loop point + bool songend; // indicates song end + size_t song_pos; // current offset within the song + size_t loop_position; // position of the loop point int read_delay; // delay (in timer ticks) before further song data is read std::stack subsong_stack; - int instrument_offsets[9]; // offsets of the adlib instrument data + size_t instrument_offsets[9]; // offsets of the adlib instrument data // vibrato ("vb") unsigned char vb_current_value[9]; unsigned char vb_double_amplitude[9]; @@ -128,7 +127,7 @@ class Cu6mPlayer: public CPlayer // protected functions used by update() void command_loop(); - unsigned char read_song_byte(); + int read_song_byte(); signed char read_signed_song_byte(); void dec_clip(int&); byte_pair expand_freq_byte(unsigned char); @@ -161,8 +160,8 @@ class Cu6mPlayer: public CPlayer // protected functions used by load() bool lzw_decompress(data_block source, data_block dest); - int get_next_codeword (long& bits_read, unsigned char *source, int codeword_size); - void output_root(unsigned char root, unsigned char *destination, long& position); + int get_next_codeword(unsigned long& bits_read, data_block& source, int codeword_size); + bool output_root(unsigned char root, data_block& destination, size_t& position); void get_string(int codeword, MyDict& dictionary, std::stack& root_stack); }; diff --git a/plugins/adplug/adplug/unlzh.c b/plugins/adplug/adplug/unlzh.c new file mode 100644 index 0000000000..060a05704a --- /dev/null +++ b/plugins/adplug/adplug/unlzh.c @@ -0,0 +1,454 @@ +/* unlzh.c -- decompress files in SCO compress -H (LZH) format. + * The code in this file is directly derived from the public domain 'ar002' + * written by Haruhiko Okumura. + */ + +// NOTE: for use with .a2m v12-13-14 some defines were tweaked: +// DICBIT, CBIT, PBIT, TBIT +// old values are kept in /* */ blocks + +#include +#include +#include +#include +#include + +#include "unlzh.h" + +typedef unsigned short ush; +typedef unsigned char uch; + +/* decode.c */ + +static unsigned decode (unsigned count, uch buffer[]); +static void decode_start (void); + +/* huf.c */ +static void huf_decode_start (void); +static unsigned decode_c (void); +static unsigned decode_p (void); +static void read_pt_len (int nn, int nbit, int i_special); +static void read_c_len (void); + +/* io.c */ +static void fillbuf (int n); +static unsigned getbits (int n); +static void init_getbits (void); + +/* maketbl.c */ + +static void make_table (int nchar, uch bitlen[], + int tablebits, ush table[]); + + +#define DICBIT 14 /* 13 */ +#define DIC_SIZE ((unsigned) 1 << DICBIT) + +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +#ifndef UCHAR_MAX +# define UCHAR_MAX 255 +#endif + +#define BITBUFSIZ (CHAR_BIT * 2 * sizeof(char)) +/* Do not use CHAR_BIT * sizeof(bitbuf), does not work on machines + * for which short is not on 16 bits (Cray). + */ + +/* encode.c and decode.c */ + +#define MAXMATCH 256 /* formerly F (not more than UCHAR_MAX + 1) */ +#define THRESHOLD 2 /* 3 */ /* choose optimal value */ + +/* huf.c */ + +#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD) +/* alphabet = {0, 1, 2, ..., NC - 1} */ +#define CBIT 16 /* 9 */ +#define CODE_BIT 16 /* codeword length */ + +#define NP (DICBIT + 1) +#define NT (CODE_BIT + 3) +#define PBIT 14 /* 4 */ /* smallest integer such that (1U << PBIT) > NP */ +#define TBIT 15 /* 5 */ /* smallest integer such that (1U << TBIT) > NT */ +#define NPT (1 << TBIT) + +static ush left[2 * NC - 1]; +static ush right[2 * NC - 1]; +static uch c_len[NC]; +static uch pt_len[NPT]; +static unsigned blocksize; +static ush pt_table[256]; +static ush c_table[4096]; + +/*********************************************************** + io.c -- input/output +***********************************************************/ + +static ush bitbuf; +static unsigned subbitbuf; +static int bitcount; + +uch *input_buffer, *output_buffer; +unsigned input_buffer_idx, output_buffer_idx, input_buffer_size; + +static uch try_byte(void) +{ + if (input_buffer_idx < input_buffer_size) + return input_buffer[input_buffer_idx++]; + else + return 0; +} + +void write_buf(uch *ptr, ush size) +{ + memcpy(output_buffer + output_buffer_idx, ptr, size); + output_buffer_idx += size; +} + +static void fillbuf(int n) /* Shift bitbuf n bits left, read n bits */ +{ + bitbuf <<= n; + while (n > bitcount) { + bitbuf |= subbitbuf << (n -= bitcount); + subbitbuf = (unsigned)try_byte(); + if ((int)subbitbuf == EOF) subbitbuf = 0; + bitcount = CHAR_BIT; + } + bitbuf |= subbitbuf >> (bitcount -= n); +} + +static unsigned getbits(int n) +{ + unsigned x; + + x = bitbuf >> (BITBUFSIZ - n); fillbuf(n); + return x; +} + +static void init_getbits(void) +{ + bitbuf = 0; subbitbuf = 0; bitcount = 0; + fillbuf(BITBUFSIZ); +} + +/*********************************************************** + maketbl.c -- make table for decoding +***********************************************************/ + +static void make_table(int nchar, uch bitlen[], int tablebits, ush table[]) +{ + ush count[17], weight[17], start[18], *p; + unsigned i, k, len, ch, jutbits, avail, nextcode, mask; + + for (i = 1; i <= 16; i++) count[i] = 0; + for (i = 0; i < (unsigned)nchar; i++) count[bitlen[i]]++; + + start[1] = 0; + for (i = 1; i <= 16; i++) + start[i + 1] = start[i] + (count[i] << (16 - i)); + if ((start[17] & 0xffff) != 0) + printf("Bad table\n"); + + jutbits = 16 - tablebits; + for (i = 1; i <= (unsigned)tablebits; i++) { + start[i] >>= jutbits; + weight[i] = (unsigned) 1 << (tablebits - i); + } + while (i <= 16) { + weight[i] = (unsigned) 1 << (16 - i); + i++; + } + + i = start[tablebits + 1] >> jutbits; + if (i != 0) { + k = 1 << tablebits; + while (i != k) table[i++] = 0; + } + + avail = nchar; + mask = (unsigned) 1 << (15 - tablebits); + for (ch = 0; ch < (unsigned)nchar; ch++) { + if ((len = bitlen[ch]) == 0) continue; + nextcode = start[len] + weight[len]; + if (len <= (unsigned)tablebits) { + if ((unsigned) 1 << tablebits < nextcode) + printf("Bad table\n"); + for (i = start[len]; i < nextcode; i++) table[i] = ch; + } else { + k = start[len]; + p = &table[k >> jutbits]; + i = len - tablebits; + while (i != 0) { + if (*p == 0) { + right[avail] = left[avail] = 0; + *p = avail++; + } + if (k & mask) p = &right[*p]; + else p = &left[*p]; + k <<= 1; i--; + } + *p = ch; + } + start[len] = nextcode; + } +} + +/*********************************************************** + huf.c -- static Huffman +***********************************************************/ + +static void read_pt_len(int nn, int nbit, int i_special) +{ + int i, c, n; + unsigned mask; + + n = getbits(nbit); + if (n == 0) { + c = getbits(nbit); + for (i = 0; i < nn; i++) pt_len[i] = 0; + for (i = 0; i < 256; i++) pt_table[i] = c; + } else { + i = 0; + while (i < n) { + c = bitbuf >> (BITBUFSIZ - 3); + if (c == 7) { + mask = (unsigned) 1 << (BITBUFSIZ - 1 - 3); + while (mask & bitbuf) { mask >>= 1; c++; } + if (16 < c) + printf("Bad table\n"); + } + fillbuf((c < 7) ? 3 : c - 3); + pt_len[i++] = c; + if (i == i_special) { + c = getbits(2); + while (--c >= 0) pt_len[i++] = 0; + } + } + while (i < nn) pt_len[i++] = 0; + make_table(nn, pt_len, 8, pt_table); + } +} + +static void read_c_len(void) +{ + int i, c, n; + unsigned mask; + + n = getbits(CBIT); + if (n == 0) { + c = getbits(CBIT); + for (i = 0; i < NC; i++) c_len[i] = 0; + for (i = 0; i < 4096; i++) c_table[i] = c; + } else { + i = 0; + while (i < n) { + c = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if (c >= NT) { + mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8); + do { + if (bitbuf & mask) c = right[c]; + else c = left [c]; + mask >>= 1; + } while (c >= NT); + } + fillbuf((int) pt_len[c]); + if (c <= 2) { + if (c == 0) c = 1; + else if (c == 1) c = getbits(4) + 3; + else c = getbits(CBIT) + 20; + while (--c >= 0) c_len[i++] = 0; + } else c_len[i++] = c - 2; + } + while (i < NC) c_len[i++] = 0; + make_table(NC, c_len, 12, c_table); + } +} + +static unsigned decode_c(void) +{ + unsigned j, mask; + + if (blocksize == 0) { + blocksize = getbits(16); + if (blocksize == 0) { + return NC; /* end of file */ + } + read_pt_len(NT, TBIT, 3); + read_c_len(); + read_pt_len(NP, PBIT, -1); + } + blocksize--; + j = c_table[bitbuf >> (BITBUFSIZ - 12)]; + if (j >= NC) { + mask = (unsigned) 1 << (BITBUFSIZ - 1 - 12); + do { + if (bitbuf & mask) j = right[j]; + else j = left [j]; + mask >>= 1; + } while (j >= NC); + } + fillbuf((int) c_len[j]); + return j; +} + +static unsigned decode_p(void) +{ + unsigned j, mask; + + j = pt_table[bitbuf >> (BITBUFSIZ - 8)]; + if (j >= NP) { + mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8); + do { + if (bitbuf & mask) j = right[j]; + else j = left [j]; + mask >>= 1; + } while (j >= NP); + } + fillbuf((int) pt_len[j]); + if (j != 0) j = ((unsigned) 1 << (j - 1)) + getbits((int) (j - 1)); + return j; +} + +static void huf_decode_start(void) +{ + init_getbits(); blocksize = 0; +} + +/*********************************************************** + decode.c +***********************************************************/ + +static int j; /* remaining bytes to copy */ +static int done; /* set at end of input */ + +static void decode_start() +{ + huf_decode_start(); + j = 0; + done = 0; +} + +/* Decode the input and return the number of decoded bytes put in buffer + */ +static unsigned decode(unsigned count, uch buffer[]) + /* The calling function must keep the number of + bytes to be processed. This function decodes + either 'count' bytes or 'DIC_SIZE' bytes, whichever + is smaller, into the array 'buffer[]' of size + 'DIC_SIZE' or more. + Call decode_start() once for each new file + before calling this function. + */ +{ + static unsigned i; + unsigned r, c; + + r = 0; + while (--j >= 0) { + buffer[r] = buffer[i]; + i = (i + 1) & (DIC_SIZE - 1); + if (++r == count) return r; + } + for ( ; ; ) { + c = decode_c(); + if (c == NC) { + done = 1; + return r; + } + if (c <= UCHAR_MAX) { + buffer[r] = c; + if (++r == count) return r; + } else { + j = c - (UCHAR_MAX + 1 - THRESHOLD); + i = (r - decode_p() - 1) & (DIC_SIZE - 1); + while (--j >= 0) { + buffer[r] = buffer[i]; + i = (i + 1) & (DIC_SIZE - 1); + if (++r == count) return r; + } + } + } +} + + +/* =========================================================================== + * Unlzh in to out. Return OK or ERROR. + */ +#if 0 +int unlzh(in, out) + int in; + int out; +{ + unsigned n; + ifd = in; + ofd = out; + + decode_start(); + while (!done) { + n = decode((unsigned) DIC_SIZE, window); + if (!test && n > 0) { + write_buf(out, (char*)window, n); + } + } + return OK; +} +#endif + +int LZH_decompress(char *source, char *dest, int source_size, int dest_size) +{ + unsigned char *ptr; + int size_temp; + char ultra; + uint32_t size_unpacked = 0; + int size; + + input_buffer = (unsigned char *)source; + input_buffer_idx = 0; + input_buffer_size = source_size; + + ultra = input_buffer[input_buffer_idx++] & 1; + + output_buffer = (unsigned char *)dest; + output_buffer_idx = 0; + + size_unpacked = *(uint32_t *)(input_buffer + input_buffer_idx); + input_buffer_idx += sizeof(uint32_t); + + if (ultra) { + //WIN_SIZE = WIN_SIZE_MAX; // (1U << 15) + //DIC_SIZE = DIC_SIZE_MAX; // (1U << 14) + } else { + //WIN_SIZE = WIN_SIZE_DEF; // (1U << 12) + //DIC_SIZE = DIC_SIZE_DEF; // (1U << 13) + } + + ptr = calloc(DIC_SIZE, 1); + + decode_start(); + size = size_unpacked; + while ((size > 0) && dest_size) { + if (size > (int)DIC_SIZE) { + size_temp = DIC_SIZE; + } else { + size_temp = size; + } + + decode(size_temp, ptr); + if (dest_size >= size_temp) + { + write_buf(ptr, size_temp); + dest_size -= size_temp; + } else { + write_buf(ptr, dest_size); + dest_size = 0; + } + size -= size_temp; + } + + free(ptr); + + return size_unpacked; +} diff --git a/plugins/adplug/adplug/unlzh.h b/plugins/adplug/adplug/unlzh.h new file mode 100644 index 0000000000..3b83b983c1 --- /dev/null +++ b/plugins/adplug/adplug/unlzh.h @@ -0,0 +1,19 @@ +/* unlzh.h -- decompress files in SCO compress -H (LZH) format. + * The code in this file is directly derived from the public domain 'ar002' + * written by Haruhiko Okumura. + */ + +#ifndef _UNLZH_H_ +#define _UNLZH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int LZH_decompress(char *source, char *dest, int source_size, int dest_size); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _UNLZH_H_ */ diff --git a/plugins/adplug/adplug/unlzss.c b/plugins/adplug/adplug/unlzss.c new file mode 100644 index 0000000000..ecc9b28ad6 --- /dev/null +++ b/plugins/adplug/adplug/unlzss.c @@ -0,0 +1,117 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2008 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * unlzss.c - Custom LZSS decompression routine from Adlib Tracker II + * Adapted by Dmitry Smagin + * Originally by Stanislav Baranec + * + * REFERENCES: + * https://github.com/ijsf/at2 + * http://www.adlibtracker.net/ + * + */ + +#include +#include +#include +#include +#include "unlzss.h" + +static int input_size, output_size; +static unsigned char *input_ptr, *output_ptr; +static int output_maxsize; + +#define N_BITS 12 +#define F_BITS 4 +#define THRESHOLD 2 +#define N (1 << N_BITS) +#define F ((1 << F_BITS) + THRESHOLD) + +static void LZSS_decode() { + int input_idx = 0; + uint8_t code, prevcode; + uint16_t dx; + uint32_t ebx, edi; + + unsigned char *work_ptr = calloc(1, 65536); + + output_size = 0; + ebx = 0; + dx = 0; + edi = N - F; + + for (;;) { + dx = dx >> 1; + + if (!(dx >> 8)) { + if (input_idx >= input_size) + break; + + code = input_ptr[input_idx++]; + dx = 0xff00 | code; + } + + if (dx & 1) { + if (input_idx >= input_size) + break; + + code = input_ptr[input_idx++]; + work_ptr[edi] = code; + edi = (edi + 1) & (N - 1); + if (output_size >= output_maxsize) goto error_out; + output_ptr[output_size++] = code; + continue; + } + + if (input_idx >= input_size) + break; + + prevcode = code = input_ptr[input_idx++]; + + if (input_idx >= input_size) + break; + + code = input_ptr[input_idx++]; + ebx = ((code << 4) & 0xff00) | prevcode; + + int length = (code & 0x0f) + THRESHOLD + 1; + + do { + if (output_size >= output_maxsize) goto error_out; + output_ptr[output_size++] = work_ptr[edi] = work_ptr[ebx]; + + ebx = (ebx + 1) & (N - 1); + edi = (edi + 1) & (N - 1); + } while (--length > 0); + } + +error_out: + free(work_ptr); +} + +int LZSS_decompress(char *source, char *dest, int source_size, int dest_size) +{ + input_ptr = (unsigned char *)source; + input_size = source_size; + output_ptr = (unsigned char *)dest; + output_maxsize = dest_size; + + LZSS_decode(); + + return output_size; +} diff --git a/plugins/adplug/adplug/unlzss.h b/plugins/adplug/adplug/unlzss.h new file mode 100644 index 0000000000..3bc4fb5431 --- /dev/null +++ b/plugins/adplug/adplug/unlzss.h @@ -0,0 +1,42 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2008 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * unlzss.h - Custom LZSS decompression routine from Adlib Tracker II + * Adapted by Dmitry Smagin + * Originally by Stanislav Baranec + * + * REFERENCES: + * https://github.com/ijsf/at2 + * http://www.adlibtracker.net/ + * + */ + +#ifndef _UNLZSS_H_ +#define _UNLZSS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int LZSS_decompress(char *source, char *dest, int source_size, int dest_size); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _UNLZSS_H_ */ diff --git a/plugins/adplug/adplug/unlzw.c b/plugins/adplug/adplug/unlzw.c new file mode 100644 index 0000000000..e7d62e36cf --- /dev/null +++ b/plugins/adplug/adplug/unlzw.c @@ -0,0 +1,173 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2008 Simon Peter, , et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * unlzw.c - Custom LZW decompression routine from Adlib Tracker II + * Adapted by Dmitry Smagin + * Originally by Stanislav Baranec + * + * REFERENCES: + * https://github.com/ijsf/at2 + * http://www.adlibtracker.net/ + * + */ + +#include +#include +#include +#include +#include "unlzw.h" + +static uint16_t bitshift; +static uint32_t prevbitstring; + +static uint16_t bitmask[5] = { 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff }; + +static unsigned char *input_ptr; +static int input_size; +static unsigned char *output_ptr; +static unsigned int output_maxsize; +static int output_size; + +static int nextcode() +{ + int input_idx; + uint32_t bitstring; + + input_idx = prevbitstring >> 3; + bitstring = input_ptr[input_idx + 0] + + (input_ptr[input_idx + 1] << 8) + + (input_ptr[input_idx + 2] << 16); + + bitstring >>= prevbitstring & 7; + prevbitstring += bitshift; + + return bitstring & bitmask[bitshift - 9]; +} + +static void LZW_decode() +{ + uint8_t *stack = calloc(1, 65636); + uint8_t *work_ptr = calloc(1, 65636); + + uint8_t le76, le77; + uint16_t le6a, le6c, le6e, le70, stringlength, le74; + + int code; + int output_idx; + + int sp = 65536 - 1; + + stringlength = 0; + bitshift = 9; + le70 = 0x102; + le74 = 0x200; + output_idx = 0; + code = 0; + le6a = 0; + le6c = 0; + le6e = 0; + le76 = 0; + le77 = 0; + prevbitstring = 0; + + for (;;) { + code = nextcode(); + if (code == 0x101) + break; + + if (code == 0x100) { + bitshift = 9; + le74 = 0x200; + le70 = 0x102; + code = nextcode(); + le6a = code; + le6c = code; + le77 = code; + le76 = code; + + if (output_idx >= output_maxsize) goto error_out; + output_ptr[output_idx++] = code; + continue; + } + + le6a = code; + le6e = code; + + if (code >= le70) { + code = le6c; + le6a = code; + code = (code & 0xff00) + le76; + sp--; + stack[sp] = code; + stringlength++; + } + + while (le6a > 0xff) { + code = (code & 0xff00) + work_ptr[(le6a * 3) + 2]; + sp--; + stack[sp] = code; + stringlength++; + code = work_ptr[le6a * 3] + (work_ptr[(le6a * 3) + 1] << 8); + le6a = code; + } + + code = le6a; + le76 = code; + le77 = code; + sp--; + stack[sp] = code; + stringlength++; + + while (stringlength--) { + if (output_idx >= output_maxsize) goto error_out; + output_ptr[output_idx++] = stack[sp++]; + } + + stringlength = 0; + + work_ptr[(le70 * 3) + 0] = le6c; + work_ptr[(le70 * 3) + 1] = le6c >> 8; + work_ptr[(le70 * 3) + 2] = le77; + le70++; + + code = le6e; + le6c = code; + + if (le70 >= le74 && bitshift < 14) { + bitshift++; + le74 <<= 1; + } + } + +error_out: + output_size = output_idx; + free(stack); + free(work_ptr); +} + +int LZW_decompress(char *source, char *dest, int source_size, int destination_size) +{ + input_ptr = (unsigned char *)source; + input_size = source_size; + output_ptr = (unsigned char *)dest; + output_maxsize = destination_size; + + LZW_decode(); + + return output_size; +} diff --git a/plugins/adplug/adplug/rad.h b/plugins/adplug/adplug/unlzw.h similarity index 57% rename from plugins/adplug/adplug/rad.h rename to plugins/adplug/adplug/unlzw.h index f41188fe64..bb63c14dc6 100644 --- a/plugins/adplug/adplug/rad.h +++ b/plugins/adplug/adplug/unlzw.h @@ -1,44 +1,42 @@ /* * Adplug - Replayer for many OPL2/OPL3 audio file formats. - * Copyright (C) 1999 - 2003 Simon Peter, , et al. - * + * Copyright (C) 1999 - 2008 Simon Peter, , et al. + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * rad.h - RAD Loader by Simon Peter + * unlzw.h - Custom LZW decompression routine from Adlib Tracker II + * Adapted by Dmitry Smagin + * Originally by Stanislav Baranec + * + * REFERENCES: + * https://github.com/ijsf/at2 + * http://www.adlibtracker.net/ + * */ -#include "protrack.h" - -class CradLoader: public CmodPlayer -{ -public: - static CPlayer *factory(Copl *newopl); +#ifndef _UNLZW_H_ +#define _UNLZW_H_ - CradLoader(Copl *newopl) - : CmodPlayer(newopl) - { *desc = '\0'; }; +#ifdef __cplusplus +extern "C" { +#endif - bool load(const std::string &filename, const CFileProvider &fp); - float getrefresh(); +int LZW_decompress(char *source, char *dest, int source_size, int destination_size); - std::string gettype() - { return std::string("Reality ADlib Tracker"); }; - std::string getdesc() - { return std::string(desc); }; +#ifdef __cplusplus +} /* extern "C" */ +#endif -private: - unsigned char version,radflags; - char desc[80*22]; -}; +#endif /* _UNLZW_H_ */ diff --git a/plugins/adplug/adplug/version.h b/plugins/adplug/adplug/version.h index bc9e455a2f..b66101eb4b 100644 --- a/plugins/adplug/adplug/version.h +++ b/plugins/adplug/adplug/version.h @@ -1 +1 @@ -#define ADPLUG_VERSION "2.3.1" +#define ADPLUG_VERSION "a6706ba256976eedf73e06d67ad74c5985271c1f" diff --git a/plugins/adplug/adplug/vgm.cpp b/plugins/adplug/adplug/vgm.cpp index 2fc8a971b6..fff95cb3ce 100644 --- a/plugins/adplug/adplug/vgm.cpp +++ b/plugins/adplug/adplug/vgm.cpp @@ -162,8 +162,8 @@ bool CvgmPlayer::load(const std::string &filename, const CFileProvider &fp) f->readString(id, 4); if (!strncmp(id, GD3_HEADER_ID, 4)) { - int gd3_ver = f->readInt(4); - int gd3_size = f->readInt(4); + /* int gd3_ver = */ f->readInt(4); + /* int gd3_size = */ f->readInt(4); fillGD3Tag(f, GD3.title_en); fillGD3Tag(f, GD3.title_jp); fillGD3Tag(f, GD3.game_en); @@ -285,7 +285,7 @@ std::string CvgmPlayer::gettype() char tmpstr[40]; uint8_t major = (version >> 8) & 0xFF; uint8_t minor = version & 0xFF; - sprintf(tmpstr, "Video Game Music %x.%x (%s)", major, minor, chip); + snprintf(tmpstr, sizeof(tmpstr), "Video Game Music %x.%x (%s)", major, minor, chip); return std::string(tmpstr); } @@ -347,9 +347,9 @@ std::string CvgmPlayer::getdesc() if (GD3.notes[0]) wcstombs(notes, GD3.notes, 256); char str_sys[256]; str_sys[0] = 0; - if (system[0] && date[0]) + if (system[0] && date[0] && strlen(system) <= 251) { - sprintf(str_sys, "%s / %s", system, date); + snprintf(str_sys, sizeof(str_sys), "%.251s / %.*s", system, 252 - (int)strlen(system), date); } else if (system[0]) { @@ -363,9 +363,9 @@ std::string CvgmPlayer::getdesc() char str_desc[256]; str_desc[0] = 0; if (game[0]) { - if (str_sys[0]) + if (str_sys[0] && strlen(game) <= 251) { - sprintf(str_game, "%s (%s)", game, str_sys); + snprintf(str_game, sizeof(str_game), "%.251s (%.*s)", game, 252 - (int)strlen(game), str_sys); } else { @@ -376,9 +376,9 @@ std::string CvgmPlayer::getdesc() { strcpy(str_game, str_sys); } - if (notes[0]) + if (notes[0] && strlen(str_game) <= 250) { - sprintf(str_desc, "%s\r\n\r\n%s", str_game, notes); + snprintf(str_desc, sizeof(str_desc), "%.250s\r\n\r\n%.*s", str_game, 251 - (int)strlen(str_game), notes); } else { diff --git a/plugins/adplug/adplug/vgm.h b/plugins/adplug/adplug/vgm.h index dbaa93d85c..7894e0f652 100644 --- a/plugins/adplug/adplug/vgm.h +++ b/plugins/adplug/adplug/vgm.h @@ -29,14 +29,7 @@ #include "player.h" -#if !defined(UINT8_MAX) -typedef signed char int8_t; -typedef short int16_t; -typedef int int32_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -#endif +#include // for uintxx_t #define VGM_GZIP_MIN 8 // minimum size for GZip header #define VGM_HEADER_MIN 84 // minimum size for header (till YM3812 clock field) diff --git a/plugins/adplug/adplug/wemuopl.h b/plugins/adplug/adplug/wemuopl.h index d796dcd2db..315f621561 100644 --- a/plugins/adplug/adplug/wemuopl.h +++ b/plugins/adplug/adplug/wemuopl.h @@ -31,9 +31,10 @@ class CWemuopl: public Copl { public: CWemuopl(int rate, bool bit16, bool usestereo) + : use16bit(bit16), stereo(usestereo), sampleerate(rate) { opl.adlib_init(rate, usestereo ? 2 : 1, bit16 ? 2 : 1); - currType = TYPE_OPL2; + currType = TYPE_OPL3; }; void update(short *buf, int samples) @@ -47,10 +48,15 @@ class CWemuopl: public Copl opl.adlib_write((currChip << 8) | reg, val); }; - void init() {}; + void init() { + opl.adlib_init(sampleerate, stereo ? 2 : 1, use16bit ? 2 : 1); + currChip = 0; + }; private: OPLChipClass opl; + bool use16bit,stereo; + int sampleerate; }; #endif diff --git a/plugins/adplug/adplug/woodyopl.cpp b/plugins/adplug/adplug/woodyopl.cpp index 04dcd0d543..96fce4149c 100644 --- a/plugins/adplug/adplug/woodyopl.cpp +++ b/plugins/adplug/adplug/woodyopl.cpp @@ -945,7 +945,7 @@ static void OPL_INLINE clipit8(Bit32s ival, Bit8s* outval) { *outval = 0; } } else { - *outval = -1; + *outval = 255; } } diff --git a/plugins/adplug/adplug/xad.cpp b/plugins/adplug/adplug/xad.cpp index 3082a8028c..8d846137a8 100644 --- a/plugins/adplug/adplug/xad.cpp +++ b/plugins/adplug/adplug/xad.cpp @@ -82,7 +82,13 @@ bool CxadPlayer::load(const std::string &filename, const CFileProvider &fp) else { // get file size - tune_size = fp.filesize(f) - 80; + tune_size = fp.filesize(f); + if (tune_size <= 80) + { + fp.close(f); + return false; + } + tune_size -= 80; } // load() diff --git a/plugins/adplug/adplug/xsm.cpp b/plugins/adplug/adplug/xsm.cpp index f5536f0672..a5f3bbdd21 100644 --- a/plugins/adplug/adplug/xsm.cpp +++ b/plugins/adplug/adplug/xsm.cpp @@ -45,17 +45,9 @@ bool CxsmPlayer::load(const std::string &filename, const CFileProvider &fp) // read and set instruments for(i = 0; i < 9; i++) { - opl->write(0x20 + op_table[i], f->readInt(1)); - opl->write(0x23 + op_table[i], f->readInt(1)); - opl->write(0x40 + op_table[i], f->readInt(1)); - opl->write(0x43 + op_table[i], f->readInt(1)); - opl->write(0x60 + op_table[i], f->readInt(1)); - opl->write(0x63 + op_table[i], f->readInt(1)); - opl->write(0x80 + op_table[i], f->readInt(1)); - opl->write(0x83 + op_table[i], f->readInt(1)); - opl->write(0xe0 + op_table[i], f->readInt(1)); - opl->write(0xe3 + op_table[i], f->readInt(1)); - opl->write(0xc0 + op_table[i], f->readInt(1)); + for (j = 0; j < 11; j++) { + inst[i].value[j] = f->readInt(1); + } f->ignore(5); } @@ -98,8 +90,23 @@ bool CxsmPlayer::update() void CxsmPlayer::rewind(int subsong) { + int i; notenum = last = 0; songend = false; + opl->init(); opl->write(1, 32); + for (i = 0; i < 9; i++) { + opl->write(0x20 + op_table[i], inst[i].value[0]); + opl->write(0x23 + op_table[i], inst[i].value[1]); + opl->write(0x40 + op_table[i], inst[i].value[2]); + opl->write(0x43 + op_table[i], inst[i].value[3]); + opl->write(0x60 + op_table[i], inst[i].value[4]); + opl->write(0x63 + op_table[i], inst[i].value[5]); + opl->write(0x80 + op_table[i], inst[i].value[6]); + opl->write(0x83 + op_table[i], inst[i].value[7]); + opl->write(0xe0 + op_table[i], inst[i].value[8]); + opl->write(0xe3 + op_table[i], inst[i].value[9]); + opl->write(0xc0 + op_table[i], inst[i].value[10]); + } } float CxsmPlayer::getrefresh() diff --git a/plugins/adplug/adplug/xsm.h b/plugins/adplug/adplug/xsm.h index 5f8397eb1f..25df43492b 100644 --- a/plugins/adplug/adplug/xsm.h +++ b/plugins/adplug/adplug/xsm.h @@ -41,6 +41,9 @@ class CxsmPlayer: public CPlayer char *music; unsigned int last, notenum; bool songend; + struct { + unsigned char value[11]; + } inst[9]; void play_note(int c, int note, int octv); }; diff --git a/plugins/adplug/plugin.c b/plugins/adplug/plugin.c index 55cd6c845c..3aa44b3ab7 100644 --- a/plugins/adplug/plugin.c +++ b/plugins/adplug/plugin.c @@ -48,8 +48,8 @@ int adplug_stop (void); static const char settings_dlg[] = - "property \"Prefer Ken emu over Satoh (surround won't work)\" checkbox adplug.use_ken 0;\n" - "property \"Enable surround\" checkbox adplug.surround 1;\n" + "property \"Sample rate (real OPL is 49716Hz)\" entry adplug.samplerate 49716;\n" + "property \"Synth engine\" select[5] adplug.synth 0 \"Nuked OPL3\" \"DOSBox OPL3 emulator\" \"Tatsuyuki Satoh's OPL2 emulator\" \"Ken Silverman's OPL emulator\" \"Simon Peter's OPL emulator\";\n" ; // define plugin interface @@ -64,6 +64,7 @@ DB_decoder_t adplug_plugin = { .plugin.copyright = "ADPLUG DeaDBeeF Player Plugin\n" "Copyright (C) 2009-2014 Oleksiy Yakovenko \n" + "Copyright (C) 2024 Thomas Jepp \n" "\n" "This software is provided 'as-is', without any express or implied\n" "warranty. In no event will the authors be held liable for any damages\n" @@ -86,8 +87,8 @@ DB_decoder_t adplug_plugin = { "\n" "\n" "adplug (modified)\n" - "Copyright (C) 1999 - 2006 Simon Peter, , et al.\n" - "deadbeef-related modifications (c) 2009-2014 Oleksiy Yakovenko\n" + "Copyright (C) 1999 - 2017 Simon Peter, et al.\n" + "deadbeef-related modifications (c) 2009-2014 Oleksiy Yakovenko, 2024 Thomas Jepp\n" "\n" "This library is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU Lesser General Public\n"