From 0c2560bb36b0ae6b64d823b9af8f7cfe4e351d15 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 12 Mar 2008 15:21:17 +0000 Subject: [PATCH] - implemented Vavoom's vertex height things (1505, 1506). These are not tested yet! - added Updaterevision tool to print proper revision information in the console. - removed support for specialty file formats from ModPlug loader. Having the 4 basic module formats (MOD, XM, S3M and IT) plus UMX should be enough. - added new brightmaps by phi108 (Player and Baron attack) - fixed a few brightmap definition errors - fixed: DFraggleThinker::Destroy still treated the SpawnedThings array as DActorPointers although that helper class has been removed. - merged the GC branch into the trunk - updated to ZDoom r796 git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@54 b0f79afe-0144-0410-b225-9a4edf0717df --- docs/rh-log.txt | 241 +- gzdoom.sln | 6 +- gzdoom.vcproj | 12 +- snes_spc/Makefile | 52 + snes_spc/changes.txt | 107 + snes_spc/demo/benchmark.c | 58 + snes_spc/demo/comm.c | 70 + snes_spc/demo/demo_util.c | 57 + snes_spc/demo/demo_util.h | 31 + snes_spc/demo/play_spc.c | 57 + snes_spc/demo/save_state.c | 107 + snes_spc/demo/trim_spc.c | 83 + snes_spc/demo/wave_writer.c | 153 + snes_spc/demo/wave_writer.h | 20 + snes_spc/fast_dsp/SPC_DSP.cpp | 703 +++ snes_spc/fast_dsp/SPC_DSP.h | 212 + snes_spc/license.txt | 504 ++ snes_spc/readme.txt | 86 + snes_spc/slow_dsp/SPC_DSP.cpp | 1018 ++++ snes_spc/slow_dsp/SPC_DSP.h | 304 ++ snes_spc/snes_spc.txt | 318 ++ snes_spc/snes_spc.vcproj | 397 ++ snes_spc/snes_spc/SNES_SPC.cpp | 564 +++ snes_spc/snes_spc/SNES_SPC.h | 279 ++ snes_spc/snes_spc/SNES_SPC_misc.cpp | 380 ++ snes_spc/snes_spc/SNES_SPC_state.cpp | 129 + snes_spc/snes_spc/SPC_CPU.h | 1220 +++++ snes_spc/snes_spc/SPC_DSP.cpp | 703 +++ snes_spc/snes_spc/SPC_DSP.h | 212 + snes_spc/snes_spc/SPC_Filter.cpp | 68 + snes_spc/snes_spc/SPC_Filter.h | 47 + snes_spc/snes_spc/blargg_common.h | 186 + snes_spc/snes_spc/blargg_config.h | 24 + snes_spc/snes_spc/blargg_endian.h | 185 + snes_spc/snes_spc/blargg_source.h | 100 + snes_spc/snes_spc/dsp.cpp | 48 + snes_spc/snes_spc/dsp.h | 83 + snes_spc/snes_spc/spc.cpp | 73 + snes_spc/snes_spc/spc.h | 147 + src/actor.h | 42 +- src/b_bot.h | 6 +- src/b_func.cpp | 10 + src/b_game.cpp | 4 +- src/c_console.cpp | 2 +- src/c_dispatch.cpp | 10 +- src/cmdlib.h | 2 +- src/d_dehacked.cpp | 2 +- src/d_main.cpp | 95 +- src/d_net.cpp | 9 +- src/d_player.h | 5 +- src/decallib.cpp | 3 +- src/dobject.cpp | 282 +- src/dobject.h | 302 +- src/dobjgc.cpp | 623 +++ src/dsectoreffect.cpp | 3 +- src/dsectoreffect.h | 2 +- src/dthinker.cpp | 72 +- src/dthinker.h | 5 +- src/farchive.cpp | 8 + src/fragglescript/t_func.cpp | 134 +- src/fragglescript/t_load.cpp | 9 +- src/fragglescript/t_oper.cpp | 92 +- src/fragglescript/t_prepro.cpp | 1 + src/fragglescript/t_script.cpp | 41 +- src/fragglescript/t_script.h | 60 +- src/fragglescript/t_spec.cpp | 1 + src/fragglescript/t_variable.cpp | 13 +- src/g_doom/a_bossbrain.cpp | 2 +- src/g_doom/doom_sbar.cpp | 49 +- src/g_game.cpp | 10 +- src/g_heretic/a_hereticmisc.cpp | 2 +- src/g_heretic/a_hereticweaps.cpp | 2 +- src/g_heretic/heretic_sbar.cpp | 35 +- src/g_hexen/a_clericholy.cpp | 1 + src/g_hexen/a_heresiarch.cpp | 2 +- src/g_hexen/a_hexenglobal.h | 2 +- src/g_hexen/a_spike.cpp | 2 +- src/g_hexen/a_teleportother.cpp | 2 +- src/g_hexen/hexen_sbar.cpp | 45 +- src/g_level.cpp | 7 +- src/g_shared/a_action.cpp | 2 +- src/g_shared/a_decals.cpp | 6 +- src/g_shared/a_morph.cpp | 2 +- src/g_shared/a_movingcamera.cpp | 10 +- src/g_shared/a_pickups.cpp | 8 +- src/g_shared/a_pickups.h | 12 +- src/g_shared/a_sectoraction.cpp | 2 +- src/g_shared/a_sharedglobal.h | 7 +- src/g_shared/a_weaponpiece.cpp | 6 +- src/g_shared/a_weaponpiece.h | 3 +- src/g_shared/a_weapons.cpp | 4 +- src/g_shared/hudmessages.cpp | 5 +- src/g_shared/sbar.h | 30 +- src/g_shared/sbarinfo.cpp | 21 +- src/g_shared/shared_sbar.cpp | 116 +- src/g_strife/strife_sbar.cpp | 21 +- src/gameconfigfile.cpp | 16 +- src/gl/gl_data.cpp | 2 +- src/gl/gl_dynlight.cpp | 2 +- src/gl/gl_lights.h | 5 +- src/i_net.cpp | 20 +- src/m_alloc.cpp | 24 +- src/m_alloc.h | 3 - src/m_argv.h | 2 +- src/m_bbox.cpp | 8 +- src/m_bbox.h | 6 +- src/m_misc.cpp | 30 +- src/modplug/load_669.cpp | 186 - src/modplug/load_abc.cpp | 5114 -------------------- src/modplug/load_amf.cpp | 417 -- src/modplug/load_ams.cpp | 628 --- src/modplug/load_dbm.cpp | 368 -- src/modplug/load_dmf.cpp | 606 --- src/modplug/load_dsm.cpp | 236 - src/modplug/load_far.cpp | 270 -- src/modplug/load_j2b.cpp | 15 - src/modplug/load_mdl.cpp | 503 -- src/modplug/load_med.cpp | 916 ---- src/modplug/load_mid.cpp | 2019 -------- src/modplug/load_mod.cpp | 10 +- src/modplug/load_mt2.cpp | 635 --- src/modplug/load_mtm.cpp | 168 - src/modplug/load_okt.cpp | 197 - src/modplug/load_pat.cpp | 1576 ------ src/modplug/load_pat.h | 27 - src/modplug/load_psm.cpp | 839 ---- src/modplug/load_ptm.cpp | 207 - src/modplug/load_ult.cpp | 222 - src/modplug/load_wav.cpp | 220 - src/modplug/sndfile.cpp | 256 +- src/p_acs.cpp | 81 +- src/p_acs.h | 5 +- src/p_doors.cpp | 8 +- src/p_enemy.cpp | 20 +- src/p_enemy_a_lookex.cpp | 20 +- src/p_floor.cpp | 2 +- src/p_lnspec.cpp | 6 +- src/p_mobj.cpp | 49 +- src/p_setup.cpp | 122 +- src/p_spec.h | 2 +- src/p_user.cpp | 20 + src/r_defs.h | 12 +- src/r_things.cpp | 2 +- src/s_sndseq.cpp | 49 +- src/s_sndseq.h | 6 +- src/s_sound.cpp | 50 +- src/sdl/hardware.cpp | 6 +- src/sdl/i_main.cpp | 8 +- src/sound/fmodsound.cpp | 23 +- src/sound/i_music.cpp | 8 +- src/sound/i_musicinterns.h | 36 +- src/sound/i_sound.cpp | 2 +- src/sound/music_spc.cpp | 190 +- src/stats.cpp | 3 +- src/svnrevision.h | 6 +- src/v_video.cpp | 11 +- src/version.h | 15 +- src/w_wad.cpp | 4 +- src/win32/hardware.cpp | 12 +- src/win32/i_cd.cpp | 2 +- src/win32/i_input.cpp | 4 +- src/win32/i_main.cpp | 6 +- src/win32/i_system.cpp | 2 +- src/win32/win32video.cpp | 1 + tools/updaterevision/Makefile | 42 + tools/updaterevision/trustinfo.rc | 6 + tools/updaterevision/trustinfo.txt | 16 + tools/updaterevision/updaterevision.c | 118 + tools/updaterevision/updaterevision.vcproj | 346 ++ wadsrc_bm/brightmaps/doom/BON2B0.png | Bin 147 -> 99 bytes wadsrc_bm/brightmaps/doom/BON2C0.png | Bin 141 -> 97 bytes wadsrc_bm/brightmaps/doom/BON2D0.png | Bin 149 -> 98 bytes wadsrc_bm/brightmaps/doom/BOSSE1.png | Bin 190 -> 357 bytes wadsrc_bm/brightmaps/doom/BOSSE2.png | Bin 138 -> 196 bytes wadsrc_bm/brightmaps/doom/BOSSE3.png | Bin 152 -> 233 bytes wadsrc_bm/brightmaps/doom/BOSSE4.png | Bin 187 -> 294 bytes wadsrc_bm/brightmaps/doom/BOSSE5.png | Bin 161 -> 291 bytes wadsrc_bm/brightmaps/doom/BOSSE6.png | Bin 156 -> 267 bytes wadsrc_bm/brightmaps/doom/BOSSE7.png | Bin 181 -> 305 bytes wadsrc_bm/brightmaps/doom/BOSSE8.png | Bin 173 -> 299 bytes wadsrc_bm/brightmaps/doom/BOSSF1.png | Bin 219 -> 375 bytes wadsrc_bm/brightmaps/doom/BOSSF2.png | Bin 219 -> 398 bytes wadsrc_bm/brightmaps/doom/BOSSF3.png | Bin 132 -> 217 bytes wadsrc_bm/brightmaps/doom/BOSSF4.png | Bin 136 -> 216 bytes wadsrc_bm/brightmaps/doom/BOSSF5.png | Bin 170 -> 270 bytes wadsrc_bm/brightmaps/doom/BOSSF6.png | Bin 176 -> 268 bytes wadsrc_bm/brightmaps/doom/BOSSF7.png | Bin 188 -> 337 bytes wadsrc_bm/brightmaps/doom/BOSSF8.png | Bin 213 -> 366 bytes wadsrc_bm/brightmaps/doom/BOSSG1.png | Bin 185 -> 330 bytes wadsrc_bm/brightmaps/doom/BOSSG2.png | Bin 194 -> 312 bytes wadsrc_bm/brightmaps/doom/BOSSG3.png | Bin 180 -> 313 bytes wadsrc_bm/brightmaps/doom/BOSSG4.png | Bin 165 -> 261 bytes wadsrc_bm/brightmaps/doom/BOSSG5.png | Bin 137 -> 198 bytes wadsrc_bm/brightmaps/doom/BOSSG6.png | Bin 150 -> 203 bytes wadsrc_bm/brightmaps/doom/BOSSG7.png | Bin 153 -> 228 bytes wadsrc_bm/brightmaps/doom/BOSSG8.png | Bin 141 -> 243 bytes wadsrc_bm/brightmaps/doom/CPOSE1.png | Bin 290 -> 121 bytes wadsrc_bm/brightmaps/doom/CPOSE2.png | Bin 310 -> 137 bytes wadsrc_bm/brightmaps/doom/CPOSE3.png | Bin 322 -> 146 bytes wadsrc_bm/brightmaps/doom/CPOSE5.png | Bin 288 -> 116 bytes wadsrc_bm/brightmaps/doom/CPOSE6.png | Bin 253 -> 116 bytes wadsrc_bm/brightmaps/doom/CPOSE7.png | Bin 327 -> 138 bytes wadsrc_bm/brightmaps/doom/CPOSE8.png | Bin 285 -> 143 bytes wadsrc_bm/brightmaps/doom/CPOSF1.png | Bin 775 -> 467 bytes wadsrc_bm/brightmaps/doom/CPOSF2.png | Bin 812 -> 488 bytes wadsrc_bm/brightmaps/doom/CPOSF3.png | Bin 675 -> 400 bytes wadsrc_bm/brightmaps/doom/CPOSF4.png | Bin 323 -> 178 bytes wadsrc_bm/brightmaps/doom/CPOSF5.png | Bin 327 -> 143 bytes wadsrc_bm/brightmaps/doom/CPOSF6.png | Bin 377 -> 214 bytes wadsrc_bm/brightmaps/doom/CPOSF7.png | Bin 616 -> 341 bytes wadsrc_bm/brightmaps/doom/CPOSF8.png | Bin 617 -> 352 bytes wadsrc_bm/brightmaps/doom/CYBRF1.png | Bin 3173 -> 1895 bytes wadsrc_bm/brightmaps/doom/CYBRF2.png | Bin 3241 -> 1976 bytes wadsrc_bm/brightmaps/doom/CYBRF3.png | Bin 3144 -> 1830 bytes wadsrc_bm/brightmaps/doom/CYBRF4.png | Bin 2735 -> 1540 bytes wadsrc_bm/brightmaps/doom/CYBRF5.png | Bin 1844 -> 1014 bytes wadsrc_bm/brightmaps/doom/CYBRF6.png | Bin 1562 -> 852 bytes wadsrc_bm/brightmaps/doom/CYBRF7.png | Bin 2760 -> 1597 bytes wadsrc_bm/brightmaps/doom/CYBRF8.png | Bin 3871 -> 2259 bytes wadsrc_bm/brightmaps/doom/FATTG1.png | Bin 324 -> 111 bytes wadsrc_bm/brightmaps/doom/FATTG2G8.png | Bin 330 -> 110 bytes wadsrc_bm/brightmaps/doom/FATTH1.png | Bin 549 -> 266 bytes wadsrc_bm/brightmaps/doom/FATTH2H8.png | Bin 483 -> 184 bytes wadsrc_bm/brightmaps/doom/FATTH3H7.png | Bin 461 -> 182 bytes wadsrc_bm/brightmaps/doom/FATTH4H6.png | Bin 389 -> 140 bytes wadsrc_bm/brightmaps/doom/FATTH5.png | Bin 373 -> 114 bytes wadsrc_bm/brightmaps/doom/FCANA0.png | Bin 654 -> 350 bytes wadsrc_bm/brightmaps/doom/FCANB0.png | Bin 546 -> 326 bytes wadsrc_bm/brightmaps/doom/FCANC0.png | Bin 567 -> 315 bytes wadsrc_bm/brightmaps/doom/PLAYF1.png | Bin 103 -> 330 bytes wadsrc_bm/brightmaps/doom/PLAYF2F8.png | Bin 105 -> 367 bytes wadsrc_bm/brightmaps/doom/PLAYF3F7.png | Bin 106 -> 355 bytes wadsrc_bm/brightmaps/doom/PLAYF4F6.png | Bin 102 -> 286 bytes wadsrc_bm/brightmaps/doom/PLAYF5.png | Bin 0 -> 275 bytes wadsrc_bm/brightmaps/doom/POSSE1.png | Bin 196 -> 97 bytes wadsrc_bm/brightmaps/doom/POSSE2E8.png | Bin 218 -> 97 bytes wadsrc_bm/brightmaps/doom/POSSE3E7.png | Bin 247 -> 98 bytes wadsrc_bm/brightmaps/doom/POSSF1.png | Bin 380 -> 215 bytes wadsrc_bm/brightmaps/doom/POSSF2F8.png | Bin 437 -> 232 bytes wadsrc_bm/brightmaps/doom/POSSF3F7.png | Bin 404 -> 202 bytes wadsrc_bm/brightmaps/doom/POSSF4F6.png | Bin 314 -> 165 bytes wadsrc_bm/brightmaps/doom/POSSF5.png | Bin 227 -> 127 bytes wadsrc_bm/brightmaps/doom/SMRTA0.png | Bin 380 -> 240 bytes wadsrc_bm/brightmaps/doom/SMRTB0.png | Bin 369 -> 248 bytes wadsrc_bm/brightmaps/doom/SMRTC0.png | Bin 363 -> 245 bytes wadsrc_bm/brightmaps/doom/SMRTD0.png | Bin 372 -> 250 bytes wadsrc_bm/brightmaps/doom/SPOSE1.png | Bin 196 -> 97 bytes wadsrc_bm/brightmaps/doom/SPOSE2E8.png | Bin 201 -> 97 bytes wadsrc_bm/brightmaps/doom/SPOSE3E7.png | Bin 230 -> 97 bytes wadsrc_bm/brightmaps/doom/SPOSF1.png | Bin 456 -> 272 bytes wadsrc_bm/brightmaps/doom/SPOSF2F8.png | Bin 446 -> 257 bytes wadsrc_bm/brightmaps/doom/SPOSF3F7.png | Bin 442 -> 266 bytes wadsrc_bm/brightmaps/doom/SPOSF4F6.png | Bin 399 -> 245 bytes wadsrc_bm/brightmaps/doom/SPOSF5.png | Bin 277 -> 155 bytes wadsrc_bm/brightmaps/doom/TREDA0.png | Bin 797 -> 609 bytes wadsrc_bm/brightmaps/doom/TREDB0.png | Bin 811 -> 628 bytes wadsrc_bm/brightmaps/doom/TREDC0.png | Bin 826 -> 629 bytes wadsrc_bm/brightmaps/doom/TREDD0.png | Bin 824 -> 635 bytes wadsrc_bm/brightmaps/doom/TbluB0.png | Bin 819 -> 632 bytes wadsrc_bm/brightmaps/doom/TbluC0.png | Bin 834 -> 638 bytes wadsrc_bm/doomdefs.bm | 7 + wadsrc_bm/zdoom.lst | 17 + 262 files changed, 11637 insertions(+), 16759 deletions(-) create mode 100644 snes_spc/Makefile create mode 100644 snes_spc/changes.txt create mode 100644 snes_spc/demo/benchmark.c create mode 100644 snes_spc/demo/comm.c create mode 100644 snes_spc/demo/demo_util.c create mode 100644 snes_spc/demo/demo_util.h create mode 100644 snes_spc/demo/play_spc.c create mode 100644 snes_spc/demo/save_state.c create mode 100644 snes_spc/demo/trim_spc.c create mode 100644 snes_spc/demo/wave_writer.c create mode 100644 snes_spc/demo/wave_writer.h create mode 100644 snes_spc/fast_dsp/SPC_DSP.cpp create mode 100644 snes_spc/fast_dsp/SPC_DSP.h create mode 100644 snes_spc/license.txt create mode 100644 snes_spc/readme.txt create mode 100644 snes_spc/slow_dsp/SPC_DSP.cpp create mode 100644 snes_spc/slow_dsp/SPC_DSP.h create mode 100644 snes_spc/snes_spc.txt create mode 100644 snes_spc/snes_spc.vcproj create mode 100644 snes_spc/snes_spc/SNES_SPC.cpp create mode 100644 snes_spc/snes_spc/SNES_SPC.h create mode 100644 snes_spc/snes_spc/SNES_SPC_misc.cpp create mode 100644 snes_spc/snes_spc/SNES_SPC_state.cpp create mode 100644 snes_spc/snes_spc/SPC_CPU.h create mode 100644 snes_spc/snes_spc/SPC_DSP.cpp create mode 100644 snes_spc/snes_spc/SPC_DSP.h create mode 100644 snes_spc/snes_spc/SPC_Filter.cpp create mode 100644 snes_spc/snes_spc/SPC_Filter.h create mode 100644 snes_spc/snes_spc/blargg_common.h create mode 100644 snes_spc/snes_spc/blargg_config.h create mode 100644 snes_spc/snes_spc/blargg_endian.h create mode 100644 snes_spc/snes_spc/blargg_source.h create mode 100644 snes_spc/snes_spc/dsp.cpp create mode 100644 snes_spc/snes_spc/dsp.h create mode 100644 snes_spc/snes_spc/spc.cpp create mode 100644 snes_spc/snes_spc/spc.h create mode 100644 src/dobjgc.cpp delete mode 100644 src/modplug/load_669.cpp delete mode 100644 src/modplug/load_abc.cpp delete mode 100644 src/modplug/load_amf.cpp delete mode 100644 src/modplug/load_ams.cpp delete mode 100644 src/modplug/load_dbm.cpp delete mode 100644 src/modplug/load_dmf.cpp delete mode 100644 src/modplug/load_dsm.cpp delete mode 100644 src/modplug/load_far.cpp delete mode 100644 src/modplug/load_j2b.cpp delete mode 100644 src/modplug/load_mdl.cpp delete mode 100644 src/modplug/load_med.cpp delete mode 100644 src/modplug/load_mid.cpp delete mode 100644 src/modplug/load_mt2.cpp delete mode 100644 src/modplug/load_mtm.cpp delete mode 100644 src/modplug/load_okt.cpp delete mode 100644 src/modplug/load_pat.cpp delete mode 100644 src/modplug/load_pat.h delete mode 100644 src/modplug/load_psm.cpp delete mode 100644 src/modplug/load_ptm.cpp delete mode 100644 src/modplug/load_ult.cpp delete mode 100644 src/modplug/load_wav.cpp create mode 100644 tools/updaterevision/Makefile create mode 100644 tools/updaterevision/trustinfo.rc create mode 100644 tools/updaterevision/trustinfo.txt create mode 100644 tools/updaterevision/updaterevision.c create mode 100644 tools/updaterevision/updaterevision.vcproj create mode 100644 wadsrc_bm/brightmaps/doom/PLAYF5.png diff --git a/docs/rh-log.txt b/docs/rh-log.txt index af259422..92f5bca3 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,160 @@ +March 12, 2008 (Changes by Graf Zahl) +- Bumped the minimum savegame version because the current version crashes + each time an old savegame is loaded. + +March 11, 2008 +- Removed lots of spc_* cvars that are no longer meaningful and changed + spc_amp from a x.4 fixed point number to a normal float. +- Switched SPC playback from the external SNESAPU.DLL to Blargg's LGPL + snes_spc library. I've compiled it with the fast DSP rather than the + highly accurate one, since I didn't notice a meaningful difference between + the two in my limited testing. In short: SPC playback is now built in to + ZDoom. You don't need to download anything extra to make it work, and it + also works on Linux as well as Windows (though building with Linux is + currently untested). +- Fixed: Stereo separation was calculated very wrongly when in 2D sound mode. + +March 10, 2008 +- EAX is a pain in the butt to get it to work reliably. Why? I can get the + reverb to work just fine with software, but with hardware, I hear nothing + special at all. +- Fixed: Sounds apparently don't default to location 0,0,0 so I need to set + that explicitly for 2D sounds in 3D mode. +- Fixed: I had forgotten to actually set the head relative flag for 2D sounds + played in 3D. +- Fixed: Reverb was applied to digital music in software 3D mode. I tried + turning off reverb for 2D sounds too, but that turned it off for _all_ + subsequent sounds and not that specific channel. + +March 9, 2008 (Changes by Graf Zahl) +- fixed: StreamSong::SetPosition required a NULL pointer check. +- fixed: The release build still linked to the old FMOD version. +- fixed: SPCSong only works for Win32 so its definition must be excluded for Linux. + +March 8, 2008 +- Fixed: If you wanted to make cleandep with MinGW, you had to specifically + specify Makefile.mingw as the makefile to use. +- Added a normalizer to the OPL synth. It helped bring up the volume a little, + but not nearly as much as I would have liked. +- Removed MIDI Mapper references. It doesn't work with the stream API, and + it doesn't really exist on NT kernels, either. +- Reworked music volume: Except for MIDI, all music volume is controlled + through GSnd and not at the individual song level. +- Removed the mididevice global variable. +- Added back support for custom streams. + +March 7, 2008 +- I think the new FMOD Ex code should support everything the old FMOD 3 code + did, sans custom streaming sounds. +- Removed snd_midivolume. Now that all music uses a linear volume scale, + there's no need for two separate music volume controls. +- Increased snd_samplerate default up to 48000. +- Added snd_format, defaulting to "PCM-16". +- Added snd_speakermode, defaulting to "Auto". +- Replaced snd_fpu with snd_resampler, defaulting to "Linear". +- Bumped the snd_channels default up from a pitiful 12 to 32. +- Changed snd_3d default to true. The new cvar snd_hw3d determines if + hardware 3D support is used and default to false. +- Removed the libFLAC source, since FMOD Ex has native FLAC support. +- Removed the altsound code, since it was terribly gimped in comparison to + the FMOD code. Its original purpose was to have been as a springboard for + writing a non-FMOD sound system for Unix-y systems, but that never + happened. +- Finished preliminary FMOD Ex support. + +March 6, 2008 +- Fixed: If P_BounceWall() can't find a wall when it does its trace, but it + was entered because a line blocked the projectile, then it should still use + that blocking line for the bounce. +- The full master volume SysEx is now always sent to the MIDI device, even if + it seems to have a working volume control. +- Renamed music_midi_stream.cpp to music_midi_base.cpp. +- Moved the WinMM MIDI code into a new container class. +- Fixed: StatusBar and screen need to be barriered, because they are roots + but can also change at runtime. +- Fixed: Objects that are forcibly deleted outside the GC need to be removed + from the gray list too. + +March 5, 2008 +- Fixed: Thinkers needed write barriers when they were removed from their + lists. +- Fixed: DLevelScript::Link() and Unlink() needed write barriers. + +March 4, 2008 +- Moved the identical code between the MUS and MIDI streamers into a new base + class so that all the low-level details of MIDI streaming are kept in + one place. +- Converted the SMF MIDI playback to use the same MIDI streams as MUS + playback. +- Moved MUS playback back into its own thread so that it can continue + uninterrupted if the main thread is too busy to service it in a timely + manner. +- Fixed: The MEVT_* values are not defined shifted into their spot for a + MIDIEVENT, so I need to do it myself. +- Fixed: Pausing a MUS and then changing snd_midivolume caused the paused + notes to become audible. + +March 3, 2008 +- Changed MUS playback to use MIDI streams, like it did during the early days + of ZDoom, except now the entire song isn't prebuffered in large chunks, so + I can insert MIDI events into the playback with fairly low latency. This + should offer more precise timing than the combination of low-level MIDI and + WaitForMultipleObjects timeouts that it replaces. +- Fixed: PTR_BounceTraverse only checked for projectiles that were too + high to pass through two-sided lines, but not ones that were too low. +- Fixed: SBARINFO couldn't detect the extreme death damage type for the + player face animation. + +March 1, 2008 (Changes by Graf Zahl) +- fixed: A_CountdownArg used 0 based indices although all uses of it assumed + it is 1-based. +- added MF5_DONTRIP flag. +- added CheckActorFloorTexture, CheckActorCeilingTexture and + GetActorLightLevel ACS functions. +- added IF_ADDITIVETIME flag to create powerups that add their duration + to the one of the currently active item of the same type. +- fixed: bouncecount wasn't decreased when bouncing on walls. +- Added MF5_ALWAYSRESPAWN and MF5_NEVERRESPAWN flags that selectively + enable or disable monster respawning regardless of skill setting. +- Prettified deprecated flag handling. +- Fixed: When starting a level while the music has been paused S_Start has + to stop the old music so the new one always starts at the beginning. +- Fixed:: AActor::master was not serialized. +- Fixed: Sound targets pointing to dead players should be cleared before + respawning the player. +- Fixed: When the DONTMOVE flag is set A_Chase must not call P_NewChaseDir. +- Changed PowerupGiver initialization so that the actual powerup class is looked + up during postprocessing. +- Gave Strife's instant death sector type its own damage type. + +February 29, 2008 +- Changed the default GC parameters to make it run more aggressively. +- Added NULL checks to the GC::WriteBarrier() wrappers. +- Fixed: The DObject destructor set GC::SweepPos incorrectly if it pointed + at that object being deleted. +- Added "soft roots" to the garbage collector. These are similar in + purpose to setting the OF_Fixed bit, but they behave as full-fledged roots + as far as the collector is concerned, so any objects reachable through + them will not be collected. +- Added support for the OF_Fixed bit. This is ONLY for simple objects which + do not contain pointers to other objects, because all it does is prevent + an object that is otherwise dead from being freed. It does not include + the object in the propagate stage if it is unreachable, so any objects + that are only reachable through it will still be freed unless they are + also fixed. +- Fixed: R_SetupAddClampCol() checked add4cols' memory instead of + adclamp4cols' memory when deciding if it should skip modification. + +February 28, 2008 +- Fixed: The colormap changes by column, so the assembly rt_* routines need + to be setup for every column group, not once per image. +- Fixed: rt_Translate1col() had a couple of bugs. + +February 27, 2008 +- Added assembly versions of rt_add4cols and rt_addclamp4cols. +- Added a read barrier to FArchive::SerializeObject() to make sure that + objects that are being euthanized are not stored in the archive. + February 26, 2008 - Added an assembly version of rt_shaded4cols, since that's the main decal drawing function. The most improvement came from being able to turn some @@ -11,12 +168,54 @@ February 26, 2008 the translation in one step and the drawing in another. This lets me call the untranslated drawer to do the real drawing instead of mostly duplicating them. Performance wise, there is practically no difference from before. +- Fixed: Using +stat from the command line caused a crash. +- More write barriers and pointer declarations. Here are rules I've come up + with so far for when write barriers can be ommitted: + * Initializing pointers for a newly created object: A new object can never + black, so none of its pointers that get set by the constructor (or code + just following its creation) need to be behind write barriers. + * Creating a new thinker and storing it in a pointer: The thinker + constructor already puts it behind a write barrier when it links it into + the thinker list, so you don't need to do it again. + * As a corollary of the above: No pointers to thinkers should need to be + behind write barriers, because it is incorrect to have a thinker that is + live but not in a thinker list. I realized this while I was going through + the long list of actor target references, so there are some write barriers + for those in place already, since they don't hurt to have them around. + As a consequence of the last point, I think I'm done placing write barriers + in all their necessary places without even touching most of the code. I'm + going to let it sit like this for a few days to see if I can think of a + counter-example where a live thinker wouldn't be in a thinker list, but it + feels correct to me right now. February 25, 2008 (Changes by Graf Zahl) - Fixed: The DECORATE expression evaluator's random function could produce incorrect results for ranges > 255. Changed so that FRandom's default implementation is used instead. +February 24, 2008 +- Fixed: DDecalFader::Tick() still referenced TheDecal even after destroying + it. +- Added write barriers around the thinker list insertions in dthinker.cpp. + +February 23, 2008 (Changes by Graf Zahl) +- Fixed: AWeaponPiece didn't declare its FullWeapon pointer as such. +- Fixed: Hexen's random player class skill menu was missing the '$' indicating + a string table identifier. +- Fixed: DCorpsePointer::Serialize didn't call the super method. + +February 22, 2008 +- Moved DSeqNode's and its children's destructor code into their Destroy() + methods. +- Moved DSectorEffect's destructor code into a Destroy() method. +- Changed the myoffsetof definition to match DECLARE_POINTER's because GCC + started complaining about its use in p_setup.cpp, though I'm not sure why. +- Added a pointer list to DACSThinker so that the collector can find scripts + instead of thinking they're all dead. +- Added read barriers to sector_t's pointers. +- Used the HAS_OBJECT_POINTERS macros to locate places to place TObjPtrs to + form read barriers. + February 21, 2008 - Fixed: DThinker::SerializeAll() did not serialize any thinkers in the FreshThinkers lists. These thinkers would still be saved in the savegame if @@ -34,10 +233,30 @@ February 21, 2008 work for GCC (and presumably VC++, though I never ran into that case with it) because it tried to stringify something that wasn't a macro argument. +February 20, 2008 +- Changed the DHUDMessage deletions into destroys. +- Added read barriers to the pointers inside AActor. +- Split the AActor::LastLook union into its constituent parts. + February 20, 2008 (Changes by Graf Zahl) - Added a modified version of Karate Chris's submission for killing specific monsters with the 'Kill' ccmd. +February 19, 2008 +- Added a rough initial garbage collector, based largely on Lua's. There are + no write or read barriers in place yet, so it's not a very close + approximation of the pointer cleaning code that was in place before, but it + does collect stuff. But guess what! Nuts.wad ran at 1-2 FPS before. Now + it's way, way more and easily stays above 35 FPS. The performance hit when + the GC is running is pretty minimal compared to the old way, which was in + many ways like a full garbage collection every tic. +- Changed the status bars into DObjects. +- Fixed: FBaseStatusBar::Tick() deleted hud messages instead of destroying + them. +- Changed the global Args object to a pointer to one. +- Changed several DArgs deletions into destroys. +- Removed DBoundingBox from the DObject hierarchy. + February 18, 2008 - Added vid_refreshrate cvar to override Windows' automatic refresh rate selection. @@ -632,7 +851,7 @@ January 1, 2008 - Fixed a D3D memory leak on every frame in windowed mode and the same thing for the screen wipes. Note to self: If it's an interface, be sure to Release it, because it will be AddRef'ed before being returned to you. -- Moved the BlendView() call out of FBaseStatusBar::Draw() so that it can be +- Moved the BlendView() call out of DBaseStatusBar::Draw() so that it can be applied before copying the 3D scene to the screen underneath the 2D parts. - Restored the console's darkening level to its old table-based amount. - Fixed D3DFB::SetColorOverlay()'s incorrect calculations. @@ -4330,7 +4549,7 @@ April 18, 2006 (Changes by Graf Zahl) fighting the programmer. April 18, 2006 -- Fixed: FBaseStatusBar::DrBNumber() should behave like Doom's +- Fixed: DBaseStatusBar::DrBNumber() should behave like Doom's STlib_drawNum(), and FDoomStatusBarTexture::DrawToBar() should add the textures left offset to the x coordinate before drawing. These fix Twice Risen's status bar. @@ -5143,7 +5362,7 @@ February 11, 2005 February 10, 2005 - Fixed: The screen fading ACS commands did not work when viewing through a camera - because FBaseStatusBar::BlendView() did not think there was a player to get those + because DBaseStatusBar::BlendView() did not think there was a player to get those values from. Now, the blend comes from either the player viewing from or the console player if the current view is not from a player. - Fixed: Returning to a previously visited level in a hub with fewer players than you @@ -5318,7 +5537,7 @@ January 26, 2005 Unfortunately, I didn't bump the demo version between 2.0.63a and 2.0.90, so anything pre-2.0.97 won't play with 2.0.97, even though there's probably no changes that would cause desynching between 96 and 97. Oh well. -- Fixed FBaseStatusBar::DrBNumber(Outer) to work when some of the big number +- Fixed DBaseStatusBar::DrBNumber(Outer) to work when some of the big number graphics are missing. January 15, 2005 @@ -5494,7 +5713,7 @@ December 18, 2004 on top of the next digit. - Fixed: The Doom HUD drew the selected inventory item on top of the secondary ammo if both were present. -- Moved the DrawPowerups() call out of FBaseStatusBar::Draw() and into +- Moved the DrawPowerups() call out of DBaseStatusBar::Draw() and into DrawTopStuff(), so the Doom HUD key display won't cover them up - Fixed: If you started flying and then pressed the land key, you wouldn't be able to fly again until the Wings of Wrath wore off. This was done by changing @@ -6350,7 +6569,7 @@ July 15, 2004 - Updated to FMOD 3.73. July 14, 2004 -- Moved the full-screen HUD coordinate logic out of FBaseStatusBar and into +- Moved the full-screen HUD coordinate logic out of DBaseStatusBar and into DCanvas::DrawTexture. This is so that powerups can draw their status icons themselves without needing to hook into the status bar. - Reimplemented the Tome of Power. @@ -6919,7 +7138,7 @@ December 12, 2003 up okay in Doom.) - Added a fix to the blockmap tracers where all the blocks along the trace are crossed on their corners. -- Fixed a potential crash in FBaseStatusBar::DrawMessages() when a HUD message +- Fixed a potential crash in DBaseStatusBar::DrawMessages() when a HUD message removes itself as part of its drawing process. - Fixed: A few of the Strife weapons were erroneously defined with WIF_NOALERT. - Fixed: Sky textures defaulted to 0 if a map had a MAPINFO that did not @@ -7067,7 +7286,7 @@ November 23, 2003 things spawned afterward. - Added calls to SetWindowLongPtr to take away the window's border in fullscreen mode and put it back in windowed mode. -- Fixed FBaseStatusBar::RepositionCoords() so that LilWhiteMouse's Chosen +- Fixed DBaseStatusBar::RepositionCoords() so that LilWhiteMouse's Chosen ammo icons appear on the fullscreen HUD again. November 21, 2003 @@ -7799,7 +8018,7 @@ July 30, 2003 - Added nonexistant texture check to the warping texture setup. July 28, 2003 -- Removed the ScaleCopy canvas from FBaseStatusBar, because everything is now +- Removed the ScaleCopy canvas from DBaseStatusBar, because everything is now drawn to the screen directly even when the status bar is scaled. - Changed the pitch calculations in the DSimpleCanvas constructor to provide better wall drawing performance at all resolutions. @@ -10941,7 +11160,7 @@ October 23, 2001 torch lighting up the entire sky, so skies should ignore the setting of fixedlightlev. - Fixed the scaled status bar. I was simply forgetting to add the image - offsets in FBaseStatusBar::DrawImage() before calling CopyToScreen(). + offsets in DBaseStatusBar::DrawImage() before calling CopyToScreen(). - Added code to let decals move with sliding and rotating polyobjects. - Fixed: The wait console command waited one tic too few. - Fixed a resizing problem with playing movies windowed. @@ -11761,7 +11980,7 @@ April 7, 2001 big ammo count when switching to a weapon that does not use ammo. - Fixed: At certain screen heights, scaled status bars would not touch the bottom of the screen because of inaccuracy in the calculation of ::ST_Y - in FBaseStatusBar::SetScaled(). + in DBaseStatusBar::SetScaled(). - Added separate pickup sounds for health, armor, and ammo items. - Improved sound link resolution in S_StartSound() so that regular sounds can alias player sounds. diff --git a/gzdoom.sln b/gzdoom.sln index 19ece822..e079b135 100644 --- a/gzdoom.sln +++ b/gzdoom.sln @@ -11,8 +11,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zdoom", "gzdoom.vcproj", "{ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib\zlib.vcproj", "{F9D9E7D4-E1A2-4866-9E85-B1B14137EE63}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FLAC", "FLAC\FLAC.vcproj", "{873F2EEA-24DF-454C-B245-CB9738BA993E}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lemon", "tools\lemon\lemon.vcproj", "{0F80ACBF-460E-44F0-B28E-B3272D1774A7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "re2c", "tools\re2c\re2c.vcproj", "{667D2EE7-C357-49E2-9BAB-0A4A45F0F76E}" @@ -37,10 +35,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dehsupp", "tools\dehsupp\de {0F80ACBF-460E-44F0-B28E-B3272D1774A7} = {0F80ACBF-460E-44F0-B28E-B3272D1774A7} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "updaterevision", "tools\updaterevision\updaterevision.vcproj", "{6077B7D6-349F-4077-B552-3BC302EF5859}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jpeg-6b", "jpeg-6b\jpeg-6b.vcproj", "{AC3F5340-40CB-4C3A-8AA7-CB7158DB4466}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fixrtext", "tools\fixrtext\fixrtext.vcproj", "{DA47396F-60C1-4BDE-A977-7F7DE461CF77}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "snes_spc", "snes_spc\snes_spc.vcproj", "{E83FD370-2E72-4D4C-9427-FF9D9DED1E88}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "brightmaps", "wadsrc_bm\brightmaps.vcproj", "{087B206F-F49E-4EFB-92CB-E1F6E32D1278}" ProjectSection(ProjectDependencies) = postProject {24A19C02-F041-4AB0-A1A1-02E1E88EDBD3} = {24A19C02-F041-4AB0-A1A1-02E1E88EDBD3} diff --git a/gzdoom.vcproj b/gzdoom.vcproj index dcd7a4fc..af410061 100644 --- a/gzdoom.vcproj +++ b/gzdoom.vcproj @@ -28,6 +28,8 @@ > + + diff --git a/snes_spc/Makefile b/snes_spc/Makefile new file mode 100644 index 00000000..c32c06cc --- /dev/null +++ b/snes_spc/Makefile @@ -0,0 +1,52 @@ +# Makefile for snes_spc, derived from zlib/Makefile.mgw. + +ifeq (Windows_NT,$(OS)) + CMD=1 +endif +ifeq ($(findstring msys,$(shell sh --version 2>nul)),msys) + CMD=0 +endif + +STATICLIB = libsnes_spc.a + +CCDV = @../ccdv + +CC = gcc +CFLAGS = $(LOC) -O3 -Wall -fomit-frame-pointer + +LD = $(CC) +LDFLAGS = $(LOC) -s + +AR = ar +ARFLAGS = rcs + +OBJS = snes_spc/dsp.o snes_spc/SNES_SPC.o snes_spc/SNES_SPC_misc.o snes_spc/SNES_SPC_state.o \ + snes_spc/spc.o snes_spc/SPC_DSP.o snes_spc/SPC_Filter.o + +all: $(STATICLIB) + +.cpp.o: + $(CCDV) $(CC) $(CFLAGS) -c -o $@ $< + +$(STATICLIB): $(OBJS) + $(CCDV) $(AR) $(ARFLAGS) $@ $(OBJS) + + +.PHONY: clean + +clean: +ifeq (0,$(CMD)) + rm -f $(STATICLIB) + rm -f snes_spc/*.o +else + -del /q /f $(STATICLIB) 2>nul + -del /q /f snes_spc\*.o 2>nul +endif + +dsp.o: snes_spc/dsp.cpp snes_spc/dsp.h snes_spc/SPC_DSP.h +SNES_SPC.o: snes_spc/SNES_SPC.cpp snes_spc/SNES_SPC.h snes_spc/SPC_DSP.h +SNES_SPC_misc.o: snes_spc/SNES_SPC_misc.cpp snes_spc/SNES_SPC.h snes_spc/SPC_DSP.h +SNES_SPC_state.o: snes_spc/SNES_SPC_state.cpp snes_spc/SNES_SPC.h snes_spc/SPC_DSP.h +spc.o: snes_spc/spc.cpp snes_spc/spc.h snes_spc/SNES_SPC.h snes_spc/SPC_DSP.h snes_spc/SPC_Filter.h +SPC_DSP.o: snes_spc/SPC_DSP.cpp snes_spc/SPC_DSP.h +SPC_Filter.o: snes_spc/SPC_Filter.cpp snes_spc/SPC_Filter.h diff --git a/snes_spc/changes.txt b/snes_spc/changes.txt new file mode 100644 index 00000000..33661832 --- /dev/null +++ b/snes_spc/changes.txt @@ -0,0 +1,107 @@ +snes_spc Change Log +------------------- + +snes_spc 0.9.0 +-------------- +- Improved documentation + +- SPC: Added spc_skip() function for quickly seeking in an SPC music +file. Runs 3-4x faster than normal playback using the fast DSP (or about +43-60X real-time on my 400 MHz Mac). + +- SPC: Added spc_set_tempo() to change tempo of SPC music playback. + +- SPC: Sample generation is now corrected to generate exactly one pair +of samples every 32 clocks without exception. Before it could generate a +few samples more or less depending on how far ahead or behind DSP was at +the moment. + +- SPC: Changed spc_reset() and spc_soft_reset() to also reset output +buffer (see spc.h). + +- SPC: Fixed minor timer counting bug. + +- SPC: Stack pointer wrap-around is now emulated (and without any +noticeable performance hit). + +- SPC: Runs about 5% faster due to various optimizations. + +- SPC: Found way to make fast DSP register accesses cycle-accurate in +most cases, without reducing performance. Allows fast DSP to pass most +of my validation tests. + +- DSP: Added surround disable support to fast DSP again. + +- DSP: Improved voice un-muting to take effect immediately on fast DSP. + +- DSP: Noise shift register now starts at 0x4000 instead of 0x4001 as it +incorrectly did before. + +- Converted library to C++ code internally. A C interface is still +included in spc.h and dsp.h. Note that these are different than the +previous interface, so your code will require minor changes: + + Old SPC code New SPC code + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + #include "spc/spc.h" #include "snes_spc/spc.h" + + snes_spc_t* spc; SNES_SPC* spc; + spc = malloc( sizeof (snes_spc_t) ); spc = spc_new(); + spc_init( spc ); + + spc_end_frame( time ); spc_end_frame( spc, time ); + /* etc. */ + + /* done using SPC */ spc_delete( spc ); + + + Old DSP code New DSP code + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + #include "spc/spc_dsp.h" #include "snes_spc/dsp.h" + + spc_dsp_init( ram ); SPC_DSP* dsp; + dsp = spc_dsp_new(); + spc_dsp_init( dsp, ram ); + + spc_dsp_run( count ); spc_dsp_run( dsp, count ); + /* etc. */ + + /* done using DSP */ spc_dsp_delete( dsp ); + + +snes_spc 0.8.0 +-------------- +- Added several demos + +- Added high-pass/low-pass filter to better match SNES sound + +- Added save state functionality for SPC and accurate DSP (but not fast +DSP) + +- Added emulation of reset switch on NES (soft reset) + +- Made source more compatible with pre-C99 compilers by eliminating +mid-block declarations + +- SPC: Many S-SMP accuracy improvements, mostly in memory access times + +- SPC: S-SMP speed improvements + +- SPC: Added SPC load/save functions and KON checking to help trim +silence from beginning + +- SPC: Changed spc_init() to have you allocate most of the memory used +by the library so you have more control over it + +- DSP: New highly accurate DSP and faster version derived from same code + +- DSP: Changed prefix from dsp_ to spc_dsp_. Your DSP code will require +changes. + +- DSP: Removed surround disable and gain. Gain can now be done with the +dsp_filter module, and surround disable will probably only be +implemented in the fast DSP at some point. + +- DSP: Changed interface to work in clocks rather than samples, +necessary for the new accurate DSP. Sample output is now done with +separate functions. Your DSP code will require changes. diff --git a/snes_spc/demo/benchmark.c b/snes_spc/demo/benchmark.c new file mode 100644 index 00000000..6b91faaf --- /dev/null +++ b/snes_spc/demo/benchmark.c @@ -0,0 +1,58 @@ +/* Measures performance of SPC emulator. Takes about 4 seconds. +NOTE: This assumes that the program is getting all processor time; you might need to +arrange for this or else the performance will be reported lower than it really is. + +Usage: benchmark [test.spc] +*/ + +#include "snes_spc/spc.h" + +#include "demo_util.h" /* error(), load_file() */ +#include + +clock_t start_timing( int seconds ); + +int main( int argc, char** argv ) +{ + /* Load SPC */ + long spc_size; + void* spc = load_file( (argc > 1) ? argv [1] : "test.spc", &spc_size ); + SNES_SPC* snes_spc = spc_new(); + if ( !snes_spc ) error( "Out of memory" ); + spc_load_spc( snes_spc, spc, spc_size ); + free( spc ); + + { + /* Repeatedly fill buffer for 4 seconds */ + int const seconds = 4; + #define BUF_SIZE 4096 + clock_t end = start_timing( seconds ); + int count = 0; + while ( clock() < end ) + { + static short buf [BUF_SIZE]; + error( spc_play( snes_spc, BUF_SIZE, buf ) ); + count++; + } + + /* Report performance based on how many buffer fills were possible */ + { + double rate = (double) count * BUF_SIZE / (spc_sample_rate * 2 * seconds); + printf( "Performance: %.3fx real-time, or %.0f%% CPU for normal rate\n", + rate, 100.0 / rate ); + } + } + + return 0; +} + +/* Synchronizes with host clock and returns clock() time that is duration seconds from now */ +clock_t start_timing( int duration ) +{ + clock_t clock_dur = duration * CLOCKS_PER_SEC; + clock_t time = clock(); + while ( clock() == time ) { } + if ( clock() - time > clock_dur ) + error( "Insufficient clock() time resolution" ); + return clock() + clock_dur; +} diff --git a/snes_spc/demo/comm.c b/snes_spc/demo/comm.c new file mode 100644 index 00000000..4198a2de --- /dev/null +++ b/snes_spc/demo/comm.c @@ -0,0 +1,70 @@ +/* Communicates with SPC the way the SNES would. + +Note: You'll need an "spc_rom.h" file that contains the 64-byte IPL ROM contents */ + +#include "snes_spc/spc.h" + +#include "demo_util.h" +#include +#include + +static SNES_SPC* snes_spc; + +/* Make port access more convenient. Fakes time by simply incrementing it each call. */ +static spc_time_t stime; +static int pread ( int port ) { return spc_read_port( snes_spc, stime++, port ); } +static void pwrite( int port, int data ) { spc_write_port( snes_spc, stime++, port, data ); } + +static unsigned char const spc_rom [spc_rom_size] = { + /* ROM data not provided with emulator */ + #include "spc_rom.h" +}; + +int main() +{ + int i; + + /* Data to upload */ + static unsigned char const data [4] = "\xFA\xDE\xD1"; + unsigned const data_addr = 0xF5; /* second I/O port */ + + snes_spc = spc_new(); + if ( !snes_spc ) error( "Out of memory" ); + spc_init_rom( snes_spc, spc_rom ); + spc_reset( snes_spc ); + + /* Simulate reads and writes that SNES code would do */ + + /* Wait for SPC to be ready */ + while ( pread( 0 ) != 0xAA || pread( 1 ) != 0xBB ) { } + + /* Start address */ + pwrite( 2, data_addr & 0xFF ); + pwrite( 3, data_addr >> 8 ); + + /* Tell SPC to start transfer and wait for acknowledgement */ + pwrite( 0, 0xCC ); + pwrite( 1, 0x01 ); + while ( pread( 0 ) != 0xCC ) { } + + /* Send each byte and wait for acknowledgement */ + for ( i = 0; i < 4; i++ ) + { + printf( "%02X ", data [i] ); + pwrite( 1, data [i] ); + pwrite( 0, i ); + while ( pread( 0 ) != i ) { } + } + printf( "\n" ); + + /* Verify that data was transferred properly */ + for ( i = 0; i < 3; i++ ) + printf( "%02X ", pread( i + 1 ) ); + printf( "\n" ); + + printf( "Cycles: %ld\n", (long) stime ); + + spc_delete( snes_spc ); + + return 0; +} diff --git a/snes_spc/demo/demo_util.c b/snes_spc/demo/demo_util.c new file mode 100644 index 00000000..61f250d7 --- /dev/null +++ b/snes_spc/demo/demo_util.c @@ -0,0 +1,57 @@ +#include "demo_util.h" + +#include +#include +#include +#include + +/* Copyright (C) 2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +unsigned char* load_file( const char* path, long* size_out ) +{ + size_t size; + unsigned char* data; + + FILE* in = fopen( path, "rb" ); + if ( !in ) error( "Couldn't open file" ); + + fseek( in, 0, SEEK_END ); + size = ftell( in ); + if ( size_out ) + *size_out = size; + rewind( in ); + + data = (unsigned char*) malloc( size ); + if ( !data ) error( "Out of memory" ); + + if ( fread( data, 1, size, in ) < size ) error( "Couldn't read file" ); + fclose( in ); + + return data; +} + +void write_file( const char* path, void const* in, long size ) +{ + FILE* out = fopen( path, "wb" ); + if ( !out ) error( "Couldn't create file" ); + if ( (long) fwrite( in, 1, size, out ) < size ) error( "Couldn't write file" ); + fclose( out ); +} + +void error( const char* str ) +{ + if ( str ) + { + fprintf( stderr, "Error: %s\n", str ); + exit( EXIT_FAILURE ); + } +} diff --git a/snes_spc/demo/demo_util.h b/snes_spc/demo/demo_util.h new file mode 100644 index 00000000..d52e571b --- /dev/null +++ b/snes_spc/demo/demo_util.h @@ -0,0 +1,31 @@ +/* General-purpose utilities used by demos */ + +/* snes_spc 0.9.0 */ +#ifndef DEMO_UTIL_H +#define DEMO_UTIL_H + +/* commonly used headers */ +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/* If str is not NULL, prints it and exits program, otherwise returns */ +void error( const char* str ); + +/* Loads file and returns pointer to data in memory, allocated with malloc(). +If size_out != NULL, sets *size_out to size of data. */ +unsigned char* load_file( const char* path, long* size_out ); + +/* Writes data to file */ +void write_file( const char* path, void const* in, long size ); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snes_spc/demo/play_spc.c b/snes_spc/demo/play_spc.c new file mode 100644 index 00000000..f3e4d5ae --- /dev/null +++ b/snes_spc/demo/play_spc.c @@ -0,0 +1,57 @@ +/* Records SPC into wave file. Uses dsp_filter to give more authentic sound. + +Usage: play_spc [test.spc] +*/ + +#include "snes_spc/spc.h" + +#include "wave_writer.h" +#include "demo_util.h" /* error(), load_file() */ + +int main( int argc, char** argv ) +{ + /* Create emulator and filter */ + SNES_SPC* snes_spc = spc_new(); + SPC_Filter* filter = spc_filter_new(); + if ( !snes_spc || !filter ) error( "Out of memory" ); + + /* Load SPC */ + { + /* Load file into memory */ + long spc_size; + void* spc = load_file( (argc > 1) ? argv [1] : "test.spc", &spc_size ); + + /* Load SPC data into emulator */ + error( spc_load_spc( snes_spc, spc, spc_size ) ); + free( spc ); /* emulator makes copy of data */ + + /* Most SPC files have garbage data in the echo buffer, so clear that */ + spc_clear_echo( snes_spc ); + + /* Clear filter before playing */ + spc_filter_clear( filter ); + } + + /* Record 20 seconds to wave file */ + wave_open( spc_sample_rate, "out.wav" ); + wave_enable_stereo(); + while ( wave_sample_count() < 20 * spc_sample_rate * 2 ) + { + /* Play into buffer */ + #define BUF_SIZE 2048 + short buf [BUF_SIZE]; + error( spc_play( snes_spc, BUF_SIZE, buf ) ); + + /* Filter samples */ + spc_filter_run( filter, buf, BUF_SIZE ); + + wave_write( buf, BUF_SIZE ); + } + + /* Cleanup */ + spc_filter_delete( filter ); + spc_delete( snes_spc ); + wave_close(); + + return 0; +} diff --git a/snes_spc/demo/save_state.c b/snes_spc/demo/save_state.c new file mode 100644 index 00000000..1a6674cc --- /dev/null +++ b/snes_spc/demo/save_state.c @@ -0,0 +1,107 @@ +/* Loads "test.spc", skips 5 seconds, saves exact emulator state to "state.bin", +hen records 5 seconds to "first.wav". When run again, loads this state back into +emulator and records 5 seconds to "second.wav". These two wave files should +be identical. + +Usage: save_state [test.spc] +*/ + +#include "snes_spc/spc.h" + +#include "wave_writer.h" +#include "demo_util.h" /* error(), load_file() */ + +static SNES_SPC* snes_spc; + +void record_wav( const char* path, int secs ) +{ + /* Start writing wave file */ + wave_open( spc_sample_rate, path ); + wave_enable_stereo(); + while ( wave_sample_count() < secs * spc_sample_rate * 2 ) + { + /* Play into buffer */ + #define BUF_SIZE 2048 + short buf [BUF_SIZE]; + error( spc_play( snes_spc, BUF_SIZE, buf ) ); + + wave_write( buf, BUF_SIZE ); + } + wave_close(); +} + +void state_save( unsigned char** out, void* in, size_t size ) +{ + memcpy( *out, in, size ); + *out += size; +} + +void make_save_state( const char* path ) +{ + /* Load SPC */ + long spc_size; + void* spc = load_file( path, &spc_size ); + error( spc_load_spc( snes_spc, spc, spc_size ) ); + free( spc ); + spc_clear_echo( snes_spc ); + + /* Skip several seconds */ + error( spc_play( snes_spc, 5 * spc_sample_rate * 2, 0 ) ); + + /* Save state to file */ + { + static unsigned char state [spc_state_size]; /* buffer large enough for data */ + unsigned char* out = state; + spc_copy_state( snes_spc, &out, state_save ); + write_file( "state.bin", state, out - state ); + } + + record_wav( "first.wav", 5 ); +} + +void state_load( unsigned char** in, void* out, size_t size ) +{ + memcpy( out, *in, size ); + *in += size; +} + +void use_save_state() +{ + /* Load state into memory */ + long state_size; + unsigned char* state = load_file( "state.bin", &state_size ); + + /* Load into emulator */ + unsigned char* in = state; + spc_copy_state( snes_spc, &in, state_load ); + assert( in - state <= state_size ); /* be sure it didn't read past end */ + + record_wav( "second.wav", 5 ); +} + +int file_exists( const char* path ) +{ + FILE* file = fopen( path, "rb" ); + if ( !file ) + return 0; + + fclose( file ); + return 1; +} + +int main( int argc, char** argv ) +{ + snes_spc = spc_new(); + if ( !snes_spc ) error( "Out of memory" ); + + /* Make new state if it doesn't exist, otherwise load it and + record to wave file */ + if ( !file_exists( "state.bin" ) ) + make_save_state( (argc > 1) ? argv [1] : "test.spc" ); + else + use_save_state(); + + spc_delete( snes_spc ); + + return 0; +} diff --git a/snes_spc/demo/trim_spc.c b/snes_spc/demo/trim_spc.c new file mode 100644 index 00000000..cf91c11e --- /dev/null +++ b/snes_spc/demo/trim_spc.c @@ -0,0 +1,83 @@ +/* Trims silence off beginning of SPC file. +Requires the accurate DSP; won't compile with the fast DSP. +Please note that the algorithm could be improved; this is just +a simple example showing the idea. + +Usage: trim_spc [test.spc [trimmed.spc]] +*/ + +#include "snes_spc/spc.h" + +#include "demo_util.h" /* error(), load_file() */ + +/* Change to 1 to have it trim to next key on event rather than trim silence */ +enum { use_kon_check = 0 }; + +/* True if all count samples from in are silent (or very close to it) */ +int is_silent( short const* in, int count ); + +int main( int argc, char** argv ) +{ + /* Load file into memory */ + long spc_size; + void* spc = load_file( (argc > 1) ? argv [1] : "test.spc", &spc_size ); + + /* Load into emulator */ + SNES_SPC* snes_spc = spc_new(); + if ( !snes_spc ) error( "Out of memory" ); + error( spc_load_spc( snes_spc, spc, spc_size ) ); + + /* Expand SPC data so there's enough room for emulator to save to. + We simply overwrite the emulator data in the old SPC file rather + than creating new SPC data. This preserves the text tags from + the old file. */ + if ( spc_size < spc_file_size ) + { + spc_size = spc_file_size; + spc = realloc( spc, spc_size ); /* leaks memory if allocation fails */ + if ( !spc ) error( "Out of memory" ); + } + + /* Keep saving SPC, then playing a little more. Once SPC becomes non-silent, + write the SPC data saved just before this. */ + { + long samples_trimmed = 0; + while ( 1 ) + { + #define BUF_SIZE 1024 + short buf [BUF_SIZE]; + + if ( samples_trimmed > 10 * spc_sample_rate * 2 ) + error( "Excess silence found" ); + + spc_clear_echo( snes_spc ); + spc_save_spc( snes_spc, spc ); + + /* Fill buffer */ + error( spc_play( snes_spc, BUF_SIZE, buf ) ); + samples_trimmed += BUF_SIZE; + + /* See if SPC became non-silent */ + if ( use_kon_check ? spc_check_kon( snes_spc ) : !is_silent( buf, BUF_SIZE ) ) + break; + } + + printf( "Trimmed %.1f seconds\n", (double) samples_trimmed / spc_sample_rate / 2 ); + } + + spc_delete( snes_spc ); + write_file( (argc > 2) ? argv [2] : "trimmed.spc", spc, spc_size ); + + return 0; +} + +int is_silent( short const* in, int count ) +{ + unsigned const threshold = 0x10; + while ( count-- ) + { + if ( (unsigned) (*in++ + threshold / 2) > threshold ) + return 0; + } + return 1; +} diff --git a/snes_spc/demo/wave_writer.c b/snes_spc/demo/wave_writer.c new file mode 100644 index 00000000..b8da5d60 --- /dev/null +++ b/snes_spc/demo/wave_writer.c @@ -0,0 +1,153 @@ +/* snes_spc 0.9.0. http://www.slack.net/~ant/ */ + +#include "wave_writer.h" + +#include +#include +#include + +/* Copyright (C) 2003-2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +enum { buf_size = 32768 * 2 }; +enum { header_size = 0x2C }; + +typedef short sample_t; + +static unsigned char* buf; +static FILE* file; +static long sample_count_; +static long sample_rate_; +static long buf_pos; +static int chan_count; + +static void exit_with_error( const char* str ) +{ + printf( "Error: %s\n", str ); getchar(); + exit( EXIT_FAILURE ); +} + +void wave_open( long sample_rate, const char* filename ) +{ + sample_count_ = 0; + sample_rate_ = sample_rate; + buf_pos = header_size; + chan_count = 1; + + buf = (unsigned char*) malloc( buf_size * sizeof *buf ); + if ( !buf ) + exit_with_error( "Out of memory" ); + + file = fopen( filename, "wb" ); + if ( !file ) + exit_with_error( "Couldn't open WAVE file for writing" ); + + setvbuf( file, 0, _IOFBF, 32 * 1024L ); +} + +void wave_enable_stereo( void ) +{ + chan_count = 2; +} + +static void flush_() +{ + if ( buf_pos && !fwrite( buf, buf_pos, 1, file ) ) + exit_with_error( "Couldn't write WAVE data" ); + buf_pos = 0; +} + +void wave_write( short const* in, long remain ) +{ + sample_count_ += remain; + while ( remain ) + { + if ( buf_pos >= buf_size ) + flush_(); + + { + unsigned char* p = &buf [buf_pos]; + long n = (buf_size - buf_pos) / sizeof (sample_t); + if ( n > remain ) + n = remain; + remain -= n; + + /* convert to LSB first format */ + while ( n-- ) + { + int s = *in++; + *p++ = (unsigned char) s; + *p++ = (unsigned char) (s >> 8); + } + + buf_pos = p - buf; + assert( buf_pos <= buf_size ); + } + } +} + +long wave_sample_count( void ) +{ + return sample_count_; +} + +static void set_le32( void* p, unsigned long n ) +{ + ((unsigned char*) p) [0] = (unsigned char) n; + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); +} + +void wave_close( void ) +{ + if ( file ) + { + /* generate header */ + unsigned char h [header_size] = + { + 'R','I','F','F', + 0,0,0,0, /* length of rest of file */ + 'W','A','V','E', + 'f','m','t',' ', + 0x10,0,0,0, /* size of fmt chunk */ + 1,0, /* uncompressed format */ + 0,0, /* channel count */ + 0,0,0,0, /* sample rate */ + 0,0,0,0, /* bytes per second */ + 0,0, /* bytes per sample frame */ + 16,0, /* bits per sample */ + 'd','a','t','a', + 0,0,0,0, /* size of sample data */ + /* ... */ /* sample data */ + }; + long ds = sample_count_ * sizeof (sample_t); + int frame_size = chan_count * sizeof (sample_t); + + set_le32( h + 0x04, header_size - 8 + ds ); + h [0x16] = chan_count; + set_le32( h + 0x18, sample_rate_ ); + set_le32( h + 0x1C, sample_rate_ * frame_size ); + h [0x20] = frame_size; + set_le32( h + 0x28, ds ); + + flush_(); + + /* write header */ + fseek( file, 0, SEEK_SET ); + fwrite( h, sizeof h, 1, file ); + + fclose( file ); + file = 0; + free( buf ); + buf = 0; + } +} diff --git a/snes_spc/demo/wave_writer.h b/snes_spc/demo/wave_writer.h new file mode 100644 index 00000000..5472293b --- /dev/null +++ b/snes_spc/demo/wave_writer.h @@ -0,0 +1,20 @@ +/* WAVE sound file writer for recording 16-bit output during program development */ + +#ifndef WAVE_WRITER_H +#define WAVE_WRITER_H + +#ifdef __cplusplus + extern "C" { +#endif + +void wave_open( long sample_rate, const char* filename ); +void wave_enable_stereo( void ); +void wave_write( short const* in, long count ); +long wave_sample_count( void ); +void wave_close( void ); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snes_spc/fast_dsp/SPC_DSP.cpp b/snes_spc/fast_dsp/SPC_DSP.cpp new file mode 100644 index 00000000..96719683 --- /dev/null +++ b/snes_spc/fast_dsp/SPC_DSP.cpp @@ -0,0 +1,703 @@ +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "SPC_DSP.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +#if INT_MAX < 0x7FFFFFFF + #error "Requires that int type have at least 32 bits" +#endif + + +// TODO: add to blargg_endian.h +#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) +#define GET_LE16A( addr ) GET_LE16( addr ) +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) + +static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] = +{ + 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, + 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, + 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, + 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, + 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, + 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, + 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, + 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF +}; + +// if ( io < -32768 ) io = -32768; +// if ( io > 32767 ) io = 32767; +#define CLAMP16( io )\ +{\ + if ( (int16_t) io != io )\ + io = (io >> 31) ^ 0x7FFF;\ +} + +// Access global DSP register +#define REG(n) m.regs [r_##n] + +// Access voice DSP register +#define VREG(r,n) r [v_##n] + +#define WRITE_SAMPLES( l, r, out ) \ +{\ + out [0] = l;\ + out [1] = r;\ + out += 2;\ + if ( out >= m.out_end )\ + {\ + check( out == m.out_end );\ + check( m.out_end != &m.extra [extra_size] || \ + (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ + out = m.extra;\ + m.out_end = &m.extra [extra_size];\ + }\ +}\ + +void SPC_DSP::set_output( sample_t* out, int size ) +{ + require( (size & 1) == 0 ); // must be even + if ( !out ) + { + out = m.extra; + size = extra_size; + } + m.out_begin = out; + m.out = out; + m.out_end = out + size; +} + +// Volume registers and efb are signed! Easy to forget int8_t cast. +// Prefixes are to avoid accidental use of locals with same names. + +// Interleved gauss table (to improve cache coherency) +// interleved_gauss [i] = gauss [(i & 1) * 256 + 255 - (i >> 1 & 0xFF)] +static short const interleved_gauss [512] = +{ + 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303, + 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299, + 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292, + 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282, + 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269, + 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253, + 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234, + 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213, + 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190, + 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164, + 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136, + 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106, + 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074, + 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040, + 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005, + 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969, + 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932, + 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894, + 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855, + 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816, + 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777, + 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737, + 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698, + 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659, + 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620, + 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582, + 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545, + 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508, + 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473, + 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439, + 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405, + 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374, +}; + + +//// Counters + +#define RATE( rate, div )\ + (rate >= div ? rate / div * 8 - 1 : rate - 1) + +static unsigned const counter_mask [32] = +{ + RATE( 2,2), RATE(2048,4), RATE(1536,3), + RATE(1280,5), RATE(1024,4), RATE( 768,3), + RATE( 640,5), RATE( 512,4), RATE( 384,3), + RATE( 320,5), RATE( 256,4), RATE( 192,3), + RATE( 160,5), RATE( 128,4), RATE( 96,3), + RATE( 80,5), RATE( 64,4), RATE( 48,3), + RATE( 40,5), RATE( 32,4), RATE( 24,3), + RATE( 20,5), RATE( 16,4), RATE( 12,3), + RATE( 10,5), RATE( 8,4), RATE( 6,3), + RATE( 5,5), RATE( 4,4), RATE( 3,3), + RATE( 2,4), + RATE( 1,4) +}; +#undef RATE + +inline void SPC_DSP::init_counter() +{ + // counters start out with this synchronization + m.counters [0] = 1; + m.counters [1] = 0; + m.counters [2] = -0x20u; + m.counters [3] = 0x0B; + + int n = 2; + for ( int i = 1; i < 32; i++ ) + { + m.counter_select [i] = &m.counters [n]; + if ( !--n ) + n = 3; + } + m.counter_select [ 0] = &m.counters [0]; + m.counter_select [30] = &m.counters [2]; +} + +inline void SPC_DSP::run_counter( int i ) +{ + int n = m.counters [i]; + if ( !(n-- & 7) ) + n -= 6 - i; + m.counters [i] = n; +} + +#define READ_COUNTER( rate )\ + (*m.counter_select [rate] & counter_mask [rate]) + + +//// Emulation + +void SPC_DSP::run( int clock_count ) +{ + int new_phase = m.phase + clock_count; + int count = new_phase >> 5; + m.phase = new_phase & 31; + if ( !count ) + return; + + uint8_t* const ram = m.ram; + uint8_t const* const dir = &ram [REG(dir) * 0x100]; + int const slow_gaussian = (REG(pmon) >> 1) | REG(non); + int const noise_rate = REG(flg) & 0x1F; + + // Global volume + int mvoll = (int8_t) REG(mvoll); + int mvolr = (int8_t) REG(mvolr); + if ( mvoll * mvolr < m.surround_threshold ) + mvoll = -mvoll; // eliminate surround + + do + { + // KON/KOFF reading + if ( (m.every_other_sample ^= 1) != 0 ) + { + m.new_kon &= ~m.kon; + m.kon = m.new_kon; + m.t_koff = REG(koff); + } + + run_counter( 1 ); + run_counter( 2 ); + run_counter( 3 ); + + // Noise + if ( !READ_COUNTER( noise_rate ) ) + { + int feedback = (m.noise << 13) ^ (m.noise << 14); + m.noise = (feedback & 0x4000) ^ (m.noise >> 1); + } + + // Voices + int pmon_input = 0; + int main_out_l = 0; + int main_out_r = 0; + int echo_out_l = 0; + int echo_out_r = 0; + voice_t* v = m.voices; + uint8_t* v_regs = m.regs; + int vbit = 1; + do + { + #define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] ) + + int brr_header = ram [v->brr_addr]; + int kon_delay = v->kon_delay; + + // Pitch + int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF; + if ( REG(pmon) & vbit ) + pitch += ((pmon_input >> 5) * pitch) >> 10; + + // KON phases + if ( --kon_delay >= 0 ) + { + v->kon_delay = kon_delay; + + // Get ready to start BRR decoding on next sample + if ( kon_delay == 4 ) + { + v->brr_addr = SAMPLE_PTR( 0 ); + v->brr_offset = 1; + v->buf_pos = v->buf; + brr_header = 0; // header is ignored on this sample + } + + // Envelope is never run during KON + v->env = 0; + v->hidden_env = 0; + + // Disable BRR decoding until last three samples + v->interp_pos = (kon_delay & 3 ? 0x4000 : 0); + + // Pitch is never added during KON + pitch = 0; + } + + int env = v->env; + + // Gaussian interpolation + { + int output = 0; + VREG(v_regs,envx) = (uint8_t) (env >> 4); + if ( env ) + { + // Make pointers into gaussian based on fractional position between samples + int offset = (unsigned) v->interp_pos >> 3 & 0x1FE; + short const* fwd = interleved_gauss + offset; + short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian + + int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12]; + + if ( !(slow_gaussian & vbit) ) // 99% + { + // Faster approximation when exact sample value isn't necessary for pitch mod + output = (fwd [0] * in [0] + + fwd [1] * in [1] + + rev [1] * in [2] + + rev [0] * in [3]) >> 11; + output = (output * env) >> 11; + } + else + { + output = (int16_t) (m.noise * 2); + if ( !(REG(non) & vbit) ) + { + output = (fwd [0] * in [0]) >> 11; + output += (fwd [1] * in [1]) >> 11; + output += (rev [1] * in [2]) >> 11; + output = (int16_t) output; + output += (rev [0] * in [3]) >> 11; + + CLAMP16( output ); + output &= ~1; + } + output = (output * env) >> 11 & ~1; + } + + // Output + int l = output * v->volume [0]; + int r = output * v->volume [1]; + + main_out_l += l; + main_out_r += r; + + if ( REG(eon) & vbit ) + { + echo_out_l += l; + echo_out_r += r; + } + } + + pmon_input = output; + VREG(v_regs,outx) = (uint8_t) (output >> 8); + } + + // Soft reset or end of sample + if ( REG(flg) & 0x80 || (brr_header & 3) == 1 ) + { + v->env_mode = env_release; + env = 0; + } + + if ( m.every_other_sample ) + { + // KOFF + if ( m.t_koff & vbit ) + v->env_mode = env_release; + + // KON + if ( m.kon & vbit ) + { + v->kon_delay = 5; + v->env_mode = env_attack; + REG(endx) &= ~vbit; + } + } + + // Envelope + if ( !v->kon_delay ) + { + if ( v->env_mode == env_release ) // 97% + { + env -= 0x8; + v->env = env; + if ( env <= 0 ) + { + v->env = 0; + goto skip_brr; // no BRR decoding for you! + } + } + else // 3% + { + int rate; + int const adsr0 = VREG(v_regs,adsr0); + int env_data = VREG(v_regs,adsr1); + if ( adsr0 >= 0x80 ) // 97% ADSR + { + if ( v->env_mode > env_decay ) // 89% + { + env--; + env -= env >> 8; + rate = env_data & 0x1F; + + // optimized handling + v->hidden_env = env; + if ( READ_COUNTER( rate ) ) + goto exit_env; + v->env = env; + goto exit_env; + } + else if ( v->env_mode == env_decay ) + { + env--; + env -= env >> 8; + rate = (adsr0 >> 3 & 0x0E) + 0x10; + } + else // env_attack + { + rate = (adsr0 & 0x0F) * 2 + 1; + env += rate < 31 ? 0x20 : 0x400; + } + } + else // GAIN + { + int mode; + env_data = VREG(v_regs,gain); + mode = env_data >> 5; + if ( mode < 4 ) // direct + { + env = env_data * 0x10; + rate = 31; + } + else + { + rate = env_data & 0x1F; + if ( mode == 4 ) // 4: linear decrease + { + env -= 0x20; + } + else if ( mode < 6 ) // 5: exponential decrease + { + env--; + env -= env >> 8; + } + else // 6,7: linear increase + { + env += 0x20; + if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) + env += 0x8 - 0x20; // 7: two-slope linear increase + } + } + } + + // Sustain level + if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) + v->env_mode = env_sustain; + + v->hidden_env = env; + + // unsigned cast because linear decrease going negative also triggers this + if ( (unsigned) env > 0x7FF ) + { + env = (env < 0 ? 0 : 0x7FF); + if ( v->env_mode == env_attack ) + v->env_mode = env_decay; + } + + if ( !READ_COUNTER( rate ) ) + v->env = env; // nothing else is controlled by the counter + } + } + exit_env: + + { + // Apply pitch + int old_pos = v->interp_pos; + int interp_pos = (old_pos & 0x3FFF) + pitch; + if ( interp_pos > 0x7FFF ) + interp_pos = 0x7FFF; + v->interp_pos = interp_pos; + + // BRR decode if necessary + if ( old_pos >= 0x4000 ) + { + // Arrange the four input nybbles in 0xABCD order for easy decoding + int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 + + ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; + + // Advance read position + int const brr_block_size = 9; + int brr_offset = v->brr_offset; + if ( (brr_offset += 2) >= brr_block_size ) + { + // Next BRR block + int brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; + assert( brr_offset == brr_block_size ); + if ( brr_header & 1 ) + { + brr_addr = SAMPLE_PTR( 1 ); + if ( !v->kon_delay ) + REG(endx) |= vbit; + } + v->brr_addr = brr_addr; + brr_offset = 1; + } + v->brr_offset = brr_offset; + + // Decode + + // 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11 + static unsigned char const shifts [16 * 2] = { + 13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11 + }; + int const scale = brr_header >> 4; + int const right_shift = shifts [scale]; + int const left_shift = shifts [scale + 16]; + + // Write to next four samples in circular buffer + int* pos = v->buf_pos; + int* end; + + // Decode four samples + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) + { + // Extract upper nybble and scale appropriately + int s = ((int16_t) nybbles >> right_shift) << left_shift; + + // Apply IIR filter (8 is the most commonly used) + int const filter = brr_header & 0x0C; + int const p1 = pos [brr_buf_size - 1]; + int const p2 = pos [brr_buf_size - 2] >> 1; + if ( filter >= 8 ) + { + s += p1; + s -= p2; + if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 + { + s += p2 >> 4; + s += (p1 * -3) >> 6; + } + else // s += p1 * 0.8984375 - p2 * 0.40625 + { + s += (p1 * -13) >> 7; + s += (p2 * 3) >> 4; + } + } + else if ( filter ) // s += p1 * 0.46875 + { + s += p1 >> 1; + s += (-p1) >> 5; + } + + // Adjust and write sample + CLAMP16( s ); + s = (int16_t) (s * 2); + pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around + } + + if ( pos >= &v->buf [brr_buf_size] ) + pos = v->buf; + v->buf_pos = pos; + } + } +skip_brr: + // Next voice + vbit <<= 1; + v_regs += 0x10; + v++; + } + while ( vbit < 0x100 ); + + // Echo position + int echo_offset = m.echo_offset; + uint8_t* const echo_ptr = &ram [(REG(esa) * 0x100 + echo_offset) & 0xFFFF]; + if ( !echo_offset ) + m.echo_length = (REG(edl) & 0x0F) * 0x800; + echo_offset += 4; + if ( echo_offset >= m.echo_length ) + echo_offset = 0; + m.echo_offset = echo_offset; + + // FIR + int echo_in_l = GET_LE16SA( echo_ptr + 0 ); + int echo_in_r = GET_LE16SA( echo_ptr + 2 ); + + int (*echo_hist_pos) [2] = m.echo_hist_pos; + if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] ) + echo_hist_pos = m.echo_hist; + m.echo_hist_pos = echo_hist_pos; + + echo_hist_pos [0] [0] = echo_hist_pos [8] [0] = echo_in_l; + echo_hist_pos [0] [1] = echo_hist_pos [8] [1] = echo_in_r; + + #define CALC_FIR_( i, in ) ((in) * (int8_t) REG(fir + i * 0x10)) + echo_in_l = CALC_FIR_( 7, echo_in_l ); + echo_in_r = CALC_FIR_( 7, echo_in_r ); + + #define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] ) + #define DO_FIR( i )\ + echo_in_l += CALC_FIR( i, 0 );\ + echo_in_r += CALC_FIR( i, 1 ); + DO_FIR( 0 ); + DO_FIR( 1 ); + DO_FIR( 2 ); + #if defined (__MWERKS__) && __MWERKS__ < 0x3200 + __eieio(); // keeps compiler from stupidly "caching" things in memory + #endif + DO_FIR( 3 ); + DO_FIR( 4 ); + DO_FIR( 5 ); + DO_FIR( 6 ); + + // Echo out + if ( !(REG(flg) & 0x20) ) + { + int l = (echo_out_l >> 7) + ((echo_in_l * (int8_t) REG(efb)) >> 14); + int r = (echo_out_r >> 7) + ((echo_in_r * (int8_t) REG(efb)) >> 14); + + // just to help pass more validation tests + #if SPC_MORE_ACCURACY + l &= ~1; + r &= ~1; + #endif + + CLAMP16( l ); + CLAMP16( r ); + + SET_LE16A( echo_ptr + 0, l ); + SET_LE16A( echo_ptr + 2, r ); + } + + // Sound out + int l = (main_out_l * mvoll + echo_in_l * (int8_t) REG(evoll)) >> 14; + int r = (main_out_r * mvolr + echo_in_r * (int8_t) REG(evolr)) >> 14; + + CLAMP16( l ); + CLAMP16( r ); + + if ( (REG(flg) & 0x40) ) + { + l = 0; + r = 0; + } + + sample_t* out = m.out; + WRITE_SAMPLES( l, r, out ); + m.out = out; + } + while ( --count ); +} + + +//// Setup + +void SPC_DSP::mute_voices( int mask ) +{ + m.mute_mask = mask; + for ( int i = 0; i < voice_count; i++ ) + { + m.voices [i].enabled = (mask >> i & 1) - 1; + update_voice_vol( i * 0x10 ); + } +} + +void SPC_DSP::init( void* ram_64k ) +{ + m.ram = (uint8_t*) ram_64k; + mute_voices( 0 ); + disable_surround( false ); + set_output( 0, 0 ); + reset(); + + #ifndef NDEBUG + // be sure this sign-extends + assert( (int16_t) 0x8000 == -0x8000 ); + + // be sure right shift preserves sign + assert( (-1 >> 1) == -1 ); + + // check clamp macro + int i; + i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); + i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); + + blargg_verify_byte_order(); + #endif +} + +void SPC_DSP::soft_reset_common() +{ + require( m.ram ); // init() must have been called already + + m.noise = 0x4000; + m.echo_hist_pos = m.echo_hist; + m.every_other_sample = 1; + m.echo_offset = 0; + m.phase = 0; + + init_counter(); +} + +void SPC_DSP::soft_reset() +{ + REG(flg) = 0xE0; + soft_reset_common(); +} + +void SPC_DSP::load( uint8_t const regs [register_count] ) +{ + memcpy( m.regs, regs, sizeof m.regs ); + memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); + + // Internal state + int i; + for ( i = voice_count; --i >= 0; ) + { + voice_t& v = m.voices [i]; + v.brr_offset = 1; + v.buf_pos = v.buf; + } + m.new_kon = REG(kon); + + mute_voices( m.mute_mask ); + soft_reset_common(); +} + +void SPC_DSP::reset() { load( initial_regs ); } diff --git a/snes_spc/fast_dsp/SPC_DSP.h b/snes_spc/fast_dsp/SPC_DSP.h new file mode 100644 index 00000000..045ce3c2 --- /dev/null +++ b/snes_spc/fast_dsp/SPC_DSP.h @@ -0,0 +1,212 @@ +// Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one) + +// snes_spc 0.9.0 +#ifndef SPC_DSP_H +#define SPC_DSP_H + +#include "blargg_common.h" + +struct SPC_DSP { +public: + typedef BOOST::uint8_t uint8_t; + +// Setup + + // Initializes DSP and has it use the 64K RAM provided + void init( void* ram_64k ); + + // Sets destination for output samples. If out is NULL or out_size is 0, + // doesn't generate any. + typedef short sample_t; + void set_output( sample_t* out, int out_size ); + + // Number of samples written to output since it was last set, always + // a multiple of 2. Undefined if more samples were generated than + // output buffer could hold. + int sample_count() const; + +// Emulation + + // Resets DSP to power-on state + void reset(); + + // Emulates pressing reset switch on SNES + void soft_reset(); + + // Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp() + // to catch the DSP up to present. + int read ( int addr ) const; + void write( int addr, int data ); + + // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks + // a pair of samples is be generated. + void run( int clock_count ); + +// Sound control + + // Mutes voices corresponding to non-zero bits in mask (overrides VxVOL with 0). + // Reduces emulation accuracy. + enum { voice_count = 8 }; + void mute_voices( int mask ); + + // If true, prevents channels and global volumes from being phase-negated + void disable_surround( bool disable = true ); + +// State + + // Resets DSP and uses supplied values to initialize registers + enum { register_count = 128 }; + void load( uint8_t const regs [register_count] ); + +// DSP register addresses + + // Global registers + enum { + r_mvoll = 0x0C, r_mvolr = 0x1C, + r_evoll = 0x2C, r_evolr = 0x3C, + r_kon = 0x4C, r_koff = 0x5C, + r_flg = 0x6C, r_endx = 0x7C, + r_efb = 0x0D, r_pmon = 0x2D, + r_non = 0x3D, r_eon = 0x4D, + r_dir = 0x5D, r_esa = 0x6D, + r_edl = 0x7D, + r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F + }; + + // Voice registers + enum { + v_voll = 0x00, v_volr = 0x01, + v_pitchl = 0x02, v_pitchh = 0x03, + v_srcn = 0x04, v_adsr0 = 0x05, + v_adsr1 = 0x06, v_gain = 0x07, + v_envx = 0x08, v_outx = 0x09 + }; + +public: + enum { extra_size = 16 }; + sample_t* extra() { return m.extra; } + sample_t const* out_pos() const { return m.out; } +public: + BLARGG_DISABLE_NOTHROW + + typedef BOOST::int8_t int8_t; + typedef BOOST::int16_t int16_t; + + enum { echo_hist_size = 8 }; + + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; + enum { brr_buf_size = 12 }; + struct voice_t + { + int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) + int* buf_pos; // place in buffer where next samples will be decoded + int interp_pos; // relative fractional position in sample (0x1000 = 1.0) + int brr_addr; // address of current BRR block + int brr_offset; // current decoding offset in BRR block + int kon_delay; // KON delay/current setup phase + env_mode_t env_mode; + int env; // current envelope level + int hidden_env; // used by GAIN mode 7, very obscure quirk + int volume [2]; // copy of volume from DSP registers, with surround disabled + int enabled; // -1 if enabled, 0 if muted + }; +private: + struct state_t + { + uint8_t regs [register_count]; + + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) + int echo_hist [echo_hist_size * 2] [2]; + int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] + + int every_other_sample; // toggles every sample + int kon; // KON value when last checked + int noise; + int echo_offset; // offset from ESA in echo buffer + int echo_length; // number of bytes that echo_offset will stop at + int phase; // next clock cycle to run (0-31) + unsigned counters [4]; + + int new_kon; + int t_koff; + + voice_t voices [voice_count]; + + unsigned* counter_select [32]; + + // non-emulation state + uint8_t* ram; // 64K shared RAM between DSP and SMP + int mute_mask; + int surround_threshold; + sample_t* out; + sample_t* out_end; + sample_t* out_begin; + sample_t extra [extra_size]; + }; + state_t m; + + void init_counter(); + void run_counter( int ); + void soft_reset_common(); + void write_outline( int addr, int data ); + void update_voice_vol( int addr ); +}; + +#include + +inline int SPC_DSP::sample_count() const { return int(m.out - m.out_begin); } + +inline int SPC_DSP::read( int addr ) const +{ + assert( (unsigned) addr < register_count ); + return m.regs [addr]; +} + +inline void SPC_DSP::update_voice_vol( int addr ) +{ + int l = (int8_t) m.regs [addr + v_voll]; + int r = (int8_t) m.regs [addr + v_volr]; + + if ( l * r < m.surround_threshold ) + { + // signs differ, so negate those that are negative + l ^= l >> 7; + r ^= r >> 7; + } + + voice_t& v = m.voices [addr >> 4]; + int enabled = v.enabled; + v.volume [0] = l & enabled; + v.volume [1] = r & enabled; +} + +inline void SPC_DSP::write( int addr, int data ) +{ + assert( (unsigned) addr < register_count ); + + m.regs [addr] = (uint8_t) data; + int low = addr & 0x0F; + if ( low < 0x2 ) // voice volumes + { + update_voice_vol( low ^ addr ); + } + else if ( low == 0xC ) + { + if ( addr == r_kon ) + m.new_kon = (uint8_t) data; + + if ( addr == r_endx ) // always cleared, regardless of data written + m.regs [r_endx] = 0; + } +} + +inline void SPC_DSP::disable_surround( bool disable ) +{ + m.surround_threshold = disable ? 0 : -0x4000; +} + +#define SPC_NO_COPY_STATE_FUNCS 1 + +#define SPC_LESS_ACCURATE 1 + +#endif diff --git a/snes_spc/license.txt b/snes_spc/license.txt new file mode 100644 index 00000000..5faba9d4 --- /dev/null +++ b/snes_spc/license.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/snes_spc/readme.txt b/snes_spc/readme.txt new file mode 100644 index 00000000..979913f0 --- /dev/null +++ b/snes_spc/readme.txt @@ -0,0 +1,86 @@ +snes_spc 0.9.0: SNES SPC-700 APU Emulator +----------------------------------------- +This library includes a full SPC emulator and an S-DSP emulator that can +be used on its own. Two S-DSP emulators are available: a highly accurate +one for use in a SNES emulator, and a 3x faster one for use in an SPC +music player or a resource-limited SNES emulator. + +* Can be used from C and C++ code +* Full SPC-700 APU emulator with cycle accuracy in most cases +* Loads, plays, and saves SPC music files +* Can save and load exact full emulator state +* DSP voice muting, surround sound disable, and song tempo adjustment +* Uses 7% CPU average on 400 MHz Mac to play an SPC using fast DSP + +The accurate DSP emulator is based on past research by others and +hundreds of hours of recent research by me. It passes over a hundred +strenuous timing and behavior validation tests that were also run on the +SNES. As far as I know, it's the first DSP emulator with cycle accuracy, +properly emulating every DSP register and memory access at the exact SPC +cycle it occurs at, whereas previous DSP emulators emulated these only +to the nearest sample (which occurs every 32 clocks). + +Author : Shay Green +Website: http://www.slack.net/~ant/ +Forum : http://groups.google.com/group/blargg-sound-libs +License: GNU Lesser General Public License (LGPL) + + +Getting Started +--------------- +Build a program consisting of demo/play_spc.c, demo/demo_util.c, +demo/wave_writer.c, and all source files in snes_spc/. Put an SPC music +file in the same directory and name it "test.spc". Running the program +should generate the recording "out.wav". + +Read snes_spc.txt for more information. Post to the discussion forum for +assistance. + + +Files +----- +snes_spc.txt Documentation +changes.txt Change log +license.txt GNU LGPL license + +demo/ + play_spc.c Records SPC file to wave sound file + benchmark.c Finds how fast emulator runs on your computer + trim_spc.c Trims silence off beginning of an SPC file + save_state.c Saves/loads exact emulator state to/from file + comm.c Communicates with SPC how SNES would + demo_util.h General utility functions used by demos + demo_util.c + wave_writer.h WAVE sound file writer used for demo output + wave_writer.c + +fast_dsp/ Optional standalone fast DSP emulator + SPC_DSP.h To use with full SPC emulator, move into + SPC_DSP.cpp snes_spc/ and replace original files + +snes_spc/ Library sources + blargg_config.h Configuration (modify as necessary) + + spc.h C interface to SPC emulator and sound filter + spc.cpp + + SPC_Filter.h Optional filter to make sound more authentic + SPC_Filter.cpp + + SNES_SPC.h Full SPC emulator + SNES_SPC.cpp + SNES_SPC_misc.cpp + SNES_SPC_state.cpp + SPC_CPU.h + + dsp.h C interface to DSP emulator + dsp.cpp + + SPC_DSP.h Standalone accurate DSP emulator + SPC_DSP.cpp + blargg_common.h + blargg_endian.h + blargg_source.h + +-- +Shay Green diff --git a/snes_spc/slow_dsp/SPC_DSP.cpp b/snes_spc/slow_dsp/SPC_DSP.cpp new file mode 100644 index 00000000..dd180506 --- /dev/null +++ b/snes_spc/slow_dsp/SPC_DSP.cpp @@ -0,0 +1,1018 @@ +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "SPC_DSP.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +#if INT_MAX < 0x7FFFFFFF + #error "Requires that int type have at least 32 bits" +#endif + +// TODO: add to blargg_endian.h +#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) +#define GET_LE16A( addr ) GET_LE16( addr ) +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) + +static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] = +{ + 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, + 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, + 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, + 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, + 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, + 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, + 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, + 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF +}; + +// if ( io < -32768 ) io = -32768; +// if ( io > 32767 ) io = 32767; +#define CLAMP16( io )\ +{\ + if ( (int16_t) io != io )\ + io = (io >> 31) ^ 0x7FFF;\ +} + +// Access global DSP register +#define REG(n) m.regs [r_##n] + +// Access voice DSP register +#define VREG(r,n) r [v_##n] + +#define WRITE_SAMPLES( l, r, out ) \ +{\ + out [0] = l;\ + out [1] = r;\ + out += 2;\ + if ( out >= m.out_end )\ + {\ + check( out == m.out_end );\ + check( m.out_end != &m.extra [extra_size] || \ + (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ + out = m.extra;\ + m.out_end = &m.extra [extra_size];\ + }\ +}\ + +void SPC_DSP::set_output( sample_t* out, int size ) +{ + require( (size & 1) == 0 ); // must be even + if ( !out ) + { + out = m.extra; + size = extra_size; + } + m.out_begin = out; + m.out = out; + m.out_end = out + size; +} + +// Volume registers and efb are signed! Easy to forget int8_t cast. +// Prefixes are to avoid accidental use of locals with same names. + +// Gaussian interpolation + +static short const gauss [512] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, + 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, + 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, + 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, + 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, + 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, + 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, + 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, + 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, + 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, + 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, + 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, + 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, + 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, + 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, + 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, + 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, + 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, + 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036, +1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102, +1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160, +1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210, +1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251, +1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280, +1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298, +1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, +}; + +inline int SPC_DSP::interpolate( voice_t const* v ) +{ + // Make pointers into gaussian based on fractional position between samples + int offset = v->interp_pos >> 4 & 0xFF; + short const* fwd = gauss + 255 - offset; + short const* rev = gauss + offset; // mirror left half of gaussian + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = (fwd [ 0] * in [0]) >> 11; + out += (fwd [256] * in [1]) >> 11; + out += (rev [256] * in [2]) >> 11; + out = (int16_t) out; + out += (rev [ 0] * in [3]) >> 11; + + CLAMP16( out ); + out &= ~1; + return out; +} + + +//// Counters + +int const simple_counter_range = 2048 * 5 * 3; // 30720 + +static unsigned const counter_rates [32] = +{ + simple_counter_range + 1, // never fires + 2048, 1536, + 1280, 1024, 768, + 640, 512, 384, + 320, 256, 192, + 160, 128, 96, + 80, 64, 48, + 40, 32, 24, + 20, 16, 12, + 10, 8, 6, + 5, 4, 3, + 2, + 1 +}; + +static unsigned const counter_offsets [32] = +{ + 1, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 0, + 0 +}; + +inline void SPC_DSP::init_counter() +{ + m.counter = 0; +} + +inline void SPC_DSP::run_counters() +{ + if ( --m.counter < 0 ) + m.counter = simple_counter_range - 1; +} + +inline unsigned SPC_DSP::read_counter( int rate ) +{ + return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; +} + + +//// Envelope + +inline void SPC_DSP::run_envelope( voice_t* const v ) +{ + int env = v->env; + if ( v->env_mode == env_release ) // 60% + { + if ( (env -= 0x8) < 0 ) + env = 0; + v->env = env; + } + else + { + int rate; + int env_data = VREG(v->regs,adsr1); + if ( m.t_adsr0 & 0x80 ) // 99% ADSR + { + if ( v->env_mode >= env_decay ) // 99% + { + env--; + env -= env >> 8; + rate = env_data & 0x1F; + if ( v->env_mode == env_decay ) // 1% + rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10; + } + else // env_attack + { + rate = (m.t_adsr0 & 0x0F) * 2 + 1; + env += rate < 31 ? 0x20 : 0x400; + } + } + else // GAIN + { + int mode; + env_data = VREG(v->regs,gain); + mode = env_data >> 5; + if ( mode < 4 ) // direct + { + env = env_data * 0x10; + rate = 31; + } + else + { + rate = env_data & 0x1F; + if ( mode == 4 ) // 4: linear decrease + { + env -= 0x20; + } + else if ( mode < 6 ) // 5: exponential decrease + { + env--; + env -= env >> 8; + } + else // 6,7: linear increase + { + env += 0x20; + if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) + env += 0x8 - 0x20; // 7: two-slope linear increase + } + } + } + + // Sustain level + if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) + v->env_mode = env_sustain; + + v->hidden_env = env; + + // unsigned cast because linear decrease going negative also triggers this + if ( (unsigned) env > 0x7FF ) + { + env = (env < 0 ? 0 : 0x7FF); + if ( v->env_mode == env_attack ) + v->env_mode = env_decay; + } + + if ( !read_counter( rate ) ) + v->env = env; // nothing else is controlled by the counter + } +} + + +//// BRR Decoding + +inline void SPC_DSP::decode_brr( voice_t* v ) +{ + // Arrange the four input nybbles in 0xABCD order for easy decoding + int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; + + int const header = m.t_brr_header; + + // Write to next four samples in circular buffer + int* pos = &v->buf [v->buf_pos]; + int* end; + if ( (v->buf_pos += 4) >= brr_buf_size ) + v->buf_pos = 0; + + // Decode four samples + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) + { + // Extract nybble and sign-extend + int s = (int16_t) nybbles >> 12; + + // Shift sample based on header + int const shift = header >> 4; + s = (s << shift) >> 1; + if ( shift >= 0xD ) // handle invalid range + s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0) + + // Apply IIR filter (8 is the most commonly used) + int const filter = header & 0x0C; + int const p1 = pos [brr_buf_size - 1]; + int const p2 = pos [brr_buf_size - 2] >> 1; + if ( filter >= 8 ) + { + s += p1; + s -= p2; + if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 + { + s += p2 >> 4; + s += (p1 * -3) >> 6; + } + else // s += p1 * 0.8984375 - p2 * 0.40625 + { + s += (p1 * -13) >> 7; + s += (p2 * 3) >> 4; + } + } + else if ( filter ) // s += p1 * 0.46875 + { + s += p1 >> 1; + s += (-p1) >> 5; + } + + // Adjust and write sample + CLAMP16( s ); + s = (int16_t) (s * 2); + pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around + } +} + + +//// Misc + +#define MISC_CLOCK( n ) inline void SPC_DSP::misc_##n() + +MISC_CLOCK( 27 ) +{ + m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON +} +MISC_CLOCK( 28 ) +{ + m.t_non = REG(non); + m.t_eon = REG(eon); + m.t_dir = REG(dir); +} +MISC_CLOCK( 29 ) +{ + if ( (m.every_other_sample ^= 1) != 0 ) + m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read +} +MISC_CLOCK( 30 ) +{ + if ( m.every_other_sample ) + { + m.kon = m.new_kon; + m.t_koff = REG(koff) | m.mute_mask; + } + + run_counters(); + + // Noise + if ( !read_counter( REG(flg) & 0x1F ) ) + { + int feedback = (m.noise << 13) ^ (m.noise << 14); + m.noise = (feedback & 0x4000) ^ (m.noise >> 1); + } +} + + +//// Voices + +#define VOICE_CLOCK( n ) void SPC_DSP::voice_##n( voice_t* const v ) + +inline VOICE_CLOCK( V1 ) +{ + m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4; + m.t_srcn = VREG(v->regs,srcn); +} +inline VOICE_CLOCK( V2 ) +{ + // Read sample pointer (ignored if not needed) + uint8_t const* entry = &m.ram [m.t_dir_addr]; + if ( !v->kon_delay ) + entry += 2; + m.t_brr_next_addr = GET_LE16A( entry ); + + m.t_adsr0 = VREG(v->regs,adsr0); + + // Read pitch, spread over two clocks + m.t_pitch = VREG(v->regs,pitchl); +} +inline VOICE_CLOCK( V3a ) +{ + m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8; +} +inline VOICE_CLOCK( V3b ) +{ + // Read BRR header and byte + m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF]; + m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking +} +VOICE_CLOCK( V3c ) +{ + // Pitch modulation using previous voice's output + if ( m.t_pmon & v->vbit ) + m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10; + + if ( v->kon_delay ) + { + // Get ready to start BRR decoding on next sample + if ( v->kon_delay == 5 ) + { + v->brr_addr = m.t_brr_next_addr; + v->brr_offset = 1; + v->buf_pos = 0; + m.t_brr_header = 0; // header is ignored on this sample + m.kon_check = true; + } + + // Envelope is never run during KON + v->env = 0; + v->hidden_env = 0; + + // Disable BRR decoding until last three samples + v->interp_pos = 0; + if ( --v->kon_delay & 3 ) + v->interp_pos = 0x4000; + + // Pitch is never added during KON + m.t_pitch = 0; + } + + // Gaussian interpolation + { + int output = interpolate( v ); + + // Noise + if ( m.t_non & v->vbit ) + output = (int16_t) (m.noise * 2); + + // Apply envelope + m.t_output = (output * v->env) >> 11 & ~1; + v->t_envx_out = (uint8_t) (v->env >> 4); + } + + // Immediate silence due to end of sample or soft reset + if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) + { + v->env_mode = env_release; + v->env = 0; + } + + if ( m.every_other_sample ) + { + // KOFF + if ( m.t_koff & v->vbit ) + v->env_mode = env_release; + + // KON + if ( m.kon & v->vbit ) + { + v->kon_delay = 5; + v->env_mode = env_attack; + } + } + + // Run envelope for next sample + if ( !v->kon_delay ) + run_envelope( v ); +} +inline void SPC_DSP::voice_output( voice_t const* v, int ch ) +{ + // Apply left/right volume + int amp = (m.t_output * (int8_t) VREG(v->regs,voll + ch)) >> 7; + + // Add to output total + m.t_main_out [ch] += amp; + CLAMP16( m.t_main_out [ch] ); + + // Optionally add to echo total + if ( m.t_eon & v->vbit ) + { + m.t_echo_out [ch] += amp; + CLAMP16( m.t_echo_out [ch] ); + } +} +VOICE_CLOCK( V4 ) +{ + // Decode BRR + m.t_looped = 0; + if ( v->interp_pos >= 0x4000 ) + { + decode_brr( v ); + + if ( (v->brr_offset += 2) >= brr_block_size ) + { + // Start decoding next BRR block + assert( v->brr_offset == brr_block_size ); + v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; + if ( m.t_brr_header & 1 ) + { + v->brr_addr = m.t_brr_next_addr; + m.t_looped = v->vbit; + } + v->brr_offset = 1; + } + } + + // Apply pitch + v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; + + // Keep from getting too far ahead (when using pitch modulation) + if ( v->interp_pos > 0x7FFF ) + v->interp_pos = 0x7FFF; + + // Output left + voice_output( v, 0 ); +} +inline VOICE_CLOCK( V5 ) +{ + // Output right + voice_output( v, 1 ); + + // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier + int endx_buf = REG(endx) | m.t_looped; + + // Clear bit in ENDX if KON just began + if ( v->kon_delay == 5 ) + endx_buf &= ~v->vbit; + m.endx_buf = (uint8_t) endx_buf; +} +inline VOICE_CLOCK( V6 ) +{ + (void) v; // avoid compiler warning about unused v + m.outx_buf = (uint8_t) (m.t_output >> 8); +} +inline VOICE_CLOCK( V7 ) +{ + // Update ENDX + REG(endx) = m.endx_buf; + + m.envx_buf = v->t_envx_out; +} +inline VOICE_CLOCK( V8 ) +{ + // Update OUTX + VREG(v->regs,outx) = m.outx_buf; +} +inline VOICE_CLOCK( V9 ) +{ + // Update ENVX + VREG(v->regs,envx) = m.envx_buf; +} + +// Most voices do all these in one clock, so make a handy composite +inline VOICE_CLOCK( V3 ) +{ + voice_V3a( v ); + voice_V3b( v ); + voice_V3c( v ); +} + +// Common combinations of voice steps on different voices. This greatly reduces +// code size and allows everything to be inlined in these functions. +VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); } +VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); } +VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); } + + +//// Echo + +// Current echo buffer pointer for left/right channel +#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2]) + +// Sample in echo history buffer, where 0 is the oldest +#define ECHO_FIR( i ) (m.echo_hist_pos [i]) + +// Calculate FIR point for left/right channel +#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6) + +#define ECHO_CLOCK( n ) inline void SPC_DSP::echo_##n() + +inline void SPC_DSP::echo_read( int ch ) +{ + int s = GET_LE16SA( ECHO_PTR( ch ) ); + // second copy simplifies wrap-around handling + ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1; +} + +ECHO_CLOCK( 22 ) +{ + // History + if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] ) + m.echo_hist_pos = m.echo_hist; + + m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF; + echo_read( 0 ); + + // FIR (using l and r temporaries below helps compiler optimize) + int l = CALC_FIR( 0, 0 ); + int r = CALC_FIR( 0, 1 ); + + m.t_echo_in [0] = l; + m.t_echo_in [1] = r; +} +ECHO_CLOCK( 23 ) +{ + int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); + int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); + + m.t_echo_in [0] += l; + m.t_echo_in [1] += r; + + echo_read( 1 ); +} +ECHO_CLOCK( 24 ) +{ + int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 ); + int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 ); + + m.t_echo_in [0] += l; + m.t_echo_in [1] += r; +} +ECHO_CLOCK( 25 ) +{ + int l = m.t_echo_in [0] + CALC_FIR( 6, 0 ); + int r = m.t_echo_in [1] + CALC_FIR( 6, 1 ); + + l = (int16_t) l; + r = (int16_t) r; + + l += (int16_t) CALC_FIR( 7, 0 ); + r += (int16_t) CALC_FIR( 7, 1 ); + + CLAMP16( l ); + CLAMP16( r ); + + m.t_echo_in [0] = l & ~1; + m.t_echo_in [1] = r & ~1; +} +inline int SPC_DSP::echo_output( int ch ) +{ + int out = (int16_t) ((m.t_main_out [ch] * (int8_t) REG(mvoll + ch * 0x10)) >> 7) + + (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7); + CLAMP16( out ); + return out; +} +ECHO_CLOCK( 26 ) +{ + // Left output volumes + // (save sample for next clock so we can output both together) + m.t_main_out [0] = echo_output( 0 ); + + // Echo feedback + int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7); + int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7); + + CLAMP16( l ); + CLAMP16( r ); + + m.t_echo_out [0] = l & ~1; + m.t_echo_out [1] = r & ~1; +} +ECHO_CLOCK( 27 ) +{ + // Output + int l = m.t_main_out [0]; + int r = echo_output( 1 ); + m.t_main_out [0] = 0; + m.t_main_out [1] = 0; + + // TODO: global muting isn't this simple (turns DAC on and off + // or something, causing small ~37-sample pulse when first muted) + if ( REG(flg) & 0x40 ) + { + l = 0; + r = 0; + } + + // Output sample to DAC + #ifdef SPC_DSP_OUT_HOOK + SPC_DSP_OUT_HOOK( l, r ); + #else + sample_t* out = m.out; + WRITE_SAMPLES( l, r, out ); + m.out = out; + #endif +} +ECHO_CLOCK( 28 ) +{ + m.t_echo_enabled = REG(flg); +} +inline void SPC_DSP::echo_write( int ch ) +{ + if ( !(m.t_echo_enabled & 0x20) ) + SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] ); + m.t_echo_out [ch] = 0; +} +ECHO_CLOCK( 29 ) +{ + m.t_esa = REG(esa); + + if ( !m.echo_offset ) + m.echo_length = (REG(edl) & 0x0F) * 0x800; + + m.echo_offset += 4; + if ( m.echo_offset >= m.echo_length ) + m.echo_offset = 0; + + // Write left echo + echo_write( 0 ); + + m.t_echo_enabled = REG(flg); +} +ECHO_CLOCK( 30 ) +{ + // Write right echo + echo_write( 1 ); +} + + +//// Timing + +// Execute clock for a particular voice +#define V( clock, voice ) voice_##clock( &m.voices [voice] ); + +/* The most common sequence of clocks uses composite operations +for efficiency. For example, the following are equivalent to the +individual steps on the right: + +V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5) +V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4) +V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */ + +// Voice 0 1 2 3 4 5 6 7 +#define GEN_DSP_TIMING \ +PHASE( 0) V(V5,0)V(V2,1)\ +PHASE( 1) V(V6,0)V(V3,1)\ +PHASE( 2) V(V7_V4_V1,0)\ +PHASE( 3) V(V8_V5_V2,0)\ +PHASE( 4) V(V9_V6_V3,0)\ +PHASE( 5) V(V7_V4_V1,1)\ +PHASE( 6) V(V8_V5_V2,1)\ +PHASE( 7) V(V9_V6_V3,1)\ +PHASE( 8) V(V7_V4_V1,2)\ +PHASE( 9) V(V8_V5_V2,2)\ +PHASE(10) V(V9_V6_V3,2)\ +PHASE(11) V(V7_V4_V1,3)\ +PHASE(12) V(V8_V5_V2,3)\ +PHASE(13) V(V9_V6_V3,3)\ +PHASE(14) V(V7_V4_V1,4)\ +PHASE(15) V(V8_V5_V2,4)\ +PHASE(16) V(V9_V6_V3,4)\ +PHASE(17) V(V1,0) V(V7,5)V(V4,6)\ +PHASE(18) V(V8_V5_V2,5)\ +PHASE(19) V(V9_V6_V3,5)\ +PHASE(20) V(V1,1) V(V7,6)V(V4,7)\ +PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\ +PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\ +PHASE(23) V(V7,7) echo_23();\ +PHASE(24) V(V8,7) echo_24();\ +PHASE(25) V(V3b,0) V(V9,7) echo_25();\ +PHASE(26) echo_26();\ +PHASE(27) misc_27(); echo_27();\ +PHASE(28) misc_28(); echo_28();\ +PHASE(29) misc_29(); echo_29();\ +PHASE(30) misc_30();V(V3c,0) echo_30();\ +PHASE(31) V(V4,0) V(V1,2)\ + +#if !SPC_DSP_CUSTOM_RUN + +void SPC_DSP::run( int clocks_remain ) +{ + require( clocks_remain > 0 ); + + int const phase = m.phase; + m.phase = (phase + clocks_remain) & 31; + switch ( phase ) + { + loop: + + #define PHASE( n ) if ( n && !--clocks_remain ) break; case n: + GEN_DSP_TIMING + #undef PHASE + + if ( --clocks_remain ) + goto loop; + } +} + +#endif + + +//// Setup + +void SPC_DSP::init( void* ram_64k ) +{ + m.ram = (uint8_t*) ram_64k; + mute_voices( 0 ); + disable_surround( false ); + set_output( 0, 0 ); + reset(); + + #ifndef NDEBUG + // be sure this sign-extends + assert( (int16_t) 0x8000 == -0x8000 ); + + // be sure right shift preserves sign + assert( (-1 >> 1) == -1 ); + + // check clamp macro + int i; + i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); + i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); + + blargg_verify_byte_order(); + #endif +} + +void SPC_DSP::soft_reset_common() +{ + require( m.ram ); // init() must have been called already + + m.noise = 0x4000; + m.echo_hist_pos = m.echo_hist; + m.every_other_sample = 1; + m.echo_offset = 0; + m.phase = 0; + + init_counter(); +} + +void SPC_DSP::soft_reset() +{ + REG(flg) = 0xE0; + soft_reset_common(); +} + +void SPC_DSP::load( uint8_t const regs [register_count] ) +{ + memcpy( m.regs, regs, sizeof m.regs ); + memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); + + // Internal state + for ( int i = voice_count; --i >= 0; ) + { + voice_t* v = &m.voices [i]; + v->brr_offset = 1; + v->vbit = 1 << i; + v->regs = &m.regs [i * 0x10]; + } + m.new_kon = REG(kon); + m.t_dir = REG(dir); + m.t_esa = REG(esa); + + soft_reset_common(); +} + +void SPC_DSP::reset() { load( initial_regs ); } + + +//// State save/load + +#if !SPC_NO_COPY_STATE_FUNCS + +void SPC_State_Copier::copy( void* state, size_t size ) +{ + func( buf, state, size ); +} + +int SPC_State_Copier::copy_int( int state, int size ) +{ + BOOST::uint8_t s [2]; + SET_LE16( s, state ); + func( buf, &s, size ); + return GET_LE16( s ); +} + +void SPC_State_Copier::skip( int count ) +{ + if ( count > 0 ) + { + char temp [64]; + memset( temp, 0, sizeof temp ); + do + { + int n = sizeof temp; + if ( n > count ) + n = count; + count -= n; + func( buf, temp, n ); + } + while ( count ); + } +} + +void SPC_State_Copier::extra() +{ + int n = 0; + SPC_State_Copier& copier = *this; + SPC_COPY( uint8_t, n ); + skip( n ); +} + +void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy ) +{ + SPC_State_Copier copier( io, copy ); + + // DSP registers + copier.copy( m.regs, register_count ); + + // Internal state + + // Voices + int i; + for ( i = 0; i < voice_count; i++ ) + { + voice_t* v = &m.voices [i]; + + // BRR buffer + int i; + for ( i = 0; i < brr_buf_size; i++ ) + { + int s = v->buf [i]; + SPC_COPY( int16_t, s ); + v->buf [i] = v->buf [i + brr_buf_size] = s; + } + + SPC_COPY( uint16_t, v->interp_pos ); + SPC_COPY( uint16_t, v->brr_addr ); + SPC_COPY( uint16_t, v->env ); + SPC_COPY( int16_t, v->hidden_env ); + SPC_COPY( uint8_t, v->buf_pos ); + SPC_COPY( uint8_t, v->brr_offset ); + SPC_COPY( uint8_t, v->kon_delay ); + { + int m = v->env_mode; + SPC_COPY( uint8_t, m ); + v->env_mode = (enum env_mode_t) m; + } + SPC_COPY( uint8_t, v->t_envx_out ); + + copier.extra(); + } + + // Echo history + for ( i = 0; i < echo_hist_size; i++ ) + { + int j; + for ( j = 0; j < 2; j++ ) + { + int s = m.echo_hist_pos [i] [j]; + SPC_COPY( int16_t, s ); + m.echo_hist [i] [j] = s; // write back at offset 0 + } + } + m.echo_hist_pos = m.echo_hist; + memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] ); + + // Misc + SPC_COPY( uint8_t, m.every_other_sample ); + SPC_COPY( uint8_t, m.kon ); + + SPC_COPY( uint16_t, m.noise ); + SPC_COPY( uint16_t, m.counter ); + SPC_COPY( uint16_t, m.echo_offset ); + SPC_COPY( uint16_t, m.echo_length ); + SPC_COPY( uint8_t, m.phase ); + + SPC_COPY( uint8_t, m.new_kon ); + SPC_COPY( uint8_t, m.endx_buf ); + SPC_COPY( uint8_t, m.envx_buf ); + SPC_COPY( uint8_t, m.outx_buf ); + + SPC_COPY( uint8_t, m.t_pmon ); + SPC_COPY( uint8_t, m.t_non ); + SPC_COPY( uint8_t, m.t_eon ); + SPC_COPY( uint8_t, m.t_dir ); + SPC_COPY( uint8_t, m.t_koff ); + + SPC_COPY( uint16_t, m.t_brr_next_addr ); + SPC_COPY( uint8_t, m.t_adsr0 ); + SPC_COPY( uint8_t, m.t_brr_header ); + SPC_COPY( uint8_t, m.t_brr_byte ); + SPC_COPY( uint8_t, m.t_srcn ); + SPC_COPY( uint8_t, m.t_esa ); + SPC_COPY( uint8_t, m.t_echo_enabled ); + + SPC_COPY( int16_t, m.t_main_out [0] ); + SPC_COPY( int16_t, m.t_main_out [1] ); + SPC_COPY( int16_t, m.t_echo_out [0] ); + SPC_COPY( int16_t, m.t_echo_out [1] ); + SPC_COPY( int16_t, m.t_echo_in [0] ); + SPC_COPY( int16_t, m.t_echo_in [1] ); + + SPC_COPY( uint16_t, m.t_dir_addr ); + SPC_COPY( uint16_t, m.t_pitch ); + SPC_COPY( int16_t, m.t_output ); + SPC_COPY( uint16_t, m.t_echo_ptr ); + SPC_COPY( uint8_t, m.t_looped ); + + copier.extra(); +} +#endif diff --git a/snes_spc/slow_dsp/SPC_DSP.h b/snes_spc/slow_dsp/SPC_DSP.h new file mode 100644 index 00000000..0428d5fc --- /dev/null +++ b/snes_spc/slow_dsp/SPC_DSP.h @@ -0,0 +1,304 @@ +// Highly accurate SNES SPC-700 DSP emulator + +// snes_spc 0.9.0 +#ifndef SPC_DSP_H +#define SPC_DSP_H + +#include "blargg_common.h" + +extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); } + +struct SPC_DSP { +public: + typedef BOOST::uint8_t uint8_t; + +// Setup + + // Initializes DSP and has it use the 64K RAM provided + void init( void* ram_64k ); + + // Sets destination for output samples. If out is NULL or out_size is 0, + // doesn't generate any. + typedef short sample_t; + void set_output( sample_t* out, int out_size ); + + // Number of samples written to output since it was last set, always + // a multiple of 2. Undefined if more samples were generated than + // output buffer could hold. + int sample_count() const; + +// Emulation + + // Resets DSP to power-on state + void reset(); + + // Emulates pressing reset switch on SNES + void soft_reset(); + + // Reads/writes DSP registers. For accuracy, you must first call run() + // to catch the DSP up to present. + int read ( int addr ) const; + void write( int addr, int data ); + + // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks + // a pair of samples is be generated. + void run( int clock_count ); + +// Sound control + + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). + // Reduces emulation accuracy. + enum { voice_count = 8 }; + void mute_voices( int mask ); + +// State + + // Resets DSP and uses supplied values to initialize registers + enum { register_count = 128 }; + void load( uint8_t const regs [register_count] ); + + // Saves/loads exact emulator state + enum { state_size = 640 }; // maximum space needed when saving + typedef dsp_copy_func_t copy_func_t; + void copy_state( unsigned char** io, copy_func_t ); + + // Returns non-zero if new key-on events occurred since last call + bool check_kon(); + +// DSP register addresses + + // Global registers + enum { + r_mvoll = 0x0C, r_mvolr = 0x1C, + r_evoll = 0x2C, r_evolr = 0x3C, + r_kon = 0x4C, r_koff = 0x5C, + r_flg = 0x6C, r_endx = 0x7C, + r_efb = 0x0D, r_pmon = 0x2D, + r_non = 0x3D, r_eon = 0x4D, + r_dir = 0x5D, r_esa = 0x6D, + r_edl = 0x7D, + r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F + }; + + // Voice registers + enum { + v_voll = 0x00, v_volr = 0x01, + v_pitchl = 0x02, v_pitchh = 0x03, + v_srcn = 0x04, v_adsr0 = 0x05, + v_adsr1 = 0x06, v_gain = 0x07, + v_envx = 0x08, v_outx = 0x09 + }; + +public: + enum { extra_size = 16 }; + sample_t* extra() { return m.extra; } + sample_t const* out_pos() const { return m.out; } + void disable_surround( bool ) { } // not supported +public: + BLARGG_DISABLE_NOTHROW + + typedef BOOST::int8_t int8_t; + typedef BOOST::int16_t int16_t; + + enum { echo_hist_size = 8 }; + + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; + enum { brr_buf_size = 12 }; + struct voice_t + { + int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) + int buf_pos; // place in buffer where next samples will be decoded + int interp_pos; // relative fractional position in sample (0x1000 = 1.0) + int brr_addr; // address of current BRR block + int brr_offset; // current decoding offset in BRR block + uint8_t* regs; // pointer to voice's DSP registers + int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc. + int kon_delay; // KON delay/current setup phase + env_mode_t env_mode; + int env; // current envelope level + int hidden_env; // used by GAIN mode 7, very obscure quirk + uint8_t t_envx_out; + }; +private: + enum { brr_block_size = 9 }; + + struct state_t + { + uint8_t regs [register_count]; + + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) + int echo_hist [echo_hist_size * 2] [2]; + int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] + + int every_other_sample; // toggles every sample + int kon; // KON value when last checked + int noise; + int counter; + int echo_offset; // offset from ESA in echo buffer + int echo_length; // number of bytes that echo_offset will stop at + int phase; // next clock cycle to run (0-31) + bool kon_check; // set when a new KON occurs + + // Hidden registers also written to when main register is written to + int new_kon; + uint8_t endx_buf; + uint8_t envx_buf; + uint8_t outx_buf; + + // Temporary state between clocks + + // read once per sample + int t_pmon; + int t_non; + int t_eon; + int t_dir; + int t_koff; + + // read a few clocks ahead then used + int t_brr_next_addr; + int t_adsr0; + int t_brr_header; + int t_brr_byte; + int t_srcn; + int t_esa; + int t_echo_enabled; + + // internal state that is recalculated every sample + int t_dir_addr; + int t_pitch; + int t_output; + int t_looped; + int t_echo_ptr; + + // left/right sums + int t_main_out [2]; + int t_echo_out [2]; + int t_echo_in [2]; + + voice_t voices [voice_count]; + + // non-emulation state + uint8_t* ram; // 64K shared RAM between DSP and SMP + int mute_mask; + sample_t* out; + sample_t* out_end; + sample_t* out_begin; + sample_t extra [extra_size]; + }; + state_t m; + + void init_counter(); + void run_counters(); + unsigned read_counter( int rate ); + + int interpolate( voice_t const* v ); + void run_envelope( voice_t* const v ); + void decode_brr( voice_t* v ); + + void misc_27(); + void misc_28(); + void misc_29(); + void misc_30(); + + void voice_output( voice_t const* v, int ch ); + void voice_V1( voice_t* const ); + void voice_V2( voice_t* const ); + void voice_V3( voice_t* const ); + void voice_V3a( voice_t* const ); + void voice_V3b( voice_t* const ); + void voice_V3c( voice_t* const ); + void voice_V4( voice_t* const ); + void voice_V5( voice_t* const ); + void voice_V6( voice_t* const ); + void voice_V7( voice_t* const ); + void voice_V8( voice_t* const ); + void voice_V9( voice_t* const ); + void voice_V7_V4_V1( voice_t* const ); + void voice_V8_V5_V2( voice_t* const ); + void voice_V9_V6_V3( voice_t* const ); + + void echo_read( int ch ); + int echo_output( int ch ); + void echo_write( int ch ); + void echo_22(); + void echo_23(); + void echo_24(); + void echo_25(); + void echo_26(); + void echo_27(); + void echo_28(); + void echo_29(); + void echo_30(); + + void soft_reset_common(); +}; + +#include + +inline int SPC_DSP::sample_count() const { return int(m.out - m.out_begin); } + +inline int SPC_DSP::read( int addr ) const +{ + assert( (unsigned) addr < register_count ); + return m.regs [addr]; +} + +inline void SPC_DSP::write( int addr, int data ) +{ + assert( (unsigned) addr < register_count ); + + m.regs [addr] = (uint8_t) data; + switch ( addr & 0x0F ) + { + case v_envx: + m.envx_buf = (uint8_t) data; + break; + + case v_outx: + m.outx_buf = (uint8_t) data; + break; + + case 0x0C: + if ( addr == r_kon ) + m.new_kon = (uint8_t) data; + + if ( addr == r_endx ) // always cleared, regardless of data written + { + m.endx_buf = 0; + m.regs [r_endx] = 0; + } + break; + } +} + +inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; } + +inline bool SPC_DSP::check_kon() +{ + bool old = m.kon_check; + m.kon_check = 0; + return old; +} + +#if !SPC_NO_COPY_STATE_FUNCS + +class SPC_State_Copier { + SPC_DSP::copy_func_t func; + unsigned char** buf; +public: + SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; } + void copy( void* state, size_t size ); + int copy_int( int state, int size ); + void skip( int count ); + void extra(); +}; + +#define SPC_COPY( type, state )\ +{\ + state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\ + assert( (BOOST::type) state == state );\ +} + +#endif + +#endif diff --git a/snes_spc/snes_spc.txt b/snes_spc/snes_spc.txt new file mode 100644 index 00000000..d37b3434 --- /dev/null +++ b/snes_spc/snes_spc.txt @@ -0,0 +1,318 @@ +snes_spc 0.9.0: SNES SPC-700 APU Emulator +----------------------------------------- +Author : Shay Green +Website: http://www.slack.net/~ant/ +Forum : http://groups.google.com/group/blargg-sound-libs +License: GNU Lesser General Public License (LGPL) + + +Contents +-------- +* C and C++ Interfaces +* Overview +* Full SPC Emulation +* DSP Emulation +* SPC Music Playback +* State Copying +* Library Compilation +* Error handling +* Solving Problems +* Accurate S-DSP Limitations +* Fast S-DSP Limitations +* S-SMP Limitations +* To Do +* Thanks + + +C and C++ Interfaces +-------------------- +The library includes a C interface in spc.h and dsp.h, which can be used +from C and C++. This C interface is referred to in the following +documentation. If you're building this as a shared library (rather than +linking statically), you should use the C interface since it will change +less in future versions. + +The native C++ interface is in the header files SNES_SPC.h, SPC_DSP.h, +and SPC_Filter.h, and the two interfaces can be freely mixed in C++ +code. Conversion between the two interfaces generally follows a pattern: + + C interface C++ interface + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + SNES_SPC* spc; SNES_SPC* spc; + + spc = spc_new(); spc = new SNES_SPC; + + spc_play( spc, count, buf ); spc->play( count, buf ); + + spc_sample_rate SNES_SPC::sample_rate + + spc_delete( spc ); delete spc; + + +Overview +-------- +There are three main roles for this library: +* Full SPC emulation in a SNES emulator +* DSP emulation in a SNES emulator (where you emulate the SMP CPU) +* SPC playback in an SPC music file player + +Each of these uses are described separately below. + + +Full SPC Emulation +------------------ +See spc.h for full function reference (SNES_SPC.h if using C++). + +* Create SPC emulator with spc_new() and check for NULL. + +* Call spc_init_rom() with a pointer to the 64-byte IPL ROM dump (not +included with library). + +* When your emulated SNES is powered on, call spc_reset(). When the +reset switch is pressed, call spc_soft_reset(). + +* Call spc_set_output() with your output buffer, then do emulation. + +* When the SNES CPU accesses APU ports, call spc_read_port() and +spc_write_port(). + +* When your emulator's timebase is going back to 0, call +spc_end_frame(), usually at the end of a video frame or scanline. + +* Periodically play samples from your buffer. Use spc_sample_count() to +find out how many samples have been written, then spc_set_output() after +you've made more space in your buffer. + +* Save/load full emulator state with spc_copy_state(). + +* You can save as an SPC music file with spc_save_spc(). + +* When done, use spc_delete() to free memory. + + +DSP Emulation +------------- +See dsp.h for full function reference (SPC_DSP.h if using C++). + +* Create DSP emulator with spc_dsp_new() and check for NULL. + +* Let the DSP know where your 64K RAM is with spc_dsp_init(). + +* When your emulated SNES is powered on, call spc_dsp_reset(). When the +reset switch is pressed, call spc_dsp_soft_reset(). + +* Call spc_dsp_set_output() with your output buffer, then do emulation. + +* Use spc_dsp_run() to run DSP for specified number of clocks (1024000 +per second). Every 32 clocks a pair of samples is added to your output +buffer. + +* Use spc_dsp_read() and spc_dsp_write() to handle DSP reads/writes from +the S-SMP. Before calling these always catch the DSP up to present time +with spc_dsp_run(). + +* Periodically play samples from your buffer. Use spc_dsp_sample_count() +to find out how many samples have been written, then +spc_dsp_set_output() after you've made more space in your buffer. + +* Use spc_dsp_copy_state() to save/load full DSP state. + +* When done, use spc_delete() to free memory. + + +SPC Music Playback +------------------ +See spc.h for full function reference (SNES_SPC.h if using C++). + +* Create SPC emulator with spc_new() and check for NULL. + +* Load SPC with spc_load_spc() and check for error. + +* Optionally cear echo buffer with spc_clear_echo(). Many SPCs have +garbage in echo buffer, which causes noise at the beginning. + +* Generate samples as needed with spc_play(). + +* When done, use spc_delete() to free memory. + +* For a more complete game music playback library, use Game_Music_Emu + + +State Copying +------------- +The SPC and DSP modules include state save/load functions. They take a +pointer to a pointer to a buffer to store state, and a copy function. +This copy function can either copy data to the buffer or from it, +allowing state save and restore with the same library function. The +internal save state format allows for future expansion without making +previous save states unusable. + +The SPC save state format puts the most important parts first to make it +easier to manually examine. It's organized as follows: + +Offset Size Data +- - - - - - - - - - - - - - - - - - + 0 $10000 SPC RAM +$10000 $10 SMP $F0-$FF registers +$10010 4 SMP $F4-$F8 output registers +$10014 2 PC +$10016 1 A +$10017 1 X +$10018 1 Y +$10019 1 PSW +$1001A 1 SP +$1001B 5 internal +$10020 $80 DSP registers +$100A0 ... internal + + +Library Compilation +------------------- +While this library is in C++, it has been written to easily link in a C +program *without* needing the standard C++ library. It doesn't use +exception handling or run-time type information (RTTI), so you can +disable these in your C++ compiler to increase efficiency. + +If you're building a shared library (DLL), I recommend only exporting +the C interfaces in spc.h and dsp.h, as the C++ interfaces expose +implementation details that will break link compatibility across +versions. + +If you're using C and compiling with GCC, I recommend the following +command-line options when compiling the library source, otherwise GCC +will insert calls to the standard C++ library and require that it be +linked in: + + -fno-rtti -fno-exceptions + +For maximum optimization, see the NDEBUG and BLARGG_NONPORTABLE options +in blargg_config. If using GCC, you can enable these by adding the +following command-line options when you invoke gcc. If you encounter +problems, try without -DBLARGG_NONPORTABLE; if that works, contact me so +I can figure out why BLARGG_NONPORTABLE was causing it to fail. + + -O3 -DNDEBUG -DBLARGG_NONPORTABLE -fno-rtti -fno-exceptions + + + +Error handling +-------------- +Functions which can fail have a return type of spc_err_t (blargg_err_t +in the C++ interfaces), which is a pointer to an error string (const +char*). If a function is successful it returns NULL. Errors that you can +easily avoid are checked with debug assertions; spc_err_t return values +are only used for genuine run-time errors that can't be easily predicted +in advance (out of memory, I/O errors, incompatible file data). Your +code should check all error values. + +To improve usability for C programmers, C++ programmers unfamiliar with +exceptions, and compatibility with older C++ compilers, the library does +*not* throw any C++ exceptions and uses malloc() instead of the standard +operator new. This means that you *must* check for NULL when creating a +library object with the new operator. + + +Solving Problems +---------------- +If you're having problems, try the following: + +* If you're getting garbled sound, try this simple siren generator in +place of your call to play(). This will quickly tell whether the problem +is in the library or in your code. + + static void play_siren( long count, short* out ) + { + static double a, a2; + while ( count-- ) + *out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) ); + } + +* Enable debugging support in your environment. This enables assertions +and other run-time checks. + +* Turn the compiler's optimizer is off. Sometimes an optimizer generates +bad code. + +* If multiple threads are being used, ensure that only one at a time is +accessing a given set of objects from the library. This library is not +in general thread-safe, though independent objects can be used in +separate threads. + +* If all else fails, see if the demos work. + + +Accurate S-DSP Limitations +-------------------------- +* Power-up and soft reset behavior might have slight inaccuracies. + +* Muting (FLG bit 6) behavior when toggling bit very rapidly is not +emulated properly. + +* No other known inaccuracies. Has passed 100+ strenuous tests. + + +Fast S-DSP Limitations +---------------------- +* Uses faster sample calculations except in cases where exact value is +actually important (BRR decoding, and gaussian interpolation combined +with pitch modulation). + +* Stops decoding BRR data when a voice's envelope has released to +silence. + +* Emulates 32 clocks at a time, so DSP register and memory accesses are +all done in a bunch rather than spread out. Even though, some clever +code makes register accesses separated by 40 or so clocks occur with +cycle-accurate timing. + + +S-SMP Limitations +----------------- +* Opcode fetches and indirect pointers are always read directly from +memory, even for the $F0-$FF region, and the DSP is not caught up for +these fetches. + +* Attempts to perversely execute data in registers or an area being +modified by echo will not be emulated properly. + +* Has not been thoroughly tested. + +* Test register ($F0) is not implemented. + +* Echo buffer can overwrite IPL ROM area, and does not correctly update +extra RAM there. + + +To Do +----- +* I'd like feedback on the interface and any ways to improve it. In +particular, the differing features between the accurate and fast DSP +emulators might make it harder to cleanly switch between them without +modifying source code. + +* Finish thorough tests on SMP memory access times. + +* Finish thorough tests on SMP instruction behavior (flags, registers). + +* Finish thorough tests on SMP timers. + +* Finish power-up and reset behavior testing. + +* Come up with best starting conditions to play an SPC and implement in +hardware SNES SPC player for verification. + + +Thanks +------ +Thanks to Anti-Resonance's SPC2ROM and help getting SPCs playing on my +SNES in the first place, then Brad Martin's openspc and Chris Moeller's +openspc++ C++ adaptation, giving me a good SPC emulator to start with +several years ago. Thanks to Richard Bannister, Mahendra Tallur, Shazz, +nenolod, theHobbit, Johan Samuelsson, nes6502, and Micket for helping +test my Game_Music_Emu library. Thanks to hcs for help in converting to +C for the Rockbox port. Thanks to byuu (bsnes author) and pagefault and +Nach (zsnes team) for testing and using my new rewritten DSP in their +emulators. Thanks to anomie for his good SNES documentation and +discussions with me to keep it up to date with my latest findings. +-- +Shay Green diff --git a/snes_spc/snes_spc.vcproj b/snes_spc/snes_spc.vcproj new file mode 100644 index 00000000..42560a6d --- /dev/null +++ b/snes_spc/snes_spc.vcproj @@ -0,0 +1,397 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/snes_spc/snes_spc/SNES_SPC.cpp b/snes_spc/snes_spc/SNES_SPC.cpp new file mode 100644 index 00000000..fb3b147a --- /dev/null +++ b/snes_spc/snes_spc/SNES_SPC.cpp @@ -0,0 +1,564 @@ +// Core SPC emulation: CPU, timers, SMP registers, memory + +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "SNES_SPC.h" + +#include + +/* Copyright (C) 2004-2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#define RAM (m.ram.ram) +#define REGS (m.smp_regs [0]) +#define REGS_IN (m.smp_regs [1]) + +// (n ? n : 256) +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) + +// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which +// do crazy echo buffer accesses. +#ifndef SPC_MORE_ACCURACY + #define SPC_MORE_ACCURACY 0 +#endif + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + + +//// Timers + +#if SPC_DISABLE_TEMPO + #define TIMER_DIV( t, n ) ((n) >> t->prescaler) + #define TIMER_MUL( t, n ) ((n) << t->prescaler) +#else + #define TIMER_DIV( t, n ) ((n) / t->prescaler) + #define TIMER_MUL( t, n ) ((n) * t->prescaler) +#endif + +SNES_SPC::Timer* SNES_SPC::run_timer_( Timer* t, rel_time_t time ) +{ + int elapsed = TIMER_DIV( t, time - t->next_time ) + 1; + t->next_time += TIMER_MUL( t, elapsed ); + + if ( t->enabled ) + { + int remain = IF_0_THEN_256( t->period - t->divider ); + int divider = t->divider + elapsed; + int over = elapsed - remain; + if ( over >= 0 ) + { + int n = over / t->period; + t->counter = (t->counter + 1 + n) & 0x0F; + divider = over - n * t->period; + } + t->divider = (uint8_t) divider; + } + return t; +} + +inline SNES_SPC::Timer* SNES_SPC::run_timer( Timer* t, rel_time_t time ) +{ + if ( time >= t->next_time ) + t = run_timer_( t, time ); + return t; +} + + +//// ROM + +void SNES_SPC::enable_rom( int enable ) +{ + if ( m.rom_enabled != enable ) + { + m.rom_enabled = enable; + if ( enable ) + memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram ); + memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size ); + // TODO: ROM can still get overwritten when DSP writes to echo buffer + } +} + + +//// DSP + +#if SPC_LESS_ACCURATE + int const max_reg_time = 29; + + signed char const SNES_SPC::reg_times_ [256] = + { + -1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22, + 2, 3, 0, 1,-12, 0, 1, 1, 7, 6, 14, 14, 27, 14, 14, 23, + 5, 6, 3, 4, -1, 3, 4, 4, 10, 9, 14, 14, 26, -5, 14, 23, + 8, 9, 6, 7, 2, 6, 7, 7, 13, 12, 14, 14, 27, -4, 14, 24, + 11, 12, 9, 10, 5, 9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24, + 14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24, + 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25, + 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25, + + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + }; + + #define RUN_DSP( time, offset ) \ + int count = (time) - (offset) - m.dsp_time;\ + if ( count >= 0 )\ + {\ + int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\ + m.dsp_time += clock_count;\ + dsp.run( clock_count );\ + } +#else + #define RUN_DSP( time, offset ) \ + {\ + int count = (time) - m.dsp_time;\ + if ( !SPC_MORE_ACCURACY || count )\ + {\ + assert( count > 0 );\ + m.dsp_time = (time);\ + dsp.run( count );\ + }\ + } +#endif + +int SNES_SPC::dsp_read( rel_time_t time ) +{ + RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] ); + + int result = dsp.read( REGS [r_dspaddr] & 0x7F ); + + #ifdef SPC_DSP_READ_HOOK + SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result ); + #endif + + return result; +} + +inline void SNES_SPC::dsp_write( int data, rel_time_t time ) +{ + RUN_DSP( time, reg_times [REGS [r_dspaddr]] ) + #if SPC_LESS_ACCURATE + else if ( m.dsp_time == skipping_time ) + { + int r = REGS [r_dspaddr]; + if ( r == SPC_DSP::r_kon ) + m.skipped_kon |= data & ~dsp.read( SPC_DSP::r_koff ); + + if ( r == SPC_DSP::r_koff ) + { + m.skipped_koff |= data; + m.skipped_kon &= ~data; + } + } + #endif + + #ifdef SPC_DSP_WRITE_HOOK + SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data ); + #endif + + if ( REGS [r_dspaddr] <= 0x7F ) + dsp.write( REGS [r_dspaddr], data ); + else if ( !SPC_MORE_ACCURACY ) + dprintf( "SPC wrote to DSP register > $7F\n" ); +} + + +//// Memory access extras + +#if SPC_MORE_ACCURACY + #define MEM_ACCESS( time, addr ) \ + {\ + if ( time >= m.dsp_time )\ + {\ + RUN_DSP( time, max_reg_time );\ + }\ + } +#elif !defined (NDEBUG) + // Debug-only check for read/write within echo buffer, since this might result in + // inaccurate emulation due to the DSP not being caught up to the present. + + bool SNES_SPC::check_echo_access( int addr ) + { + if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) ) + { + int start = 0x100 * dsp.read( SPC_DSP::r_esa ); + int size = 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F); + int end = start + (size ? size : 4); + if ( start <= addr && addr < end ) + { + if ( !m.echo_accessed ) + { + m.echo_accessed = 1; + return true; + } + } + } + return false; + } + + #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) ); +#else + #define MEM_ACCESS( time, addr ) +#endif + + +//// CPU write + +#if SPC_MORE_ACCURACY +static unsigned char const glitch_probs [3] [256] = +{ + 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B, + 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08, + 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09, + 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01, + 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05, + 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07, + 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07, + 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01, + 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09, + 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08, + 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03, + 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03, + 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07, + 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02, + 0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02, + 0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01, + + 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07, + 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06, + 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09, + 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03, + 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07, + 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03, + 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06, + 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03, + 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05, + 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04, + 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05, + 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01, + 0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05, + 0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01, + 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03, + 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01, + + 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A, + 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A, + 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A, + 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09, + 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09, + 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02, + 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07, + 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04, + 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A, + 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07, + 0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04, + 0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02, + 0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06, + 0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03, + 0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02, + 0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03, +}; +#endif + +// divided into multiple functions to keep rarely-used functionality separate +// so often-used functionality can be optimized better by compiler + +// If write isn't preceded by read, data has this added to it +int const no_read_before_write = 0x2000; + +void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr ) +{ + switch ( addr ) + { + case r_t0target: + case r_t1target: + case r_t2target: { + Timer* t = &m.timers [addr - r_t0target]; + int period = IF_0_THEN_256( data ); + if ( t->period != period ) + { + t = run_timer( t, time ); + #if SPC_MORE_ACCURACY + // Insane behavior when target is written just after counter is + // clocked and counter matches new period and new period isn't 1, 2, 4, or 8 + if ( t->divider == (period & 0xFF) && + t->next_time == time + TIMER_MUL( t, 1 ) && + ((period - 1) | ~0x0F) & period ) + { + //dprintf( "SPC pathological timer target write\n" ); + + // If the period is 3, 5, or 9, there's a probability this behavior won't occur, + // based on the previous period + int prob = 0xFF; + int old_period = t->period & 0xFF; + if ( period == 3 ) prob = glitch_probs [0] [old_period]; + if ( period == 5 ) prob = glitch_probs [1] [old_period]; + if ( period == 9 ) prob = glitch_probs [2] [old_period]; + + // The glitch suppresses incrementing of one of the counter bits, based on + // the lowest set bit in the new period + int b = 1; + while ( !(period & b) ) + b <<= 1; + + if ( (rand() >> 4 & 0xFF) <= prob ) + t->divider = (t->divider - b) & 0xFF; + } + #endif + t->period = period; + } + break; + } + + case r_t0out: + case r_t1out: + case r_t2out: + if ( !SPC_MORE_ACCURACY ) + dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out ); + + if ( data < no_read_before_write / 2 ) + run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0; + break; + + // Registers that act like RAM + case 0x8: + case 0x9: + REGS_IN [addr] = (uint8_t) data; + break; + + case r_test: + if ( (uint8_t) data != 0x0A ) + dprintf( "SPC wrote to test register\n" ); + break; + + case r_control: + // port clears + if ( data & 0x10 ) + { + REGS_IN [r_cpuio0] = 0; + REGS_IN [r_cpuio1] = 0; + } + if ( data & 0x20 ) + { + REGS_IN [r_cpuio2] = 0; + REGS_IN [r_cpuio3] = 0; + } + + // timers + { + for ( int i = 0; i < timer_count; i++ ) + { + Timer* t = &m.timers [i]; + int enabled = data >> i & 1; + if ( t->enabled != enabled ) + { + t = run_timer( t, time ); + t->enabled = enabled; + if ( enabled ) + { + t->divider = 0; + t->counter = 0; + } + } + } + } + enable_rom( data & 0x80 ); + break; + } +} + +void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr ) +{ + if ( addr == r_dspdata ) // 99% + dsp_write( data, time ); + else + cpu_write_smp_reg_( data, time, addr ); +} + +void SNES_SPC::cpu_write_high( int data, int i, rel_time_t time ) +{ + if ( i < rom_size ) + { + m.hi_ram [i] = (uint8_t) data; + if ( m.rom_enabled ) + RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM + } + else + { + assert( RAM [i + rom_addr] == (uint8_t) data ); + RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding + cpu_write( data, i + rom_addr - 0x10000, time ); + } +} + +int const bits_in_int = CHAR_BIT * sizeof (int); + +void SNES_SPC::cpu_write( int data, int addr, rel_time_t time ) +{ + MEM_ACCESS( time, addr ) + + // RAM + RAM [addr] = (uint8_t) data; + int reg = addr - 0xF0; + if ( reg >= 0 ) // 64% + { + // $F0-$FF + if ( reg < reg_count ) // 87% + { + REGS [reg] = (uint8_t) data; + + // Ports + #ifdef SPC_PORT_WRITE_HOOK + if ( (unsigned) (reg - r_cpuio0) < port_count ) + SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0), + (uint8_t) data, ®S [r_cpuio0] ); + #endif + + // Registers other than $F2 and $F4-$F7 + //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 ) + // TODO: this is a bit on the fragile side + if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36% + cpu_write_smp_reg( data, time, reg ); + } + // High mem/address wrap-around + else + { + reg -= rom_addr - 0xF0; + if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around + cpu_write_high( data, reg, time ); + } + } +} + + +//// CPU read + +inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time ) +{ + int result = REGS_IN [reg]; + reg -= r_dspaddr; + // DSP addr and data + if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3 + { + result = REGS [r_dspaddr]; + if ( (unsigned) reg == 1 ) + result = dsp_read( time ); // 0xF3 + } + return result; +} + +int SNES_SPC::cpu_read( int addr, rel_time_t time ) +{ + MEM_ACCESS( time, addr ) + + // RAM + int result = RAM [addr]; + int reg = addr - 0xF0; + if ( reg >= 0 ) // 40% + { + reg -= 0x10; + if ( (unsigned) reg >= 0xFF00 ) // 21% + { + reg += 0x10 - r_t0out; + + // Timers + if ( (unsigned) reg < timer_count ) // 90% + { + Timer* t = &m.timers [reg]; + if ( time >= t->next_time ) + t = run_timer_( t, time ); + result = t->counter; + t->counter = 0; + } + // Other registers + else if ( reg < 0 ) // 10% + { + result = cpu_read_smp_reg( reg + r_t0out, time ); + } + else // 1% + { + assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 ); + result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time ); + } + } + } + + return result; +} + + +//// Run + +// Prefix and suffix for CPU emulator function +#define SPC_CPU_RUN_FUNC \ +BOOST::uint8_t* SNES_SPC::run_until_( time_t end_time )\ +{\ + rel_time_t rel_time = m.spc_time - end_time;\ + assert( rel_time <= 0 );\ + m.spc_time = end_time;\ + m.dsp_time += rel_time;\ + m.timers [0].next_time += rel_time;\ + m.timers [1].next_time += rel_time;\ + m.timers [2].next_time += rel_time; + +#define SPC_CPU_RUN_FUNC_END \ + m.spc_time += rel_time;\ + m.dsp_time -= rel_time;\ + m.timers [0].next_time -= rel_time;\ + m.timers [1].next_time -= rel_time;\ + m.timers [2].next_time -= rel_time;\ + assert( m.spc_time <= end_time );\ + return ®S [r_cpuio0];\ +} + +int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks + +void SNES_SPC::end_frame( time_t end_time ) +{ + // Catch CPU up to as close to end as possible. If final instruction + // would exceed end, does NOT execute it and leaves m.spc_time < end. + if ( end_time > m.spc_time ) + run_until_( end_time ); + + m.spc_time -= end_time; + m.extra_clocks += end_time; + + // Greatest number of clocks early that emulation can stop early due to + // not being able to execute current instruction without going over + // allowed time. + assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 ); + + // Catch timers up to CPU + for ( int i = 0; i < timer_count; i++ ) + run_timer( &m.timers [i], 0 ); + + // Catch DSP up to CPU + if ( m.dsp_time < 0 ) + { + RUN_DSP( 0, max_reg_time ); + } + + // Save any extra samples beyond what should be generated + if ( m.buf_begin ) + save_extra(); +} + +// Inclusion here allows static memory access functions and better optimization +#include "SPC_CPU.h" diff --git a/snes_spc/snes_spc/SNES_SPC.h b/snes_spc/snes_spc/SNES_SPC.h new file mode 100644 index 00000000..8ea0392f --- /dev/null +++ b/snes_spc/snes_spc/SNES_SPC.h @@ -0,0 +1,279 @@ +// SNES SPC-700 APU emulator + +// snes_spc 0.9.0 +#ifndef SNES_SPC_H +#define SNES_SPC_H + +#include "SPC_DSP.h" +#include "blargg_endian.h" + +struct SNES_SPC { +public: + typedef BOOST::uint8_t uint8_t; + + // Must be called once before using + blargg_err_t init(); + + // Sample pairs generated per second + enum { sample_rate = 32000 }; + +// Emulator use + + // Sets IPL ROM data. Library does not include ROM data. Most SPC music files + // don't need ROM, but a full emulator must provide this. + enum { rom_size = 0x40 }; + void init_rom( uint8_t const rom [rom_size] ); + + // Sets destination for output samples + typedef short sample_t; + void set_output( sample_t* out, int out_size ); + + // Number of samples written to output since last set + int sample_count() const; + + // Resets SPC to power-on state. This resets your output buffer, so you must + // call set_output() after this. + void reset(); + + // Emulates pressing reset switch on SNES. This resets your output buffer, so + // you must call set_output() after this. + void soft_reset(); + + // 1024000 SPC clocks per second, sample pair every 32 clocks + typedef int time_t; + enum { clock_rate = 1024000 }; + enum { clocks_per_sample = 32 }; + + // Emulated port read/write at specified time + enum { port_count = 4 }; + int read_port ( time_t, int port ); + void write_port( time_t, int port, int data ); + + // Runs SPC to end_time and starts a new time frame at 0 + void end_frame( time_t end_time ); + +// Sound control + + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). + // Reduces emulation accuracy. + enum { voice_count = 8 }; + void mute_voices( int mask ); + + // If true, prevents channels and global volumes from being phase-negated. + // Only supported by fast DSP. + void disable_surround( bool disable = true ); + + // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc. + enum { tempo_unit = 0x100 }; + void set_tempo( int ); + +// SPC music files + + // Loads SPC data into emulator + enum { spc_min_file_size = 0x10180 }; + enum { spc_file_size = 0x10200 }; + blargg_err_t load_spc( void const* in, long size ); + + // Clears echo region. Useful after loading an SPC as many have garbage in echo. + void clear_echo(); + + // Plays for count samples and write samples to out. Discards samples if out + // is NULL. Count must be a multiple of 2 since output is stereo. + blargg_err_t play( int count, sample_t* out ); + + // Skips count samples. Several times faster than play() when using fast DSP. + blargg_err_t skip( int count ); + +// State save/load (only available with accurate DSP) + +#if !SPC_NO_COPY_STATE_FUNCS + // Saves/loads state + enum { state_size = 67 * 1024L }; // maximum space needed when saving + typedef SPC_DSP::copy_func_t copy_func_t; + void copy_state( unsigned char** io, copy_func_t ); + + // Writes minimal header to spc_out + static void init_header( void* spc_out ); + + // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out. + // Does not set up SPC header; use init_header() for that. + void save_spc( void* spc_out ); + + // Returns true if new key-on events occurred since last check. Useful for + // trimming silence while saving an SPC. + bool check_kon(); +#endif + +public: + BLARGG_DISABLE_NOTHROW + + typedef BOOST::uint16_t uint16_t; + + // Time relative to m_spc_time. Speeds up code a bit by eliminating need to + // constantly add m_spc_time to time from CPU. CPU uses time that ends at + // 0 to eliminate reloading end time every instruction. It pays off. + typedef int rel_time_t; + + struct Timer + { + rel_time_t next_time; // time of next event + int prescaler; + int period; + int divider; + int enabled; + int counter; + }; + enum { reg_count = 0x10 }; + enum { timer_count = 3 }; + enum { extra_size = SPC_DSP::extra_size }; + + enum { signature_size = 35 }; + +private: + SPC_DSP dsp; + + #if SPC_LESS_ACCURATE + static signed char const reg_times_ [256]; + signed char reg_times [256]; + #endif + + struct state_t + { + Timer timers [timer_count]; + + uint8_t smp_regs [2] [reg_count]; + + struct + { + int pc; + int a; + int x; + int y; + int psw; + int sp; + } cpu_regs; + + rel_time_t dsp_time; + time_t spc_time; + bool echo_accessed; + + int tempo; + int skipped_kon; + int skipped_koff; + const char* cpu_error; + + int extra_clocks; + sample_t* buf_begin; + sample_t const* buf_end; + sample_t* extra_pos; + sample_t extra_buf [extra_size]; + + int rom_enabled; + uint8_t rom [rom_size]; + uint8_t hi_ram [rom_size]; + + unsigned char cycle_table [256]; + + struct + { + // padding to neutralize address overflow + union { + uint8_t padding1 [0x100]; + uint16_t align; // makes compiler align data for 16-bit access + } padding1 [1]; + uint8_t ram [0x10000]; + uint8_t padding2 [0x100]; + } ram; + }; + state_t m; + + enum { rom_addr = 0xFFC0 }; + + enum { skipping_time = 127 }; + + // Value that padding should be filled with + enum { cpu_pad_fill = 0xFF }; + + enum { + r_test = 0x0, r_control = 0x1, + r_dspaddr = 0x2, r_dspdata = 0x3, + r_cpuio0 = 0x4, r_cpuio1 = 0x5, + r_cpuio2 = 0x6, r_cpuio3 = 0x7, + r_f8 = 0x8, r_f9 = 0x9, + r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC, + r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF + }; + + void timers_loaded(); + void enable_rom( int enable ); + void reset_buf(); + void save_extra(); + void load_regs( uint8_t const in [reg_count] ); + void ram_loaded(); + void regs_loaded(); + void reset_time_regs(); + void reset_common( int timer_counter_init ); + + Timer* run_timer_ ( Timer* t, rel_time_t ); + Timer* run_timer ( Timer* t, rel_time_t ); + int dsp_read ( rel_time_t ); + void dsp_write ( int data, rel_time_t ); + void cpu_write_smp_reg_( int data, rel_time_t, int addr ); + void cpu_write_smp_reg ( int data, rel_time_t, int addr ); + void cpu_write_high ( int data, int i, rel_time_t ); + void cpu_write ( int data, int addr, rel_time_t ); + int cpu_read_smp_reg ( int i, rel_time_t ); + int cpu_read ( int addr, rel_time_t ); + unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t ); + + bool check_echo_access ( int addr ); + uint8_t* run_until_( time_t end_time ); + + struct spc_file_t + { + char signature [signature_size]; + uint8_t has_id666; + uint8_t version; + uint8_t pcl, pch; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t psw; + uint8_t sp; + char text [212]; + uint8_t ram [0x10000]; + uint8_t dsp [128]; + uint8_t unused [0x40]; + uint8_t ipl_rom [0x40]; + }; + + static char const signature [signature_size + 1]; + + void save_regs( uint8_t out [reg_count] ); +}; + +#include + +inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; } + +inline int SNES_SPC::read_port( time_t t, int port ) +{ + assert( (unsigned) port < port_count ); + return run_until_( t ) [port]; +} + +inline void SNES_SPC::write_port( time_t t, int port, int data ) +{ + assert( (unsigned) port < port_count ); + run_until_( t ) [0x10 + port] = data; +} + +inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); } + +inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); } + +#if !SPC_NO_COPY_STATE_FUNCS +inline bool SNES_SPC::check_kon() { return dsp.check_kon(); } +#endif + +#endif diff --git a/snes_spc/snes_spc/SNES_SPC_misc.cpp b/snes_spc/snes_spc/SNES_SPC_misc.cpp new file mode 100644 index 00000000..0ef0275e --- /dev/null +++ b/snes_spc/snes_spc/SNES_SPC_misc.cpp @@ -0,0 +1,380 @@ +// SPC emulation support: init, sample buffering, reset, SPC loading + +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "SNES_SPC.h" + +#include + +/* Copyright (C) 2004-2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#define RAM (m.ram.ram) +#define REGS (m.smp_regs [0]) +#define REGS_IN (m.smp_regs [1]) + +// (n ? n : 256) +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) + + +//// Init + +blargg_err_t SNES_SPC::init() +{ + memset( &m, 0, sizeof m ); + dsp.init( RAM ); + + m.tempo = tempo_unit; + + // Most SPC music doesn't need ROM, and almost all the rest only rely + // on these two bytes + m.rom [0x3E] = 0xFF; + m.rom [0x3F] = 0xC0; + + static unsigned char const cycle_table [128] = + {// 01 23 45 67 89 AB CD EF + 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0 + 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1 + 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2 + 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3 + 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4 + 0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5 + 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6 + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7 + 0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8 + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9 + 0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B + 0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C + 0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D + 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E + 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F + }; + + // unpack cycle table + for ( int i = 0; i < 128; i++ ) + { + int n = cycle_table [i]; + m.cycle_table [i * 2 + 0] = n >> 4; + m.cycle_table [i * 2 + 1] = n & 0x0F; + } + + #if SPC_LESS_ACCURATE + memcpy( reg_times, reg_times_, sizeof reg_times ); + #endif + + reset(); + return 0; +} + +void SNES_SPC::init_rom( uint8_t const in [rom_size] ) +{ + memcpy( m.rom, in, sizeof m.rom ); +} + +void SNES_SPC::set_tempo( int t ) +{ + m.tempo = t; + int const timer2_shift = 4; // 64 kHz + int const other_shift = 3; // 8 kHz + + #if SPC_DISABLE_TEMPO + m.timers [2].prescaler = timer2_shift; + m.timers [1].prescaler = timer2_shift + other_shift; + m.timers [0].prescaler = timer2_shift + other_shift; + #else + if ( !t ) + t = 1; + int const timer2_rate = 1 << timer2_shift; + int rate = (timer2_rate * tempo_unit + (t >> 1)) / t; + if ( rate < timer2_rate / 4 ) + rate = timer2_rate / 4; // max 4x tempo + m.timers [2].prescaler = rate; + m.timers [1].prescaler = rate << other_shift; + m.timers [0].prescaler = rate << other_shift; + #endif +} + +// Timer registers have been loaded. Applies these to the timers. Does not +// reset timer prescalers or dividers. +void SNES_SPC::timers_loaded() +{ + int i; + for ( i = 0; i < timer_count; i++ ) + { + Timer* t = &m.timers [i]; + t->period = IF_0_THEN_256( REGS [r_t0target + i] ); + t->enabled = REGS [r_control] >> i & 1; + t->counter = REGS_IN [r_t0out + i] & 0x0F; + } + + set_tempo( m.tempo ); +} + +// Loads registers from unified 16-byte format +void SNES_SPC::load_regs( uint8_t const in [reg_count] ) +{ + memcpy( REGS, in, reg_count ); + memcpy( REGS_IN, REGS, reg_count ); + + // These always read back as 0 + REGS_IN [r_test ] = 0; + REGS_IN [r_control ] = 0; + REGS_IN [r_t0target] = 0; + REGS_IN [r_t1target] = 0; + REGS_IN [r_t2target] = 0; +} + +// RAM was just loaded from SPC, with $F0-$FF containing SMP registers +// and timer counts. Copies these to proper registers. +void SNES_SPC::ram_loaded() +{ + m.rom_enabled = 0; + load_regs( &RAM [0xF0] ); + + // Put STOP instruction around memory to catch PC underflow/overflow + memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); + memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 ); +} + +// Registers were just loaded. Applies these new values. +void SNES_SPC::regs_loaded() +{ + enable_rom( REGS [r_control] & 0x80 ); + timers_loaded(); +} + +void SNES_SPC::reset_time_regs() +{ + m.cpu_error = 0; + m.echo_accessed = 0; + m.spc_time = 0; + m.dsp_time = 0; + #if SPC_LESS_ACCURATE + m.dsp_time = clocks_per_sample + 1; + #endif + + for ( int i = 0; i < timer_count; i++ ) + { + Timer* t = &m.timers [i]; + t->next_time = 1; + t->divider = 0; + } + + regs_loaded(); + + m.extra_clocks = 0; + reset_buf(); +} + +void SNES_SPC::reset_common( int timer_counter_init ) +{ + int i; + for ( i = 0; i < timer_count; i++ ) + REGS_IN [r_t0out + i] = timer_counter_init; + + // Run IPL ROM + memset( &m.cpu_regs, 0, sizeof m.cpu_regs ); + m.cpu_regs.pc = rom_addr; + + REGS [r_test ] = 0x0A; + REGS [r_control] = 0xB0; // ROM enabled, clear ports + for ( i = 0; i < port_count; i++ ) + REGS_IN [r_cpuio0 + i] = 0; + + reset_time_regs(); +} + +void SNES_SPC::soft_reset() +{ + reset_common( 0 ); + dsp.soft_reset(); +} + +void SNES_SPC::reset() +{ + memset( RAM, 0xFF, 0x10000 ); + ram_loaded(); + reset_common( 0x0F ); + dsp.reset(); +} + +char const SNES_SPC::signature [signature_size + 1] = + "SNES-SPC700 Sound File Data v0.30\x1A\x1A"; + +blargg_err_t SNES_SPC::load_spc( void const* data, long size ) +{ + spc_file_t const* const spc = (spc_file_t const*) data; + + // be sure compiler didn't insert any padding into fle_t + assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 ); + + // Check signature and file size + if ( size < signature_size || memcmp( spc, signature, 27 ) ) + return "Not an SPC file"; + + if ( size < spc_min_file_size ) + return "Corrupt SPC file"; + + // CPU registers + m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl; + m.cpu_regs.a = spc->a; + m.cpu_regs.x = spc->x; + m.cpu_regs.y = spc->y; + m.cpu_regs.psw = spc->psw; + m.cpu_regs.sp = spc->sp; + + // RAM and registers + memcpy( RAM, spc->ram, 0x10000 ); + ram_loaded(); + + // DSP registers + dsp.load( spc->dsp ); + + reset_time_regs(); + + return 0; +} + +void SNES_SPC::clear_echo() +{ + if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) ) + { + int addr = 0x100 * dsp.read( SPC_DSP::r_esa ); + int end = addr + 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F); + if ( end > 0x10000 ) + end = 0x10000; + memset( &RAM [addr], 0xFF, end - addr ); + } +} + + +//// Sample output + +void SNES_SPC::reset_buf() +{ + // Start with half extra buffer of silence + sample_t* out = m.extra_buf; + while ( out < &m.extra_buf [extra_size / 2] ) + *out++ = 0; + + m.extra_pos = out; + m.buf_begin = 0; + + dsp.set_output( 0, 0 ); +} + +void SNES_SPC::set_output( sample_t* out, int size ) +{ + require( (size & 1) == 0 ); // size must be even + + m.extra_clocks &= clocks_per_sample - 1; + if ( out ) + { + sample_t const* out_end = out + size; + m.buf_begin = out; + m.buf_end = out_end; + + // Copy extra to output + sample_t const* in = m.extra_buf; + while ( in < m.extra_pos && out < out_end ) + *out++ = *in++; + + // Handle output being full already + if ( out >= out_end ) + { + // Have DSP write to remaining extra space + out = dsp.extra(); + out_end = &dsp.extra() [extra_size]; + + // Copy any remaining extra samples as if DSP wrote them + while ( in < m.extra_pos ) + *out++ = *in++; + assert( out <= out_end ); + } + + dsp.set_output( out, int(out_end - out) ); + } + else + { + reset_buf(); + } +} + +void SNES_SPC::save_extra() +{ + // Get end pointers + sample_t const* main_end = m.buf_end; // end of data written to buf + sample_t const* dsp_end = dsp.out_pos(); // end of data written to dsp.extra() + if ( m.buf_begin <= dsp_end && dsp_end <= main_end ) + { + main_end = dsp_end; + dsp_end = dsp.extra(); // nothing in DSP's extra + } + + // Copy any extra samples at these ends into extra_buf + sample_t* out = m.extra_buf; + sample_t const* in; + for ( in = m.buf_begin + sample_count(); in < main_end; in++ ) + *out++ = *in; + for ( in = dsp.extra(); in < dsp_end ; in++ ) + *out++ = *in; + + m.extra_pos = out; + assert( out <= &m.extra_buf [extra_size] ); +} + +blargg_err_t SNES_SPC::play( int count, sample_t* out ) +{ + require( (count & 1) == 0 ); // must be even + if ( count ) + { + set_output( out, count ); + end_frame( count * (clocks_per_sample / 2) ); + } + + const char* err = m.cpu_error; + m.cpu_error = 0; + return err; +} + +blargg_err_t SNES_SPC::skip( int count ) +{ + #if SPC_LESS_ACCURATE + if ( count > 2 * sample_rate * 2 ) + { + set_output( 0, 0 ); + + // Skip a multiple of 4 samples + time_t end = count; + count = (count & 3) + 1 * sample_rate * 2; + end = (end - count) * (clocks_per_sample / 2); + + m.skipped_kon = 0; + m.skipped_koff = 0; + + // Preserve DSP and timer synchronization + // TODO: verify that this really preserves it + int old_dsp_time = m.dsp_time + m.spc_time; + m.dsp_time = end - m.spc_time + skipping_time; + end_frame( end ); + m.dsp_time = m.dsp_time - skipping_time + old_dsp_time; + + dsp.write( SPC_DSP::r_koff, m.skipped_koff & ~m.skipped_kon ); + dsp.write( SPC_DSP::r_kon , m.skipped_kon ); + clear_echo(); + } + #endif + + return play( count, 0 ); +} diff --git a/snes_spc/snes_spc/SNES_SPC_state.cpp b/snes_spc/snes_spc/SNES_SPC_state.cpp new file mode 100644 index 00000000..a8052b65 --- /dev/null +++ b/snes_spc/snes_spc/SNES_SPC_state.cpp @@ -0,0 +1,129 @@ +// SPC emulation state save/load: copy_state(), save_spc() +// Separate file to avoid linking in unless needed + +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "SNES_SPC.h" + +#if !SPC_NO_COPY_STATE_FUNCS + +#include + +/* Copyright (C) 2004-2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#define RAM (m.ram.ram) +#define REGS (m.smp_regs [0]) +#define REGS_IN (m.smp_regs [1]) + +void SNES_SPC::save_regs( uint8_t out [reg_count] ) +{ + // Use current timer counter values + for ( int i = 0; i < timer_count; i++ ) + out [r_t0out + i] = m.timers [i].counter; + + // Last written values + memcpy( out, REGS, r_t0out ); +} + +void SNES_SPC::init_header( void* spc_out ) +{ + spc_file_t* const spc = (spc_file_t*) spc_out; + + spc->has_id666 = 26; // has none + spc->version = 30; + memcpy( spc, signature, sizeof spc->signature ); + memset( spc->text, 0, sizeof spc->text ); +} + +void SNES_SPC::save_spc( void* spc_out ) +{ + spc_file_t* const spc = (spc_file_t*) spc_out; + + // CPU + spc->pcl = (uint8_t) (m.cpu_regs.pc >> 0); + spc->pch = (uint8_t) (m.cpu_regs.pc >> 8); + spc->a = m.cpu_regs.a; + spc->x = m.cpu_regs.x; + spc->y = m.cpu_regs.y; + spc->psw = m.cpu_regs.psw; + spc->sp = m.cpu_regs.sp; + + // RAM, ROM + memcpy( spc->ram, RAM, sizeof spc->ram ); + if ( m.rom_enabled ) + memcpy( spc->ram + rom_addr, m.hi_ram, sizeof m.hi_ram ); + memset( spc->unused, 0, sizeof spc->unused ); + memcpy( spc->ipl_rom, m.rom, sizeof spc->ipl_rom ); + + // SMP registers + save_regs( &spc->ram [0xF0] ); + int i; + for ( i = 0; i < port_count; i++ ) + spc->ram [0xF0 + r_cpuio0 + i] = REGS_IN [r_cpuio0 + i]; + + // DSP registers + for ( i = 0; i < SPC_DSP::register_count; i++ ) + spc->dsp [i] = dsp.read( i ); +} + +void SNES_SPC::copy_state( unsigned char** io, copy_func_t copy ) +{ + SPC_State_Copier copier( io, copy ); + + // Make state data more readable by putting 64K RAM, 16 SMP registers, + // then DSP (with its 128 registers) first + + // RAM + enable_rom( 0 ); // will get re-enabled if necessary in regs_loaded() below + copier.copy( RAM, 0x10000 ); + + { + // SMP registers + uint8_t out_ports [port_count]; + uint8_t regs [reg_count]; + memcpy( out_ports, ®S [r_cpuio0], sizeof out_ports ); + save_regs( regs ); + copier.copy( regs, sizeof regs ); + copier.copy( out_ports, sizeof out_ports ); + load_regs( regs ); + regs_loaded(); + memcpy( ®S [r_cpuio0], out_ports, sizeof out_ports ); + } + + // CPU registers + SPC_COPY( uint16_t, m.cpu_regs.pc ); + SPC_COPY( uint8_t, m.cpu_regs.a ); + SPC_COPY( uint8_t, m.cpu_regs.x ); + SPC_COPY( uint8_t, m.cpu_regs.y ); + SPC_COPY( uint8_t, m.cpu_regs.psw ); + SPC_COPY( uint8_t, m.cpu_regs.sp ); + copier.extra(); + + SPC_COPY( int16_t, m.spc_time ); + SPC_COPY( int16_t, m.dsp_time ); + + // DSP + dsp.copy_state( io, copy ); + + // Timers + for ( int i = 0; i < timer_count; i++ ) + { + Timer* t = &m.timers [i]; + SPC_COPY( int16_t, t->next_time ); + SPC_COPY( uint8_t, t->divider ); + copier.extra(); + } + copier.extra(); +} +#endif diff --git a/snes_spc/snes_spc/SPC_CPU.h b/snes_spc/snes_spc/SPC_CPU.h new file mode 100644 index 00000000..957a4015 --- /dev/null +++ b/snes_spc/snes_spc/SPC_CPU.h @@ -0,0 +1,1220 @@ +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +/* Copyright (C) 2004-2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +//// Memory access + +#if SPC_MORE_ACCURACY + #define SUSPICIOUS_OPCODE( name ) ((void) 0) +#else + #define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" ) +#endif + +#define CPU_READ( time, offset, addr )\ + cpu_read( addr, time + offset ) + +#define CPU_WRITE( time, offset, addr, data )\ + cpu_write( data, addr, time + offset ) + +#if SPC_MORE_ACCURACY + #define CPU_READ_TIMER( time, offset, addr, out )\ + { out = CPU_READ( time, offset, addr ); } + +#else + // timers are by far the most common thing read from dp + #define CPU_READ_TIMER( time, offset, addr_, out )\ + {\ + rel_time_t adj_time = time + offset;\ + int dp_addr = addr_;\ + int ti = dp_addr - (r_t0out + 0xF0);\ + if ( (unsigned) ti < timer_count )\ + {\ + Timer* t = &m.timers [ti];\ + if ( adj_time >= t->next_time )\ + t = run_timer_( t, adj_time );\ + out = t->counter;\ + t->counter = 0;\ + }\ + else\ + {\ + out = ram [dp_addr];\ + int i = dp_addr - 0xF0;\ + if ( (unsigned) i < 0x10 )\ + out = cpu_read_smp_reg( i, adj_time );\ + }\ + } +#endif + +#define TIME_ADJ( n ) (n) + +#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out ) +#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) ) +#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) ) + +#define DP_ADDR( addr ) (dp + (addr)) + +#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out ) +#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) ) +#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) + +#define READ_PROG16( addr ) GET_LE16( ram + (addr) ) + +#define SET_PC( n ) (pc = ram + (n)) +#define GET_PC() (int(pc - ram)) +#define READ_PC( pc ) (*(pc)) +#define READ_PC16( pc ) GET_LE16( pc ) + +// TODO: remove non-wrapping versions? +#define SPC_NO_SP_WRAPAROUND 0 + +#define SET_SP( v ) (sp = ram + 0x101 + (v)) +#define GET_SP() (int(sp - 0x101 - ram)) + +#if SPC_NO_SP_WRAPAROUND +#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) +#define PUSH( v ) (void) (*--sp = (uint8_t) (v)) +#define POP( out ) (void) ((out) = *sp++) + +#else +#define PUSH16( data )\ +{\ + int addr = int((sp -= 2) - ram);\ + if ( addr > 0x100 )\ + {\ + SET_LE16( sp, data );\ + }\ + else\ + {\ + ram [(uint8_t) addr + 0x100] = (uint8_t) data;\ + sp [1] = (uint8_t) (data >> 8);\ + sp += 0x100;\ + }\ +} + +#define PUSH( data )\ +{\ + *--sp = (uint8_t) (data);\ + if ( sp - ram == 0x100 )\ + sp += 0x100;\ +} + +#define POP( out )\ +{\ + out = *sp++;\ + if ( sp - ram == 0x201 )\ + {\ + out = sp [-0x101];\ + sp -= 0x100;\ + }\ +} + +#endif + +#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) + +unsigned SNES_SPC::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time ) +{ + unsigned addr = READ_PC16( pc ); + unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); + return t << 8 & 0x100; +} + +//// Status flag handling + +// Hex value in name to clarify code and bit shifting. +// Flag stored in indicated variable during emulation +int const n80 = 0x80; // nz +int const v40 = 0x40; // psw +int const p20 = 0x20; // dp +int const b10 = 0x10; // psw +int const h08 = 0x08; // psw +int const i04 = 0x04; // psw +int const z02 = 0x02; // nz +int const c01 = 0x01; // c + +int const nz_neg_mask = 0x880; // either bit set indicates N flag set + +#define GET_PSW( out )\ +{\ + out = psw & ~(n80 | p20 | z02 | c01);\ + out |= c >> 8 & c01;\ + out |= dp >> 3 & p20;\ + out |= ((nz >> 4) | nz) & n80;\ + if ( !(uint8_t) nz ) out |= z02;\ +} + +#define SET_PSW( in )\ +{\ + psw = in;\ + c = in << 8;\ + dp = in << 3 & 0x100;\ + nz = (in << 4 & 0x800) | (~in & z02);\ +} + +SPC_CPU_RUN_FUNC +{ + uint8_t* const ram = RAM; + int a = m.cpu_regs.a; + int x = m.cpu_regs.x; + int y = m.cpu_regs.y; + uint8_t const* pc; + uint8_t* sp; + int psw; + int c; + int nz; + int dp; + + SET_PC( m.cpu_regs.pc ); + SET_SP( m.cpu_regs.sp ); + SET_PSW( m.cpu_regs.psw ); + + goto loop; + + + // Main loop + +cbranch_taken_loop: + pc += *(BOOST::int8_t const*) pc; +inc_pc_loop: + pc++; +loop: +{ + unsigned opcode; + unsigned data; + + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + + opcode = *pc; + if ( (rel_time += m.cycle_table [opcode]) > 0 ) + goto out_of_time; + + #ifdef SPC_CPU_OPCODE_HOOK + SPC_CPU_OPCODE_HOOK( GET_PC(), opcode ); + #endif + /* + //SUB_CASE_COUNTER( 1 ); + #define PROFILE_TIMER_LOOP( op, addr, len )\ + if ( opcode == op )\ + {\ + int cond = (unsigned) ((addr) - 0xFD) < 3 &&\ + pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\ + SUB_CASE_COUNTER( op && cond );\ + } + + PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 ); + PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 ); + PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); + */ + + // TODO: if PC is at end of memory, this will get wrong operand (very obscure) + data = *++pc; + switch ( opcode ) + { + +// Common instructions + +#define BRANCH( cond )\ +{\ + pc++;\ + pc += (BOOST::int8_t) data;\ + if ( cond )\ + goto loop;\ + pc -= (BOOST::int8_t) data;\ + rel_time -= 2;\ + goto loop;\ +} + + case 0xF0: // BEQ + BRANCH( !(uint8_t) nz ) // 89% taken + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ) + + case 0x3F:{// CALL + int old_addr = int(GET_PC() + 2); + SET_PC( READ_PC16( pc ) ); + PUSH16( old_addr ); + goto loop; + } + + case 0x6F:// RET + #if SPC_NO_SP_WRAPAROUND + { + SET_PC( GET_LE16( sp ) ); + sp += 2; + } + #else + { + int addr = int(sp - ram); + SET_PC( GET_LE16( sp ) ); + sp += 2; + if ( addr < 0x1FF ) + goto loop; + + SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] ); + sp -= 0x100; + } + #endif + goto loop; + + case 0xE4: // MOV a,dp + ++pc; + // 80% from timer + READ_DP_TIMER( 0, data, a = nz ); + goto loop; + + case 0xFA:{// MOV dp,dp + int temp; + READ_DP_TIMER( -2, data, temp ); + data = temp + no_read_before_write ; + } + // fall through + case 0x8F:{// MOV dp,#imm + int temp = READ_PC( pc + 1 ); + pc += 2; + + #if !SPC_MORE_ACCURACY + { + int i = dp + temp; + ram [i] = (uint8_t) data; + i -= 0xF0; + if ( (unsigned) i < 0x10 ) // 76% + { + REGS [i] = (uint8_t) data; + + // Registers other than $F2 and $F4-$F7 + //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 ) + if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12% + cpu_write_smp_reg( data, rel_time, i ); + } + } + #else + WRITE_DP( 0, temp, data ); + #endif + goto loop; + } + + case 0xC4: // MOV dp,a + ++pc; + #if !SPC_MORE_ACCURACY + { + int i = dp + data; + ram [i] = (uint8_t) a; + i -= 0xF0; + if ( (unsigned) i < 0x10 ) // 39% + { + unsigned sel = i - 2; + REGS [i] = (uint8_t) a; + + if ( sel == 1 ) // 51% $F3 + dsp_write( a, rel_time ); + else if ( sel > 1 ) // 1% not $F2 or $F3 + cpu_write_smp_reg_( a, rel_time, i ); + } + } + #else + WRITE_DP( 0, data, a ); + #endif + goto loop; + +#define CASE( n ) case n: + +// Define common address modes based on opcode for immediate mode. Execution +// ends with data set to the address of the operand. +#define ADDR_MODES_( op )\ + CASE( op - 0x02 ) /* (X) */\ + data = x + dp;\ + pc--;\ + goto end_##op;\ + CASE( op + 0x0F ) /* (dp)+Y */\ + data = READ_PROG16( data + dp ) + y;\ + goto end_##op;\ + CASE( op - 0x01 ) /* (dp+X) */\ + data = READ_PROG16( ((uint8_t) (data + x)) + dp );\ + goto end_##op;\ + CASE( op + 0x0E ) /* abs+Y */\ + data += y;\ + goto abs_##op;\ + CASE( op + 0x0D ) /* abs+X */\ + data += x;\ + CASE( op - 0x03 ) /* abs */\ + abs_##op:\ + data += 0x100 * READ_PC( ++pc );\ + goto end_##op;\ + CASE( op + 0x0C ) /* dp+X */\ + data = (uint8_t) (data + x); + +#define ADDR_MODES_NO_DP( op )\ + ADDR_MODES_( op )\ + data += dp;\ + end_##op: + +#define ADDR_MODES( op )\ + ADDR_MODES_( op )\ + CASE( op - 0x04 ) /* dp */\ + data += dp;\ + end_##op: + +// 1. 8-bit Data Transmission Commands. Group I + + ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr + a = nz = READ( 0, data ); + goto inc_pc_loop; + + case 0xBF:{// MOV A,(X)+ + int temp = x + dp; + x = (uint8_t) (x + 1); + a = nz = READ( -1, temp ); + goto loop; + } + + case 0xE8: // MOV A,imm + a = data; + nz = data; + goto inc_pc_loop; + + case 0xF9: // MOV X,dp+Y + data = (uint8_t) (data + y); + case 0xF8: // MOV X,dp + READ_DP_TIMER( 0, data, x = nz ); + goto inc_pc_loop; + + case 0xE9: // MOV X,abs + data = READ_PC16( pc ); + ++pc; + data = READ( 0, data ); + case 0xCD: // MOV X,imm + x = data; + nz = data; + goto inc_pc_loop; + + case 0xFB: // MOV Y,dp+X + data = (uint8_t) (data + x); + case 0xEB: // MOV Y,dp + // 70% from timer + pc++; + READ_DP_TIMER( 0, data, y = nz ); + goto loop; + + case 0xEC:{// MOV Y,abs + int temp = READ_PC16( pc ); + pc += 2; + READ_TIMER( 0, temp, y = nz ); + //y = nz = READ( 0, temp ); + goto loop; + } + + case 0x8D: // MOV Y,imm + y = data; + nz = data; + goto inc_pc_loop; + +// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 + + ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A + WRITE( 0, data, a ); + goto inc_pc_loop; + + { + int temp; + case 0xCC: // MOV abs,Y + temp = y; + goto mov_abs_temp; + case 0xC9: // MOV abs,X + temp = x; + mov_abs_temp: + WRITE( 0, READ_PC16( pc ), temp ); + pc += 2; + goto loop; + } + + case 0xD9: // MOV dp+Y,X + data = (uint8_t) (data + y); + case 0xD8: // MOV dp,X + WRITE( 0, data + dp, x ); + goto inc_pc_loop; + + case 0xDB: // MOV dp+X,Y + data = (uint8_t) (data + x); + case 0xCB: // MOV dp,Y + WRITE( 0, data + dp, y ); + goto inc_pc_loop; + +// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. + + case 0x7D: // MOV A,X + a = x; + nz = x; + goto loop; + + case 0xDD: // MOV A,Y + a = y; + nz = y; + goto loop; + + case 0x5D: // MOV X,A + x = a; + nz = a; + goto loop; + + case 0xFD: // MOV Y,A + y = a; + nz = a; + goto loop; + + case 0x9D: // MOV X,SP + x = nz = GET_SP(); + goto loop; + + case 0xBD: // MOV SP,X + SET_SP( x ); + goto loop; + + //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) + + case 0xAF: // MOV (X)+,A + WRITE_DP( 0, x, a + no_read_before_write ); + x++; + goto loop; + +// 5. 8-BIT LOGIC OPERATION COMMANDS + +#define LOGICAL_OP( op, func )\ + ADDR_MODES( op ) /* addr */\ + data = READ( 0, data );\ + case op: /* imm */\ + nz = a func##= data;\ + goto inc_pc_loop;\ + { unsigned addr;\ + case op + 0x11: /* X,Y */\ + data = READ_DP( -2, y );\ + addr = x + dp;\ + goto addr_##op;\ + case op + 0x01: /* dp,dp */\ + data = READ_DP( -3, data );\ + case op + 0x10:{/*dp,imm*/\ + uint8_t const* addr2 = pc + 1;\ + pc += 2;\ + addr = READ_PC( addr2 ) + dp;\ + }\ + addr_##op:\ + nz = data func READ( -1, addr );\ + WRITE( 0, addr, nz );\ + goto loop;\ + } + + LOGICAL_OP( 0x28, & ); // AND + + LOGICAL_OP( 0x08, | ); // OR + + LOGICAL_OP( 0x48, ^ ); // EOR + +// 4. 8-BIT ARITHMETIC OPERATION COMMANDS + + ADDR_MODES( 0x68 ) // CMP addr + data = READ( 0, data ); + case 0x68: // CMP imm + nz = a - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x79: // CMP (X),(Y) + data = READ_DP( -2, y ); + nz = READ_DP( -1, x ) - data; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0x69: // CMP dp,dp + data = READ_DP( -3, data ); + case 0x78: // CMP dp,imm + nz = READ_DP( -1, READ_PC( ++pc ) ) - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x3E: // CMP X,dp + data += dp; + goto cmp_x_addr; + case 0x1E: // CMP X,abs + data = READ_PC16( pc ); + pc++; + cmp_x_addr: + data = READ( 0, data ); + case 0xC8: // CMP X,imm + nz = x - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x7E: // CMP Y,dp + data += dp; + goto cmp_y_addr; + case 0x5E: // CMP Y,abs + data = READ_PC16( pc ); + pc++; + cmp_y_addr: + data = READ( 0, data ); + case 0xAD: // CMP Y,imm + nz = y - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + { + int addr; + case 0xB9: // SBC (x),(y) + case 0x99: // ADC (x),(y) + pc--; // compensate for inc later + data = READ_DP( -2, y ); + addr = x + dp; + goto adc_addr; + case 0xA9: // SBC dp,dp + case 0x89: // ADC dp,dp + data = READ_DP( -3, data ); + case 0xB8: // SBC dp,imm + case 0x98: // ADC dp,imm + addr = READ_PC( ++pc ) + dp; + adc_addr: + nz = READ( -1, addr ); + goto adc_data; + +// catch ADC and SBC together, then decode later based on operand +#undef CASE +#define CASE( n ) case n: case (n) + 0x20: + ADDR_MODES( 0x88 ) // ADC/SBC addr + data = READ( 0, data ); + case 0xA8: // SBC imm + case 0x88: // ADC imm + addr = -1; // A + nz = a; + adc_data: { + int flags; + if ( opcode >= 0xA0 ) // SBC + data ^= 0xFF; + + flags = data ^ nz; + nz += data + (c >> 8 & 1); + flags ^= nz; + + psw = (psw & ~(v40 | h08)) | + (flags >> 1 & h08) | + ((flags + 0x80) >> 2 & v40); + c = nz; + if ( addr < 0 ) + { + a = (uint8_t) nz; + goto inc_pc_loop; + } + WRITE( 0, addr, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + } + + } + +// 6. ADDITION & SUBTRACTION COMMANDS + +#define INC_DEC_REG( reg, op )\ + nz = reg op;\ + reg = (uint8_t) nz;\ + goto loop; + + case 0xBC: INC_DEC_REG( a, + 1 ) // INC A + case 0x3D: INC_DEC_REG( x, + 1 ) // INC X + case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y + + case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A + case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X + case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y + + case 0x9B: // DEC dp+X + case 0xBB: // INC dp+X + data = (uint8_t) (data + x); + case 0x8B: // DEC dp + case 0xAB: // INC dp + data += dp; + goto inc_abs; + case 0x8C: // DEC abs + case 0xAC: // INC abs + data = READ_PC16( pc ); + pc++; + inc_abs: + nz = (opcode >> 4 & 2) - 1; + nz += READ( -1, data ); + WRITE( 0, data, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + +// 7. SHIFT, ROTATION COMMANDS + + case 0x5C: // LSR A + c = 0; + case 0x7C:{// ROR A + nz = (c >> 1 & 0x80) | (a >> 1); + c = a << 8; + a = nz; + goto loop; + } + + case 0x1C: // ASL A + c = 0; + case 0x3C:{// ROL A + int temp = c >> 8 & 1; + c = a << 1; + nz = c | temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x0B: // ASL dp + c = 0; + data += dp; + goto rol_mem; + case 0x1B: // ASL dp+X + c = 0; + case 0x3B: // ROL dp+X + data = (uint8_t) (data + x); + case 0x2B: // ROL dp + data += dp; + goto rol_mem; + case 0x0C: // ASL abs + c = 0; + case 0x2C: // ROL abs + data = READ_PC16( pc ); + pc++; + rol_mem: + nz = c >> 8 & 1; + nz |= (c = READ( -1, data ) << 1); + WRITE( 0, data, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + + case 0x4B: // LSR dp + c = 0; + data += dp; + goto ror_mem; + case 0x5B: // LSR dp+X + c = 0; + case 0x7B: // ROR dp+X + data = (uint8_t) (data + x); + case 0x6B: // ROR dp + data += dp; + goto ror_mem; + case 0x4C: // LSR abs + c = 0; + case 0x6C: // ROR abs + data = READ_PC16( pc ); + pc++; + ror_mem: { + int temp = READ( -1, data ); + nz = (c >> 1 & 0x80) | (temp >> 1); + c = temp << 8; + WRITE( 0, data, nz ); + goto inc_pc_loop; + } + + case 0x9F: // XCN + nz = a = (a >> 4) | (uint8_t) (a << 4); + goto loop; + +// 8. 16-BIT TRANSMISION COMMANDS + + case 0xBA: // MOVW YA,dp + a = READ_DP( -2, data ); + nz = (a & 0x7F) | (a >> 1); + y = READ_DP( 0, (uint8_t) (data + 1) ); + nz |= y; + goto inc_pc_loop; + + case 0xDA: // MOVW dp,YA + WRITE_DP( -1, data, a ); + WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write ); + goto inc_pc_loop; + +// 9. 16-BIT OPERATION COMMANDS + + case 0x3A: // INCW dp + case 0x1A:{// DECW dp + int temp; + // low byte + data += dp; + temp = READ( -3, data ); + temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW + nz = ((temp >> 1) | temp) & 0x7F; + WRITE( -2, data, /*(uint8_t)*/ temp ); + + // high byte + data = (uint8_t) (data + 1) + dp; + temp = (uint8_t) ((temp >> 8) + READ( -1, data )); + nz |= temp; + WRITE( 0, data, temp ); + + goto inc_pc_loop; + } + + case 0x7A: // ADDW YA,dp + case 0x9A:{// SUBW YA,dp + int lo = READ_DP( -2, data ); + int hi = READ_DP( 0, (uint8_t) (data + 1) ); + int result; + int flags; + + if ( opcode == 0x9A ) // SUBW + { + lo = (lo ^ 0xFF) + 1; + hi ^= 0xFF; + } + + lo += a; + result = y + hi + (lo >> 8); + flags = hi ^ y ^ result; + + psw = (psw & ~(v40 | h08)) | + (flags >> 1 & h08) | + ((flags + 0x80) >> 2 & v40); + c = result; + a = (uint8_t) lo; + result = (uint8_t) result; + y = result; + nz = (((lo >> 1) | lo) & 0x7F) | result; + + goto inc_pc_loop; + } + + case 0x5A: { // CMPW YA,dp + int temp = a - READ_DP( -1, data ); + nz = ((temp >> 1) | temp) & 0x7F; + temp = y + (temp >> 8); + temp -= READ_DP( 0, (uint8_t) (data + 1) ); + nz |= temp; + c = ~temp; + nz &= 0xFF; + goto inc_pc_loop; + } + +// 10. MULTIPLICATION & DIVISON COMMANDS + + case 0xCF: { // MUL YA + unsigned temp = y * a; + a = (uint8_t) temp; + nz = ((temp >> 1) | temp) & 0x7F; + y = temp >> 8; + nz |= y; + goto loop; + } + + case 0x9E: // DIV YA,X + { + unsigned ya = y * 0x100 + a; + + psw &= ~(h08 | v40); + + if ( y >= x ) + psw |= v40; + + if ( (y & 15) >= (x & 15) ) + psw |= h08; + + if ( y < x * 2 ) + { + a = ya / x; + y = ya - a * x; + } + else + { + a = 255 - (ya - x * 0x200) / (256 - x); + y = x + (ya - x * 0x200) % (256 - x); + } + + nz = (uint8_t) a; + a = (uint8_t) a; + + goto loop; + } + +// 11. DECIMAL COMPENSATION COMMANDS + + case 0xDF: // DAA + SUSPICIOUS_OPCODE( "DAA" ); + if ( a > 0x99 || c & 0x100 ) + { + a += 0x60; + c = 0x100; + } + + if ( (a & 0x0F) > 9 || psw & h08 ) + a += 0x06; + + nz = a; + a = (uint8_t) a; + goto loop; + + case 0xBE: // DAS + SUSPICIOUS_OPCODE( "DAS" ); + if ( a > 0x99 || !(c & 0x100) ) + { + a -= 0x60; + c = 0; + } + + if ( (a & 0x0F) > 9 || !(psw & h08) ) + a -= 0x06; + + nz = a; + a = (uint8_t) a; + goto loop; + +// 12. BRANCHING COMMANDS + + case 0x2F: // BRA rel + pc += (BOOST::int8_t) data; + goto inc_pc_loop; + + case 0x30: // BMI + BRANCH( (nz & nz_neg_mask) ) + + case 0x10: // BPL + BRANCH( !(nz & nz_neg_mask) ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + + case 0x70: // BVS + BRANCH( psw & v40 ) + + case 0x50: // BVC + BRANCH( !(psw & v40) ) + + #define CBRANCH( cond )\ + {\ + pc++;\ + if ( cond )\ + goto cbranch_taken_loop;\ + rel_time -= 2;\ + goto inc_pc_loop;\ + } + + case 0x03: // BBS dp.bit,rel + case 0x23: + case 0x43: + case 0x63: + case 0x83: + case 0xA3: + case 0xC3: + case 0xE3: + CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 ) + + case 0x13: // BBC dp.bit,rel + case 0x33: + case 0x53: + case 0x73: + case 0x93: + case 0xB3: + case 0xD3: + case 0xF3: + CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) ) + + case 0xDE: // CBNE dp+X,rel + data = (uint8_t) (data + x); + // fall through + case 0x2E:{// CBNE dp,rel + int temp; + // 61% from timer + READ_DP_TIMER( -4, data, temp ); + CBRANCH( temp != a ) + } + + case 0x6E: { // DBNZ dp,rel + unsigned temp = READ_DP( -4, data ) - 1; + WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write ); + CBRANCH( temp ) + } + + case 0xFE: // DBNZ Y,rel + y = (uint8_t) (y - 1); + BRANCH( y ) + + case 0x1F: // JMP [abs+X] + SET_PC( READ_PC16( pc ) + x ); + // fall through + case 0x5F: // JMP abs + SET_PC( READ_PC16( pc ) ); + goto loop; + +// 13. SUB-ROUTINE CALL RETURN COMMANDS + + case 0x0F:{// BRK + int temp; + int ret_addr = GET_PC(); + SUSPICIOUS_OPCODE( "BRK" ); + SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified + PUSH16( ret_addr ); + GET_PSW( temp ); + psw = (psw | b10) & ~i04; + PUSH( temp ); + goto loop; + } + + case 0x4F:{// PCALL offset + int ret_addr = GET_PC() + 1; + SET_PC( 0xFF00 | data ); + PUSH16( ret_addr ); + goto loop; + } + + case 0x01: // TCALL n + case 0x11: + case 0x21: + case 0x31: + case 0x41: + case 0x51: + case 0x61: + case 0x71: + case 0x81: + case 0x91: + case 0xA1: + case 0xB1: + case 0xC1: + case 0xD1: + case 0xE1: + case 0xF1: { + int ret_addr = GET_PC(); + SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) ); + PUSH16( ret_addr ); + goto loop; + } + +// 14. STACK OPERATION COMMANDS + + { + int temp; + case 0x7F: // RET1 + temp = *sp; + SET_PC( GET_LE16( sp + 1 ) ); + sp += 3; + goto set_psw; + case 0x8E: // POP PSW + POP( temp ); + set_psw: + SET_PSW( temp ); + goto loop; + } + + case 0x0D: { // PUSH PSW + int temp; + GET_PSW( temp ); + PUSH( temp ); + goto loop; + } + + case 0x2D: // PUSH A + PUSH( a ); + goto loop; + + case 0x4D: // PUSH X + PUSH( x ); + goto loop; + + case 0x6D: // PUSH Y + PUSH( y ); + goto loop; + + case 0xAE: // POP A + POP( a ); + goto loop; + + case 0xCE: // POP X + POP( x ); + goto loop; + + case 0xEE: // POP Y + POP( y ); + goto loop; + +// 15. BIT OPERATION COMMANDS + + case 0x02: // SET1 + case 0x22: + case 0x42: + case 0x62: + case 0x82: + case 0xA2: + case 0xC2: + case 0xE2: + case 0x12: // CLR1 + case 0x32: + case 0x52: + case 0x72: + case 0x92: + case 0xB2: + case 0xD2: + case 0xF2: { + int bit = 1 << (opcode >> 5); + int mask = ~bit; + if ( opcode & 0x10 ) + bit = 0; + data += dp; + WRITE( 0, data, (READ( -1, data ) & mask) | bit ); + goto inc_pc_loop; + } + + case 0x0E: // TSET1 abs + case 0x4E: // TCLR1 abs + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -2, data ); + nz = (uint8_t) (a - temp); + temp &= ~a; + if ( opcode == 0x0E ) + temp |= a; + WRITE( 0, data, temp ); + } + goto loop; + + case 0x4A: // AND1 C,mem.bit + c &= MEM_BIT( 0 ); + pc += 2; + goto loop; + + case 0x6A: // AND1 C,/mem.bit + c &= ~MEM_BIT( 0 ); + pc += 2; + goto loop; + + case 0x0A: // OR1 C,mem.bit + c |= MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0x2A: // OR1 C,/mem.bit + c |= ~MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0x8A: // EOR1 C,mem.bit + c ^= MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0xEA: // NOT1 mem.bit + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -1, data & 0x1FFF ); + temp ^= 1 << (data >> 13); + WRITE( 0, data & 0x1FFF, temp ); + } + goto loop; + + case 0xCA: // MOV1 mem.bit,C + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -2, data & 0x1FFF ); + unsigned bit = data >> 13; + temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit); + WRITE( 0, data & 0x1FFF, temp + no_read_before_write ); + } + goto loop; + + case 0xAA: // MOV1 C,mem.bit + c = MEM_BIT( 0 ); + pc += 2; + goto loop; + +// 16. PROGRAM PSW FLAG OPERATION COMMANDS + + case 0x60: // CLRC + c = 0; + goto loop; + + case 0x80: // SETC + c = ~0; + goto loop; + + case 0xED: // NOTC + c ^= 0x100; + goto loop; + + case 0xE0: // CLRV + psw &= ~(v40 | h08); + goto loop; + + case 0x20: // CLRP + dp = 0; + goto loop; + + case 0x40: // SETP + dp = 0x100; + goto loop; + + case 0xA0: // EI + SUSPICIOUS_OPCODE( "EI" ); + psw |= i04; + goto loop; + + case 0xC0: // DI + SUSPICIOUS_OPCODE( "DI" ); + psw &= ~i04; + goto loop; + +// 17. OTHER COMMANDS + + case 0x00: // NOP + goto loop; + + case 0xFF:{// STOP + // handle PC wrap-around + unsigned addr = GET_PC() - 1; + if ( addr >= 0x10000 ) + { + addr &= 0xFFFF; + SET_PC( addr ); + dprintf( "SPC: PC wrapped around\n" ); + goto loop; + } + } + // fall through + case 0xEF: // SLEEP + SUSPICIOUS_OPCODE( "STOP/SLEEP" ); + --pc; + rel_time = 0; + m.cpu_error = "SPC emulation error"; + goto stop; + } // switch + + assert( 0 ); // catch any unhandled instructions +} +out_of_time: + rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode +stop: + + // Uncache registers + if ( GET_PC() >= 0x10000 ) + dprintf( "SPC: PC wrapped around\n" ); + m.cpu_regs.pc = (uint16_t) GET_PC(); + m.cpu_regs.sp = ( uint8_t) GET_SP(); + m.cpu_regs.a = ( uint8_t) a; + m.cpu_regs.x = ( uint8_t) x; + m.cpu_regs.y = ( uint8_t) y; + { + int temp; + GET_PSW( temp ); + m.cpu_regs.psw = (uint8_t) temp; + } +} +SPC_CPU_RUN_FUNC_END diff --git a/snes_spc/snes_spc/SPC_DSP.cpp b/snes_spc/snes_spc/SPC_DSP.cpp new file mode 100644 index 00000000..96719683 --- /dev/null +++ b/snes_spc/snes_spc/SPC_DSP.cpp @@ -0,0 +1,703 @@ +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "SPC_DSP.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +#if INT_MAX < 0x7FFFFFFF + #error "Requires that int type have at least 32 bits" +#endif + + +// TODO: add to blargg_endian.h +#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) +#define GET_LE16A( addr ) GET_LE16( addr ) +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) + +static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] = +{ + 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, + 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, + 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, + 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, + 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, + 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, + 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, + 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF +}; + +// if ( io < -32768 ) io = -32768; +// if ( io > 32767 ) io = 32767; +#define CLAMP16( io )\ +{\ + if ( (int16_t) io != io )\ + io = (io >> 31) ^ 0x7FFF;\ +} + +// Access global DSP register +#define REG(n) m.regs [r_##n] + +// Access voice DSP register +#define VREG(r,n) r [v_##n] + +#define WRITE_SAMPLES( l, r, out ) \ +{\ + out [0] = l;\ + out [1] = r;\ + out += 2;\ + if ( out >= m.out_end )\ + {\ + check( out == m.out_end );\ + check( m.out_end != &m.extra [extra_size] || \ + (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ + out = m.extra;\ + m.out_end = &m.extra [extra_size];\ + }\ +}\ + +void SPC_DSP::set_output( sample_t* out, int size ) +{ + require( (size & 1) == 0 ); // must be even + if ( !out ) + { + out = m.extra; + size = extra_size; + } + m.out_begin = out; + m.out = out; + m.out_end = out + size; +} + +// Volume registers and efb are signed! Easy to forget int8_t cast. +// Prefixes are to avoid accidental use of locals with same names. + +// Interleved gauss table (to improve cache coherency) +// interleved_gauss [i] = gauss [(i & 1) * 256 + 255 - (i >> 1 & 0xFF)] +static short const interleved_gauss [512] = +{ + 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303, + 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299, + 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292, + 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282, + 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269, + 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253, + 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234, + 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213, + 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190, + 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164, + 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136, + 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106, + 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074, + 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040, + 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005, + 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969, + 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932, + 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894, + 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855, + 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816, + 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777, + 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737, + 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698, + 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659, + 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620, + 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582, + 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545, + 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508, + 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473, + 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439, + 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405, + 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374, +}; + + +//// Counters + +#define RATE( rate, div )\ + (rate >= div ? rate / div * 8 - 1 : rate - 1) + +static unsigned const counter_mask [32] = +{ + RATE( 2,2), RATE(2048,4), RATE(1536,3), + RATE(1280,5), RATE(1024,4), RATE( 768,3), + RATE( 640,5), RATE( 512,4), RATE( 384,3), + RATE( 320,5), RATE( 256,4), RATE( 192,3), + RATE( 160,5), RATE( 128,4), RATE( 96,3), + RATE( 80,5), RATE( 64,4), RATE( 48,3), + RATE( 40,5), RATE( 32,4), RATE( 24,3), + RATE( 20,5), RATE( 16,4), RATE( 12,3), + RATE( 10,5), RATE( 8,4), RATE( 6,3), + RATE( 5,5), RATE( 4,4), RATE( 3,3), + RATE( 2,4), + RATE( 1,4) +}; +#undef RATE + +inline void SPC_DSP::init_counter() +{ + // counters start out with this synchronization + m.counters [0] = 1; + m.counters [1] = 0; + m.counters [2] = -0x20u; + m.counters [3] = 0x0B; + + int n = 2; + for ( int i = 1; i < 32; i++ ) + { + m.counter_select [i] = &m.counters [n]; + if ( !--n ) + n = 3; + } + m.counter_select [ 0] = &m.counters [0]; + m.counter_select [30] = &m.counters [2]; +} + +inline void SPC_DSP::run_counter( int i ) +{ + int n = m.counters [i]; + if ( !(n-- & 7) ) + n -= 6 - i; + m.counters [i] = n; +} + +#define READ_COUNTER( rate )\ + (*m.counter_select [rate] & counter_mask [rate]) + + +//// Emulation + +void SPC_DSP::run( int clock_count ) +{ + int new_phase = m.phase + clock_count; + int count = new_phase >> 5; + m.phase = new_phase & 31; + if ( !count ) + return; + + uint8_t* const ram = m.ram; + uint8_t const* const dir = &ram [REG(dir) * 0x100]; + int const slow_gaussian = (REG(pmon) >> 1) | REG(non); + int const noise_rate = REG(flg) & 0x1F; + + // Global volume + int mvoll = (int8_t) REG(mvoll); + int mvolr = (int8_t) REG(mvolr); + if ( mvoll * mvolr < m.surround_threshold ) + mvoll = -mvoll; // eliminate surround + + do + { + // KON/KOFF reading + if ( (m.every_other_sample ^= 1) != 0 ) + { + m.new_kon &= ~m.kon; + m.kon = m.new_kon; + m.t_koff = REG(koff); + } + + run_counter( 1 ); + run_counter( 2 ); + run_counter( 3 ); + + // Noise + if ( !READ_COUNTER( noise_rate ) ) + { + int feedback = (m.noise << 13) ^ (m.noise << 14); + m.noise = (feedback & 0x4000) ^ (m.noise >> 1); + } + + // Voices + int pmon_input = 0; + int main_out_l = 0; + int main_out_r = 0; + int echo_out_l = 0; + int echo_out_r = 0; + voice_t* v = m.voices; + uint8_t* v_regs = m.regs; + int vbit = 1; + do + { + #define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] ) + + int brr_header = ram [v->brr_addr]; + int kon_delay = v->kon_delay; + + // Pitch + int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF; + if ( REG(pmon) & vbit ) + pitch += ((pmon_input >> 5) * pitch) >> 10; + + // KON phases + if ( --kon_delay >= 0 ) + { + v->kon_delay = kon_delay; + + // Get ready to start BRR decoding on next sample + if ( kon_delay == 4 ) + { + v->brr_addr = SAMPLE_PTR( 0 ); + v->brr_offset = 1; + v->buf_pos = v->buf; + brr_header = 0; // header is ignored on this sample + } + + // Envelope is never run during KON + v->env = 0; + v->hidden_env = 0; + + // Disable BRR decoding until last three samples + v->interp_pos = (kon_delay & 3 ? 0x4000 : 0); + + // Pitch is never added during KON + pitch = 0; + } + + int env = v->env; + + // Gaussian interpolation + { + int output = 0; + VREG(v_regs,envx) = (uint8_t) (env >> 4); + if ( env ) + { + // Make pointers into gaussian based on fractional position between samples + int offset = (unsigned) v->interp_pos >> 3 & 0x1FE; + short const* fwd = interleved_gauss + offset; + short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian + + int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12]; + + if ( !(slow_gaussian & vbit) ) // 99% + { + // Faster approximation when exact sample value isn't necessary for pitch mod + output = (fwd [0] * in [0] + + fwd [1] * in [1] + + rev [1] * in [2] + + rev [0] * in [3]) >> 11; + output = (output * env) >> 11; + } + else + { + output = (int16_t) (m.noise * 2); + if ( !(REG(non) & vbit) ) + { + output = (fwd [0] * in [0]) >> 11; + output += (fwd [1] * in [1]) >> 11; + output += (rev [1] * in [2]) >> 11; + output = (int16_t) output; + output += (rev [0] * in [3]) >> 11; + + CLAMP16( output ); + output &= ~1; + } + output = (output * env) >> 11 & ~1; + } + + // Output + int l = output * v->volume [0]; + int r = output * v->volume [1]; + + main_out_l += l; + main_out_r += r; + + if ( REG(eon) & vbit ) + { + echo_out_l += l; + echo_out_r += r; + } + } + + pmon_input = output; + VREG(v_regs,outx) = (uint8_t) (output >> 8); + } + + // Soft reset or end of sample + if ( REG(flg) & 0x80 || (brr_header & 3) == 1 ) + { + v->env_mode = env_release; + env = 0; + } + + if ( m.every_other_sample ) + { + // KOFF + if ( m.t_koff & vbit ) + v->env_mode = env_release; + + // KON + if ( m.kon & vbit ) + { + v->kon_delay = 5; + v->env_mode = env_attack; + REG(endx) &= ~vbit; + } + } + + // Envelope + if ( !v->kon_delay ) + { + if ( v->env_mode == env_release ) // 97% + { + env -= 0x8; + v->env = env; + if ( env <= 0 ) + { + v->env = 0; + goto skip_brr; // no BRR decoding for you! + } + } + else // 3% + { + int rate; + int const adsr0 = VREG(v_regs,adsr0); + int env_data = VREG(v_regs,adsr1); + if ( adsr0 >= 0x80 ) // 97% ADSR + { + if ( v->env_mode > env_decay ) // 89% + { + env--; + env -= env >> 8; + rate = env_data & 0x1F; + + // optimized handling + v->hidden_env = env; + if ( READ_COUNTER( rate ) ) + goto exit_env; + v->env = env; + goto exit_env; + } + else if ( v->env_mode == env_decay ) + { + env--; + env -= env >> 8; + rate = (adsr0 >> 3 & 0x0E) + 0x10; + } + else // env_attack + { + rate = (adsr0 & 0x0F) * 2 + 1; + env += rate < 31 ? 0x20 : 0x400; + } + } + else // GAIN + { + int mode; + env_data = VREG(v_regs,gain); + mode = env_data >> 5; + if ( mode < 4 ) // direct + { + env = env_data * 0x10; + rate = 31; + } + else + { + rate = env_data & 0x1F; + if ( mode == 4 ) // 4: linear decrease + { + env -= 0x20; + } + else if ( mode < 6 ) // 5: exponential decrease + { + env--; + env -= env >> 8; + } + else // 6,7: linear increase + { + env += 0x20; + if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) + env += 0x8 - 0x20; // 7: two-slope linear increase + } + } + } + + // Sustain level + if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) + v->env_mode = env_sustain; + + v->hidden_env = env; + + // unsigned cast because linear decrease going negative also triggers this + if ( (unsigned) env > 0x7FF ) + { + env = (env < 0 ? 0 : 0x7FF); + if ( v->env_mode == env_attack ) + v->env_mode = env_decay; + } + + if ( !READ_COUNTER( rate ) ) + v->env = env; // nothing else is controlled by the counter + } + } + exit_env: + + { + // Apply pitch + int old_pos = v->interp_pos; + int interp_pos = (old_pos & 0x3FFF) + pitch; + if ( interp_pos > 0x7FFF ) + interp_pos = 0x7FFF; + v->interp_pos = interp_pos; + + // BRR decode if necessary + if ( old_pos >= 0x4000 ) + { + // Arrange the four input nybbles in 0xABCD order for easy decoding + int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 + + ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; + + // Advance read position + int const brr_block_size = 9; + int brr_offset = v->brr_offset; + if ( (brr_offset += 2) >= brr_block_size ) + { + // Next BRR block + int brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; + assert( brr_offset == brr_block_size ); + if ( brr_header & 1 ) + { + brr_addr = SAMPLE_PTR( 1 ); + if ( !v->kon_delay ) + REG(endx) |= vbit; + } + v->brr_addr = brr_addr; + brr_offset = 1; + } + v->brr_offset = brr_offset; + + // Decode + + // 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11 + static unsigned char const shifts [16 * 2] = { + 13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11 + }; + int const scale = brr_header >> 4; + int const right_shift = shifts [scale]; + int const left_shift = shifts [scale + 16]; + + // Write to next four samples in circular buffer + int* pos = v->buf_pos; + int* end; + + // Decode four samples + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) + { + // Extract upper nybble and scale appropriately + int s = ((int16_t) nybbles >> right_shift) << left_shift; + + // Apply IIR filter (8 is the most commonly used) + int const filter = brr_header & 0x0C; + int const p1 = pos [brr_buf_size - 1]; + int const p2 = pos [brr_buf_size - 2] >> 1; + if ( filter >= 8 ) + { + s += p1; + s -= p2; + if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 + { + s += p2 >> 4; + s += (p1 * -3) >> 6; + } + else // s += p1 * 0.8984375 - p2 * 0.40625 + { + s += (p1 * -13) >> 7; + s += (p2 * 3) >> 4; + } + } + else if ( filter ) // s += p1 * 0.46875 + { + s += p1 >> 1; + s += (-p1) >> 5; + } + + // Adjust and write sample + CLAMP16( s ); + s = (int16_t) (s * 2); + pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around + } + + if ( pos >= &v->buf [brr_buf_size] ) + pos = v->buf; + v->buf_pos = pos; + } + } +skip_brr: + // Next voice + vbit <<= 1; + v_regs += 0x10; + v++; + } + while ( vbit < 0x100 ); + + // Echo position + int echo_offset = m.echo_offset; + uint8_t* const echo_ptr = &ram [(REG(esa) * 0x100 + echo_offset) & 0xFFFF]; + if ( !echo_offset ) + m.echo_length = (REG(edl) & 0x0F) * 0x800; + echo_offset += 4; + if ( echo_offset >= m.echo_length ) + echo_offset = 0; + m.echo_offset = echo_offset; + + // FIR + int echo_in_l = GET_LE16SA( echo_ptr + 0 ); + int echo_in_r = GET_LE16SA( echo_ptr + 2 ); + + int (*echo_hist_pos) [2] = m.echo_hist_pos; + if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] ) + echo_hist_pos = m.echo_hist; + m.echo_hist_pos = echo_hist_pos; + + echo_hist_pos [0] [0] = echo_hist_pos [8] [0] = echo_in_l; + echo_hist_pos [0] [1] = echo_hist_pos [8] [1] = echo_in_r; + + #define CALC_FIR_( i, in ) ((in) * (int8_t) REG(fir + i * 0x10)) + echo_in_l = CALC_FIR_( 7, echo_in_l ); + echo_in_r = CALC_FIR_( 7, echo_in_r ); + + #define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] ) + #define DO_FIR( i )\ + echo_in_l += CALC_FIR( i, 0 );\ + echo_in_r += CALC_FIR( i, 1 ); + DO_FIR( 0 ); + DO_FIR( 1 ); + DO_FIR( 2 ); + #if defined (__MWERKS__) && __MWERKS__ < 0x3200 + __eieio(); // keeps compiler from stupidly "caching" things in memory + #endif + DO_FIR( 3 ); + DO_FIR( 4 ); + DO_FIR( 5 ); + DO_FIR( 6 ); + + // Echo out + if ( !(REG(flg) & 0x20) ) + { + int l = (echo_out_l >> 7) + ((echo_in_l * (int8_t) REG(efb)) >> 14); + int r = (echo_out_r >> 7) + ((echo_in_r * (int8_t) REG(efb)) >> 14); + + // just to help pass more validation tests + #if SPC_MORE_ACCURACY + l &= ~1; + r &= ~1; + #endif + + CLAMP16( l ); + CLAMP16( r ); + + SET_LE16A( echo_ptr + 0, l ); + SET_LE16A( echo_ptr + 2, r ); + } + + // Sound out + int l = (main_out_l * mvoll + echo_in_l * (int8_t) REG(evoll)) >> 14; + int r = (main_out_r * mvolr + echo_in_r * (int8_t) REG(evolr)) >> 14; + + CLAMP16( l ); + CLAMP16( r ); + + if ( (REG(flg) & 0x40) ) + { + l = 0; + r = 0; + } + + sample_t* out = m.out; + WRITE_SAMPLES( l, r, out ); + m.out = out; + } + while ( --count ); +} + + +//// Setup + +void SPC_DSP::mute_voices( int mask ) +{ + m.mute_mask = mask; + for ( int i = 0; i < voice_count; i++ ) + { + m.voices [i].enabled = (mask >> i & 1) - 1; + update_voice_vol( i * 0x10 ); + } +} + +void SPC_DSP::init( void* ram_64k ) +{ + m.ram = (uint8_t*) ram_64k; + mute_voices( 0 ); + disable_surround( false ); + set_output( 0, 0 ); + reset(); + + #ifndef NDEBUG + // be sure this sign-extends + assert( (int16_t) 0x8000 == -0x8000 ); + + // be sure right shift preserves sign + assert( (-1 >> 1) == -1 ); + + // check clamp macro + int i; + i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); + i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); + + blargg_verify_byte_order(); + #endif +} + +void SPC_DSP::soft_reset_common() +{ + require( m.ram ); // init() must have been called already + + m.noise = 0x4000; + m.echo_hist_pos = m.echo_hist; + m.every_other_sample = 1; + m.echo_offset = 0; + m.phase = 0; + + init_counter(); +} + +void SPC_DSP::soft_reset() +{ + REG(flg) = 0xE0; + soft_reset_common(); +} + +void SPC_DSP::load( uint8_t const regs [register_count] ) +{ + memcpy( m.regs, regs, sizeof m.regs ); + memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); + + // Internal state + int i; + for ( i = voice_count; --i >= 0; ) + { + voice_t& v = m.voices [i]; + v.brr_offset = 1; + v.buf_pos = v.buf; + } + m.new_kon = REG(kon); + + mute_voices( m.mute_mask ); + soft_reset_common(); +} + +void SPC_DSP::reset() { load( initial_regs ); } diff --git a/snes_spc/snes_spc/SPC_DSP.h b/snes_spc/snes_spc/SPC_DSP.h new file mode 100644 index 00000000..045ce3c2 --- /dev/null +++ b/snes_spc/snes_spc/SPC_DSP.h @@ -0,0 +1,212 @@ +// Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one) + +// snes_spc 0.9.0 +#ifndef SPC_DSP_H +#define SPC_DSP_H + +#include "blargg_common.h" + +struct SPC_DSP { +public: + typedef BOOST::uint8_t uint8_t; + +// Setup + + // Initializes DSP and has it use the 64K RAM provided + void init( void* ram_64k ); + + // Sets destination for output samples. If out is NULL or out_size is 0, + // doesn't generate any. + typedef short sample_t; + void set_output( sample_t* out, int out_size ); + + // Number of samples written to output since it was last set, always + // a multiple of 2. Undefined if more samples were generated than + // output buffer could hold. + int sample_count() const; + +// Emulation + + // Resets DSP to power-on state + void reset(); + + // Emulates pressing reset switch on SNES + void soft_reset(); + + // Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp() + // to catch the DSP up to present. + int read ( int addr ) const; + void write( int addr, int data ); + + // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks + // a pair of samples is be generated. + void run( int clock_count ); + +// Sound control + + // Mutes voices corresponding to non-zero bits in mask (overrides VxVOL with 0). + // Reduces emulation accuracy. + enum { voice_count = 8 }; + void mute_voices( int mask ); + + // If true, prevents channels and global volumes from being phase-negated + void disable_surround( bool disable = true ); + +// State + + // Resets DSP and uses supplied values to initialize registers + enum { register_count = 128 }; + void load( uint8_t const regs [register_count] ); + +// DSP register addresses + + // Global registers + enum { + r_mvoll = 0x0C, r_mvolr = 0x1C, + r_evoll = 0x2C, r_evolr = 0x3C, + r_kon = 0x4C, r_koff = 0x5C, + r_flg = 0x6C, r_endx = 0x7C, + r_efb = 0x0D, r_pmon = 0x2D, + r_non = 0x3D, r_eon = 0x4D, + r_dir = 0x5D, r_esa = 0x6D, + r_edl = 0x7D, + r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F + }; + + // Voice registers + enum { + v_voll = 0x00, v_volr = 0x01, + v_pitchl = 0x02, v_pitchh = 0x03, + v_srcn = 0x04, v_adsr0 = 0x05, + v_adsr1 = 0x06, v_gain = 0x07, + v_envx = 0x08, v_outx = 0x09 + }; + +public: + enum { extra_size = 16 }; + sample_t* extra() { return m.extra; } + sample_t const* out_pos() const { return m.out; } +public: + BLARGG_DISABLE_NOTHROW + + typedef BOOST::int8_t int8_t; + typedef BOOST::int16_t int16_t; + + enum { echo_hist_size = 8 }; + + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; + enum { brr_buf_size = 12 }; + struct voice_t + { + int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) + int* buf_pos; // place in buffer where next samples will be decoded + int interp_pos; // relative fractional position in sample (0x1000 = 1.0) + int brr_addr; // address of current BRR block + int brr_offset; // current decoding offset in BRR block + int kon_delay; // KON delay/current setup phase + env_mode_t env_mode; + int env; // current envelope level + int hidden_env; // used by GAIN mode 7, very obscure quirk + int volume [2]; // copy of volume from DSP registers, with surround disabled + int enabled; // -1 if enabled, 0 if muted + }; +private: + struct state_t + { + uint8_t regs [register_count]; + + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) + int echo_hist [echo_hist_size * 2] [2]; + int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] + + int every_other_sample; // toggles every sample + int kon; // KON value when last checked + int noise; + int echo_offset; // offset from ESA in echo buffer + int echo_length; // number of bytes that echo_offset will stop at + int phase; // next clock cycle to run (0-31) + unsigned counters [4]; + + int new_kon; + int t_koff; + + voice_t voices [voice_count]; + + unsigned* counter_select [32]; + + // non-emulation state + uint8_t* ram; // 64K shared RAM between DSP and SMP + int mute_mask; + int surround_threshold; + sample_t* out; + sample_t* out_end; + sample_t* out_begin; + sample_t extra [extra_size]; + }; + state_t m; + + void init_counter(); + void run_counter( int ); + void soft_reset_common(); + void write_outline( int addr, int data ); + void update_voice_vol( int addr ); +}; + +#include + +inline int SPC_DSP::sample_count() const { return int(m.out - m.out_begin); } + +inline int SPC_DSP::read( int addr ) const +{ + assert( (unsigned) addr < register_count ); + return m.regs [addr]; +} + +inline void SPC_DSP::update_voice_vol( int addr ) +{ + int l = (int8_t) m.regs [addr + v_voll]; + int r = (int8_t) m.regs [addr + v_volr]; + + if ( l * r < m.surround_threshold ) + { + // signs differ, so negate those that are negative + l ^= l >> 7; + r ^= r >> 7; + } + + voice_t& v = m.voices [addr >> 4]; + int enabled = v.enabled; + v.volume [0] = l & enabled; + v.volume [1] = r & enabled; +} + +inline void SPC_DSP::write( int addr, int data ) +{ + assert( (unsigned) addr < register_count ); + + m.regs [addr] = (uint8_t) data; + int low = addr & 0x0F; + if ( low < 0x2 ) // voice volumes + { + update_voice_vol( low ^ addr ); + } + else if ( low == 0xC ) + { + if ( addr == r_kon ) + m.new_kon = (uint8_t) data; + + if ( addr == r_endx ) // always cleared, regardless of data written + m.regs [r_endx] = 0; + } +} + +inline void SPC_DSP::disable_surround( bool disable ) +{ + m.surround_threshold = disable ? 0 : -0x4000; +} + +#define SPC_NO_COPY_STATE_FUNCS 1 + +#define SPC_LESS_ACCURATE 1 + +#endif diff --git a/snes_spc/snes_spc/SPC_Filter.cpp b/snes_spc/snes_spc/SPC_Filter.cpp new file mode 100644 index 00000000..b3d57708 --- /dev/null +++ b/snes_spc/snes_spc/SPC_Filter.cpp @@ -0,0 +1,68 @@ +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "SPC_Filter.h" + +#include + +/* Copyright (C) 2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void SPC_Filter::clear() { memset( ch, 0, sizeof ch ); } + +SPC_Filter::SPC_Filter() +{ + gain = gain_unit; + bass = bass_norm; + clear(); +} + +void SPC_Filter::run( short* io, int count ) +{ + require( (count & 1) == 0 ); // must be even + + int const gain = this->gain; + int const bass = this->bass; + chan_t* c = &ch [2]; + do + { + // cache in registers + int sum = (--c)->sum; + int pp1 = c->pp1; + int p1 = c->p1; + + for ( int i = 0; i < count; i += 2 ) + { + // Low-pass filter (two point FIR with coeffs 0.25, 0.75) + int f = io [i] + p1; + p1 = io [i] * 3; + + // High-pass filter ("leaky integrator") + int delta = f - pp1; + pp1 = f; + int s = sum >> (gain_bits + 2); + sum += (delta * gain) - (sum >> bass); + + // Clamp to 16 bits + if ( (short) s != s ) + s = (s >> 31) ^ 0x7FFF; + + io [i] = (short) s; + } + + c->p1 = p1; + c->pp1 = pp1; + c->sum = sum; + ++io; + } + while ( c != ch ); +} diff --git a/snes_spc/snes_spc/SPC_Filter.h b/snes_spc/snes_spc/SPC_Filter.h new file mode 100644 index 00000000..d5c83cb8 --- /dev/null +++ b/snes_spc/snes_spc/SPC_Filter.h @@ -0,0 +1,47 @@ +// Simple low-pass and high-pass filter to better match sound output of a SNES + +// snes_spc 0.9.0 +#ifndef SPC_FILTER_H +#define SPC_FILTER_H + +#include "blargg_common.h" + +struct SPC_Filter { +public: + + // Filters count samples of stereo sound in place. Count must be a multiple of 2. + typedef short sample_t; + void run( sample_t* io, int count ); + +// Optional features + + // Clears filter to silence + void clear(); + + // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit + // are fine, since output is clamped to 16-bit sample range. + enum { gain_unit = 0x100 }; + void set_gain( int gain ); + + // Sets amount of bass (logarithmic scale) + enum { bass_none = 0 }; + enum { bass_norm = 8 }; // normal amount + enum { bass_max = 31 }; + void set_bass( int bass ); + +public: + SPC_Filter(); + BLARGG_DISABLE_NOTHROW +private: + enum { gain_bits = 8 }; + int gain; + int bass; + struct chan_t { int p1, pp1, sum; }; + chan_t ch [2]; +}; + +inline void SPC_Filter::set_gain( int g ) { gain = g; } + +inline void SPC_Filter::set_bass( int b ) { bass = b; } + +#endif diff --git a/snes_spc/snes_spc/blargg_common.h b/snes_spc/snes_spc/blargg_common.h new file mode 100644 index 00000000..75edff39 --- /dev/null +++ b/snes_spc/snes_spc/blargg_common.h @@ -0,0 +1,186 @@ +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +// snes_spc 0.9.0 +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include +#include + +#undef BLARGG_COMMON_H +// allow blargg_config.h to #include blargg_common.h +#include "blargg_config.h" +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +// BLARGG_RESTRICT: equivalent to restrict, where supported +#if defined (__GNUC__) || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT +#endif + +// STATIC_CAST(T,expr): Used in place of static_cast (expr) +#ifndef STATIC_CAST + #define STATIC_CAST(T,expr) ((T) (expr)) +#endif + +// blargg_err_t (0 on success, otherwise error string) +#ifndef blargg_err_t + typedef const char* blargg_err_t; +#endif + +// blargg_vector - very lightweight vector of POD types (no constructor/destructor) +template +class blargg_vector { + T* begin_; + size_t size_; +public: + blargg_vector() : begin_( 0 ), size_( 0 ) { } + ~blargg_vector() { free( begin_ ); } + size_t size() const { return size_; } + T* begin() const { return begin_; } + T* end() const { return begin_ + size_; } + blargg_err_t resize( size_t n ) + { + // TODO: blargg_common.cpp to hold this as an outline function, ugh + void* p = realloc( begin_, n * sizeof (T) ); + if ( p ) + begin_ = (T*) p; + else if ( n > size_ ) // realloc failure only a problem if expanding + return "Out of memory"; + size_ = n; + return 0; + } + void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } + T& operator [] ( size_t n ) const + { + assert( n <= size_ ); // <= to allow past-the-end value + return begin_ [n]; + } +}; + +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if operator new can return NULL + #if __cplusplus >= 199711 || defined (__GNUC__) + #define BLARGG_THROWS( spec ) throw spec + #else + #define BLARGG_THROWS( spec ) + #endif + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ + void operator delete ( void* p ) { free( p ); } + #define BLARGG_NEW new +#else + #include + #define BLARGG_NEW new (std::nothrow) +#endif + +// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant) +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) + +// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +#ifndef BOOST_STATIC_ASSERT + #ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) + #else + // Some other compilers fail when declaring same function multiple times in class, + // so differentiate them by line + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) + #endif +#endif + +// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, +// compiler is assumed to support bool. If undefined, availability is determined. +#ifndef BLARGG_COMPILER_HAS_BOOL + #if defined (__MWERKS__) + #if !__option(bool) + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (_MSC_VER) + #if _MSC_VER < 1100 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (__GNUC__) + // supports bool + #elif __cplusplus < 199711 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif +#endif +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL + // If you get errors here, modify your blargg_config.h file + typedef int bool; + const bool true = 1; + const bool false = 0; +#endif + +// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough + +#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF + typedef long blargg_long; +#else + typedef int blargg_long; +#endif + +#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF + typedef unsigned long blargg_ulong; +#else + typedef unsigned blargg_ulong; +#endif + +// BOOST::int8_t etc. + +// HAVE_STDINT_H: If defined, use for int8_t etc. +#if defined (HAVE_STDINT_H) + #include + #define BOOST + +// HAVE_INTTYPES_H: If defined, use for int8_t etc. +#elif defined (HAVE_INTTYPES_H) + #include + #define BOOST + +#else + struct BOOST + { + #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F + typedef signed char int8_t; + typedef unsigned char uint8_t; + #else + // No suitable 8-bit type available + typedef struct see_blargg_common_h int8_t; + typedef struct see_blargg_common_h uint8_t; + #endif + + #if USHRT_MAX == 0xFFFF + typedef short int16_t; + typedef unsigned short uint16_t; + #else + // No suitable 16-bit type available + typedef struct see_blargg_common_h int16_t; + typedef struct see_blargg_common_h uint16_t; + #endif + + #if ULONG_MAX == 0xFFFFFFFF + typedef long int32_t; + typedef unsigned long uint32_t; + #elif UINT_MAX == 0xFFFFFFFF + typedef int int32_t; + typedef unsigned int uint32_t; + #else + // No suitable 32-bit type available + typedef struct see_blargg_common_h int32_t; + typedef struct see_blargg_common_h uint32_t; + #endif + }; +#endif + +#endif +#endif diff --git a/snes_spc/snes_spc/blargg_config.h b/snes_spc/snes_spc/blargg_config.h new file mode 100644 index 00000000..9dc69db8 --- /dev/null +++ b/snes_spc/snes_spc/blargg_config.h @@ -0,0 +1,24 @@ +// snes_spc 0.9.0 user configuration file. Don't replace when updating library. + +// snes_spc 0.9.0 +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment to disable debugging checks +//#define NDEBUG 1 + +// Uncomment to enable platform-specific (and possibly non-portable) optimizations +//#define BLARGG_NONPORTABLE 1 + +// Uncomment if automatic byte-order determination doesn't work +//#define BLARGG_BIG_ENDIAN 1 + +// Uncomment if you get errors in the bool section of blargg_common.h +//#define BLARGG_COMPILER_HAS_BOOL 1 + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/snes_spc/snes_spc/blargg_endian.h b/snes_spc/snes_spc/blargg_endian.h new file mode 100644 index 00000000..f2daca64 --- /dev/null +++ b/snes_spc/snes_spc/blargg_endian.h @@ -0,0 +1,185 @@ +// CPU Byte Order Utilities + +// snes_spc 0.9.0 +#ifndef BLARGG_ENDIAN +#define BLARGG_ENDIAN + +#include "blargg_common.h" + +// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) +#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + #define BLARGG_CPU_X86 1 + #define BLARGG_CPU_CISC 1 +#endif + +#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc) + #define BLARGG_CPU_POWERPC 1 + #define BLARGG_CPU_RISC 1 +#endif + +// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only +// one may be #defined to 1. Only needed if something actually depends on byte order. +#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) +#ifdef __GLIBC__ + // GCC handles this for us + #include + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define BLARGG_LITTLE_ENDIAN 1 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define BLARGG_BIG_ENDIAN 1 + #endif +#else + +#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ + (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) + #define BLARGG_LITTLE_ENDIAN 1 +#endif + +#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ + defined (__sparc__) || BLARGG_CPU_POWERPC || \ + (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) + #define BLARGG_BIG_ENDIAN 1 +#elif !defined (__mips__) + // No endian specified; assume little-endian, since it's most common + #define BLARGG_LITTLE_ENDIAN 1 +#endif +#endif +#endif + +#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN + #undef BLARGG_LITTLE_ENDIAN + #undef BLARGG_BIG_ENDIAN +#endif + +inline void blargg_verify_byte_order() +{ + #ifndef NDEBUG + #if BLARGG_BIG_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i == 0 ); + #elif BLARGG_LITTLE_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i != 0 ); + #endif + #endif +} + +inline unsigned get_le16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 8 | + (unsigned) ((unsigned char const*) p) [1]; +} + +inline blargg_ulong get_le32( void const* p ) +{ + return (blargg_ulong) ((unsigned char const*) p) [3] << 24 | + (blargg_ulong) ((unsigned char const*) p) [2] << 16 | + (blargg_ulong) ((unsigned char const*) p) [1] << 8 | + (blargg_ulong) ((unsigned char const*) p) [0]; +} + +inline blargg_ulong get_be32( void const* p ) +{ + return (blargg_ulong) ((unsigned char const*) p) [0] << 24 | + (blargg_ulong) ((unsigned char const*) p) [1] << 16 | + (blargg_ulong) ((unsigned char const*) p) [2] << 8 | + (blargg_ulong) ((unsigned char const*) p) [3]; +} + +inline void set_le16( void* p, unsigned n ) +{ + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [0] = (unsigned char) n; +} + +inline void set_be16( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) n; +} + +inline void set_le32( void* p, blargg_ulong n ) +{ + ((unsigned char*) p) [0] = (unsigned char) n; + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); +} + +inline void set_be32( void* p, blargg_ulong n ) +{ + ((unsigned char*) p) [3] = (unsigned char) n; + ((unsigned char*) p) [2] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) (n >> 16); + ((unsigned char*) p) [0] = (unsigned char) (n >> 24); +} + +#if BLARGG_NONPORTABLE + // Optimized implementation if byte order is known + #if BLARGG_LITTLE_ENDIAN + #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) + #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) + #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #elif BLARGG_BIG_ENDIAN + #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) + #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) + #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + + #if BLARGG_CPU_POWERPC + // PowerPC has special byte-reversed instructions + #if defined (__MWERKS__) + #define GET_LE16( addr ) (__lhbrx( addr, 0 )) + #define GET_LE32( addr ) (__lwbrx( addr, 0 )) + #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) + #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) + #elif defined (__GNUC__) + #define GET_LE16( addr ) ({unsigned ppc_lhbrx_; asm( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr), "0" (ppc_lhbrx_) ); ppc_lhbrx_;}) + #define GET_LE32( addr ) ({unsigned ppc_lwbrx_; asm( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr), "0" (ppc_lwbrx_) ); ppc_lwbrx_;}) + #define SET_LE16( addr, in ) ({asm( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) );}) + #define SET_LE32( addr, in ) ({asm( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) );}) + #endif + #endif + #endif +#endif + +#ifndef GET_LE16 + #define GET_LE16( addr ) get_le16( addr ) + #define SET_LE16( addr, data ) set_le16( addr, data ) +#endif + +#ifndef GET_LE32 + #define GET_LE32( addr ) get_le32( addr ) + #define SET_LE32( addr, data ) set_le32( addr, data ) +#endif + +#ifndef GET_BE16 + #define GET_BE16( addr ) get_be16( addr ) + #define SET_BE16( addr, data ) set_be16( addr, data ) +#endif + +#ifndef GET_BE32 + #define GET_BE32( addr ) get_be32( addr ) + #define SET_BE32( addr, data ) set_be32( addr, data ) +#endif + +// auto-selecting versions + +inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } +inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } +inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } +inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } +inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } +inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } + +#endif diff --git a/snes_spc/snes_spc/blargg_source.h b/snes_spc/snes_spc/blargg_source.h new file mode 100644 index 00000000..5e45c4fb --- /dev/null +++ b/snes_spc/snes_spc/blargg_source.h @@ -0,0 +1,100 @@ +/* Included at the beginning of library source files, after all other #include lines. +Sets up helpful macros and services used in my source code. They don't need +module an annoying module prefix on their names since they are defined after +all other #include lines. */ + +// snes_spc 0.9.0 +#ifndef BLARGG_SOURCE_H +#define BLARGG_SOURCE_H + +// If debugging is enabled, abort program if expr is false. Meant for checking +// internal state and consistency. A failed assertion indicates a bug in the module. +// void assert( bool expr ); +#include + +// If debugging is enabled and expr is false, abort program. Meant for checking +// caller-supplied parameters and operations that are outside the control of the +// module. A failed requirement indicates a bug outside the module. +// void require( bool expr ); +#undef require +#define require( expr ) assert( expr ) + +// Like printf() except output goes to debug log file. Might be defined to do +// nothing (not even evaluate its arguments). +// void dprintf( const char* format, ... ); +static inline void blargg_dprintf_( const char*, ... ) { } +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ + +// If enabled, evaluate expr and if false, make debug log entry with source file +// and line. Meant for finding situations that should be examined further, but that +// don't indicate a problem. In all cases, execution continues normally. +#undef check +#define check( expr ) ((void) 0) + +// If expr yields error string, return it from current function, otherwise continue. +#undef RETURN_ERR +#define RETURN_ERR( expr ) do { \ + blargg_err_t blargg_return_err_ = (expr); \ + if ( blargg_return_err_ ) return blargg_return_err_; \ + } while ( 0 ) + +// If ptr is 0, return out of memory error string. +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) + +// Avoid any macros which evaluate their arguments multiple times +#undef min +#undef max + +#define DEF_MIN_MAX( type ) \ + static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\ + static inline type max( type x, type y ) { if ( y < x ) return x; return y; } + +DEF_MIN_MAX( int ) +DEF_MIN_MAX( unsigned ) +DEF_MIN_MAX( long ) +DEF_MIN_MAX( unsigned long ) +DEF_MIN_MAX( float ) +DEF_MIN_MAX( double ) + +#undef DEF_MIN_MAX + +/* +// using const references generates crappy code, and I am currenly only using these +// for built-in types, so they take arguments by value + +// TODO: remove +inline int min( int x, int y ) +template +inline T min( T x, T y ) +{ + if ( x < y ) + return x; + return y; +} + +template +inline T max( T x, T y ) +{ + if ( x < y ) + return y; + return x; +} +*/ + +// TODO: good idea? bad idea? +#undef byte +#define byte byte_ +typedef unsigned char byte; + +// deprecated +#define BLARGG_CHECK_ALLOC CHECK_ALLOC +#define BLARGG_RETURN_ERR RETURN_ERR + +// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/snes_spc/snes_spc/dsp.cpp b/snes_spc/snes_spc/dsp.cpp new file mode 100644 index 00000000..58919e90 --- /dev/null +++ b/snes_spc/snes_spc/dsp.cpp @@ -0,0 +1,48 @@ +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "dsp.h" + +#include "SPC_DSP.h" + +/* Copyright (C) 2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +SPC_DSP* spc_dsp_new( void ) +{ + // be sure constants match + assert( spc_dsp_voice_count == (int) SPC_DSP::voice_count ); + assert( spc_dsp_register_count == (int) SPC_DSP::register_count ); + #if !SPC_NO_COPY_STATE_FUNCS + assert( spc_dsp_state_size == (int) SPC_DSP::state_size ); + #endif + + return new SPC_DSP; +} + +void spc_dsp_delete ( SPC_DSP* s ) { delete s; } +void spc_dsp_init ( SPC_DSP* s, void* ram_64k ) { s->init( ram_64k ); } +void spc_dsp_set_output ( SPC_DSP* s, spc_dsp_sample_t* p, int n ) { s->set_output( p, n ); } +int spc_dsp_sample_count( SPC_DSP const* s ) { return s->sample_count(); } +void spc_dsp_reset ( SPC_DSP* s ) { s->reset(); } +void spc_dsp_soft_reset ( SPC_DSP* s ) { s->soft_reset(); } +int spc_dsp_read ( SPC_DSP const* s, int addr ) { return s->read( addr ); } +void spc_dsp_write ( SPC_DSP* s, int addr, int data ) { s->write( addr, data ); } +void spc_dsp_run ( SPC_DSP* s, int clock_count ) { s->run( clock_count ); } +void spc_dsp_mute_voices ( SPC_DSP* s, int mask ) { s->mute_voices( mask ); } +void spc_dsp_disable_surround( SPC_DSP* s, int disable ) { s->disable_surround( !!disable ); } +void spc_dsp_load ( SPC_DSP* s, unsigned char const regs [spc_dsp_register_count] ) { s->load( regs ); } + +#if !SPC_NO_COPY_STATE_FUNCS +void spc_dsp_copy_state ( SPC_DSP* s, unsigned char** p, spc_dsp_copy_func_t f ) { s->copy_state( p, f ); } +int spc_dsp_check_kon ( SPC_DSP* s ) { return s->check_kon(); } +#endif diff --git a/snes_spc/snes_spc/dsp.h b/snes_spc/snes_spc/dsp.h new file mode 100644 index 00000000..59867d92 --- /dev/null +++ b/snes_spc/snes_spc/dsp.h @@ -0,0 +1,83 @@ +/* SNES SPC-700 DSP emulator C interface (also usable from C++) */ + +/* snes_spc 0.9.0 */ +#ifndef DSP_H +#define DSP_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct SPC_DSP SPC_DSP; + +/* Creates new DSP emulator. NULL if out of memory. */ +SPC_DSP* spc_dsp_new( void ); + +/* Frees DSP emulator */ +void spc_dsp_delete( SPC_DSP* ); + +/* Initializes DSP and has it use the 64K RAM provided */ +void spc_dsp_init( SPC_DSP*, void* ram_64k ); + +/* Sets destination for output samples. If out is NULL or out_size is 0, +doesn't generate any. */ +typedef short spc_dsp_sample_t; +void spc_dsp_set_output( SPC_DSP*, spc_dsp_sample_t* out, int out_size ); + +/* Number of samples written to output since it was last set, always +a multiple of 2. Undefined if more samples were generated than +output buffer could hold. */ +int spc_dsp_sample_count( SPC_DSP const* ); + + +/**** Emulation *****/ + +/* Resets DSP to power-on state */ +void spc_dsp_reset( SPC_DSP* ); + +/* Emulates pressing reset switch on SNES */ +void spc_dsp_soft_reset( SPC_DSP* ); + +/* Reads/writes DSP registers. For accuracy, you must first call spc_dsp_run() */ +/* to catch the DSP up to present. */ +int spc_dsp_read ( SPC_DSP const*, int addr ); +void spc_dsp_write( SPC_DSP*, int addr, int data ); + +/* Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks */ +/* a pair of samples is be generated. */ +void spc_dsp_run( SPC_DSP*, int clock_count ); + + +/**** Sound control *****/ + +/* Mutes voices corresponding to non-zero bits in mask. Reduces emulation accuracy. */ +enum { spc_dsp_voice_count = 8 }; +void spc_dsp_mute_voices( SPC_DSP*, int mask ); + +/* If true, prevents channels and global volumes from being phase-negated. +Only supported by fast DSP; has no effect on accurate DSP. */ +void spc_dsp_disable_surround( SPC_DSP*, int disable ); + + +/**** State save/load *****/ + +/* Resets DSP and uses supplied values to initialize registers */ +enum { spc_dsp_register_count = 128 }; +void spc_dsp_load( SPC_DSP*, unsigned char const regs [spc_dsp_register_count] ); + +/* Saves/loads exact emulator state (accurate DSP only) */ +enum { spc_dsp_state_size = 640 }; /* maximum space needed when saving */ +typedef void (*spc_dsp_copy_func_t)( unsigned char** io, void* state, size_t ); +void spc_dsp_copy_state( SPC_DSP*, unsigned char** io, spc_dsp_copy_func_t ); + +/* Returns non-zero if new key-on events occurred since last call (accurate DSP only) */ +int spc_dsp_check_kon( SPC_DSP* ); + + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/snes_spc/snes_spc/spc.cpp b/snes_spc/snes_spc/spc.cpp new file mode 100644 index 00000000..0aeb26f2 --- /dev/null +++ b/snes_spc/snes_spc/spc.cpp @@ -0,0 +1,73 @@ +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "spc.h" + +#include "SNES_SPC.h" +#include "SPC_Filter.h" + +/* Copyright (C) 2004-2007 Shay Green. This module 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 +module 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 module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +SNES_SPC* spc_new( void ) +{ + // be sure constants match + assert( spc_sample_rate == (int) SNES_SPC::sample_rate ); + assert( spc_rom_size == (int) SNES_SPC::rom_size ); + assert( spc_clock_rate == (int) SNES_SPC::clock_rate ); + assert( spc_clocks_per_sample == (int) SNES_SPC::clocks_per_sample ); + assert( spc_port_count == (int) SNES_SPC::port_count ); + assert( spc_voice_count == (int) SNES_SPC::voice_count ); + assert( spc_tempo_unit == (int) SNES_SPC::tempo_unit ); + assert( spc_file_size == (int) SNES_SPC::spc_file_size ); + #if !SPC_NO_COPY_STATE_FUNCS + assert( spc_state_size == (int) SNES_SPC::state_size ); + #endif + + SNES_SPC* s = new SNES_SPC; + if ( s && s->init() ) + { + delete s; + s = 0; + } + return s; +} + +void spc_delete ( SNES_SPC* s ) { delete s; } +void spc_init_rom ( SNES_SPC* s, unsigned char const r [64] ) { s->init_rom( r ); } +void spc_set_output ( SNES_SPC* s, spc_sample_t* p, int n ) { s->set_output( p, n ); } +int spc_sample_count ( SNES_SPC const* s ) { return s->sample_count(); } +void spc_reset ( SNES_SPC* s ) { s->reset(); } +void spc_soft_reset ( SNES_SPC* s ) { s->soft_reset(); } +int spc_read_port ( SNES_SPC* s, spc_time_t t, int p ) { return s->read_port( t, p ); } +void spc_write_port ( SNES_SPC* s, spc_time_t t, int p, int d ) { s->write_port( t, p, d ); } +void spc_end_frame ( SNES_SPC* s, spc_time_t t ) { s->end_frame( t ); } +void spc_mute_voices ( SNES_SPC* s, int mask ) { s->mute_voices( mask ); } +void spc_disable_surround( SNES_SPC* s, int disable ) { s->disable_surround( !!disable ); } +void spc_set_tempo ( SNES_SPC* s, int tempo ) { s->set_tempo( tempo ); } +spc_err_t spc_load_spc ( SNES_SPC* s, void const* p, long n ) { return s->load_spc( p, n ); } +void spc_clear_echo ( SNES_SPC* s ) { s->clear_echo(); } +spc_err_t spc_play ( SNES_SPC* s, int count, short* out ) { return s->play( count, out ); } +spc_err_t spc_skip ( SNES_SPC* s, int count ) { return s->skip( count ); } +#if !SPC_NO_COPY_STATE_FUNCS +void spc_copy_state ( SNES_SPC* s, unsigned char** p, spc_copy_func_t f ) { s->copy_state( p, f ); } +void spc_init_header ( void* spc_out ) { SNES_SPC::init_header( spc_out ); } +void spc_save_spc ( SNES_SPC* s, void* spc_out ) { s->save_spc( spc_out ); } +int spc_check_kon ( SNES_SPC* s ) { return s->check_kon(); } +#endif + +SPC_Filter* spc_filter_new( void ) { return new SPC_Filter; } +void spc_filter_delete( SPC_Filter* f ) { delete f; } +void spc_filter_run( SPC_Filter* f, spc_sample_t* p, int s ) { f->run( p, s ); } +void spc_filter_clear( SPC_Filter* f ) { f->clear(); } +void spc_filter_set_gain( SPC_Filter* f, int gain ) { f->set_gain( gain ); } +void spc_filter_set_bass( SPC_Filter* f, int bass ) { f->set_bass( bass ); } diff --git a/snes_spc/snes_spc/spc.h b/snes_spc/snes_spc/spc.h new file mode 100644 index 00000000..9ffdedce --- /dev/null +++ b/snes_spc/snes_spc/spc.h @@ -0,0 +1,147 @@ +/* SNES SPC-700 APU emulator C interface (also usable from C++) */ + +/* snes_spc 0.9.0 */ +#ifndef SPC_H +#define SPC_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/* Error string return. NULL if success, otherwise error message. */ +typedef const char* spc_err_t; + +typedef struct SNES_SPC SNES_SPC; + +/* Creates new SPC emulator. NULL if out of memory. */ +SNES_SPC* spc_new( void ); + +/* Frees SPC emulator */ +void spc_delete( SNES_SPC* ); + +/* Sample pairs generated per second */ +enum { spc_sample_rate = 32000 }; + + +/**** Emulator use ****/ + +/* Sets IPL ROM data. Library does not include ROM data. Most SPC music files +don't need ROM, but a full emulator must provide this. */ +enum { spc_rom_size = 0x40 }; +void spc_init_rom( SNES_SPC*, unsigned char const rom [spc_rom_size] ); + +/* Sets destination for output samples */ +typedef short spc_sample_t; +void spc_set_output( SNES_SPC*, spc_sample_t* out, int out_size ); + +/* Number of samples written to output since last set */ +int spc_sample_count( SNES_SPC const* ); + +/* Resets SPC to power-on state. This resets your output buffer, so you must +call spc_set_output() after this. */ +void spc_reset( SNES_SPC* ); + +/* Emulates pressing reset switch on SNES. This resets your output buffer, so +you must call spc_set_output() after this. */ +void spc_soft_reset( SNES_SPC* ); + +/* 1024000 SPC clocks per second, sample pair every 32 clocks */ +typedef int spc_time_t; +enum { spc_clock_rate = 1024000 }; +enum { spc_clocks_per_sample = 32 }; + +/* Reads/writes port at specified time */ +enum { spc_port_count = 4 }; +int spc_read_port ( SNES_SPC*, spc_time_t, int port ); +void spc_write_port( SNES_SPC*, spc_time_t, int port, int data ); + +/* Runs SPC to end_time and starts a new time frame at 0 */ +void spc_end_frame( SNES_SPC*, spc_time_t end_time ); + + +/**** Sound control ****/ + +/*Mutes voices corresponding to non-zero bits in mask. Reduces emulation accuracy. */ +enum { spc_voice_count = 8 }; +void spc_mute_voices( SNES_SPC*, int mask ); + +/* If true, prevents channels and global volumes from being phase-negated. +Only supported by fast DSP; has no effect on accurate DSP. */ +void spc_disable_surround( SNES_SPC*, int disable ); + +/* Sets tempo, where spc_tempo_unit = normal, spc_tempo_unit / 2 = half speed, etc. */ +enum { spc_tempo_unit = 0x100 }; +void spc_set_tempo( SNES_SPC*, int ); + + +/**** SPC music playback *****/ + +/* Loads SPC data into emulator. Returns NULL on success, otherwise error string. */ +spc_err_t spc_load_spc( SNES_SPC*, void const* spc_in, long size ); + +/* Clears echo region. Useful after loading an SPC as many have garbage in echo. */ +void spc_clear_echo( SNES_SPC* ); + +/* Plays for count samples and write samples to out. Discards samples if out +is NULL. Count must be a multiple of 2 since output is stereo. */ +spc_err_t spc_play( SNES_SPC*, int count, short* out ); + +/* Skips count samples. Several times faster than spc_play(). */ +spc_err_t spc_skip( SNES_SPC*, int count ); + + +/**** State save/load (only available with accurate DSP) ****/ + +/* Saves/loads exact emulator state */ +enum { spc_state_size = 67 * 1024L }; /* maximum space needed when saving */ +typedef void (*spc_copy_func_t)( unsigned char** io, void* state, size_t ); +void spc_copy_state( SNES_SPC*, unsigned char** io, spc_copy_func_t ); + +/* Writes minimal SPC file header to spc_out */ +void spc_init_header( void* spc_out ); + +/* Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out. +Does not set up SPC header; use spc_init_header() for that. */ +enum { spc_file_size = 0x10200 }; /* spc_out must have this many bytes allocated */ +void spc_save_spc( SNES_SPC*, void* spc_out ); + +/* Returns non-zero if new key-on events occurred since last check. Useful for +trimming silence while saving an SPC. */ +int spc_check_kon( SNES_SPC* ); + + +/**** SPC_Filter ****/ + +typedef struct SPC_Filter SPC_Filter; + +/* Creates new filter. NULL if out of memory. */ +SPC_Filter* spc_filter_new( void ); + +/* Frees filter */ +void spc_filter_delete( SPC_Filter* ); + +/* Filters count samples of stereo sound in place. Count must be a multiple of 2. */ +void spc_filter_run( SPC_Filter*, spc_sample_t* io, int count ); + +/* Clears filter to silence */ +void spc_filter_clear( SPC_Filter* ); + +/* Sets gain (volume), where spc_filter_gain_unit is normal. Gains greater than +spc_filter_gain_unit are fine, since output is clamped to 16-bit sample range. */ +enum { spc_filter_gain_unit = 0x100 }; +void spc_filter_set_gain( SPC_Filter*, int gain ); + +/* Sets amount of bass (logarithmic scale) */ +enum { spc_filter_bass_none = 0 }; +enum { spc_filter_bass_norm = 8 }; /* normal amount */ +enum { spc_filter_bass_max = 31 }; +void spc_filter_set_bass( SPC_Filter*, int bass ); + + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/src/actor.h b/src/actor.h index cfa37cb0..29339343 100644 --- a/src/actor.h +++ b/src/actor.h @@ -545,9 +545,9 @@ public: bool CheckLocalView (int playernum) const; // Finds the first item of a particular type. - AInventory *FindInventory (const PClass *type) const; - AInventory *FindInventory (FName type) const; - template T *FindInventory () const + AInventory *FindInventory (const PClass *type); + AInventory *FindInventory (FName type); + template T *FindInventory () { return static_cast (FindInventory (RUNTIME_CLASS(T))); } @@ -556,7 +556,7 @@ public: AInventory *GiveInventoryType (const PClass *type); // Returns the first item held with IF_INVBAR set. - AInventory *FirstInv () const; + AInventory *FirstInv (); // Tries to give the actor some ammo. bool GiveAmmo (const PClass *type, int amount); @@ -572,7 +572,7 @@ public: void ConversationAnimation (int animnum); // Make this actor hate the same things as another actor - void CopyFriendliness (const AActor *other, bool changeTarget); + void CopyFriendliness (AActor *other, bool changeTarget); // Moves the other actor's inventory to this one void ObtainInventory (AActor *other); @@ -647,35 +647,32 @@ public: BYTE movedir; // 0-7 SBYTE visdir; SWORD movecount; // when 0, select a new dir - AActor *target; // thing being chased/attacked (or NULL) + TObjPtr target; // thing being chased/attacked (or NULL) // also the originator for missiles - AActor *lastenemy; // Last known enemy -- killogh 2/15/98 - AActor *LastHeard; // [RH] Last actor this one heard + TObjPtr lastenemy; // Last known enemy -- killogh 2/15/98 + TObjPtr LastHeard; // [RH] Last actor this one heard SDWORD reactiontime; // if non 0, don't attack yet; used by // player to freeze a bit after teleporting SDWORD threshold; // if > 0, the target will be chased // no matter what (even if shot) player_s *player; // only valid if type of APlayerPawn - union - { - AActor *Actor; // Actor last looked for (if TIDtoHate != 0) - SDWORD PlayerNumber; // Player number last looked for - } LastLook; + TObjPtr LastLookActor; // Actor last looked for (if TIDtoHate != 0) WORD SpawnPoint[3]; // For nightmare respawn WORD SpawnAngle; int skillrespawncount; - AActor *tracer; // Thing being chased/attacked for tracers - AActor *master; // Thing which spawned this one (prevents mutual attacks) + TObjPtr tracer; // Thing being chased/attacked for tracers + TObjPtr master; // Thing which spawned this one (prevents mutual attacks) fixed_t floorclip; // value to use for floor clipping SWORD tid; // thing identifier BYTE special; // special int args[5]; // special arguments AActor *inext, **iprev;// Links to other mobjs in same bucket - AActor *goal; // Monster's goal if not chasing anything + TObjPtr goal; // Monster's goal if not chasing anything BYTE waterlevel; // 0=none, 1=feet, 2=waist, 3=eyes BYTE boomwaterlevel; // splash information for non-swimmable water sectors BYTE MinMissileChance;// [RH] If a random # is > than this, then missile attack. + SBYTE LastLookPlayerNumber;// Player number last looked for (if TIDtoHate == 0) WORD SpawnFlags; fixed_t meleerange; // specifies how far a melee attack reaches. fixed_t meleethreshold; // Distance below which a monster doesn't try to shoot missiles anynore @@ -686,9 +683,10 @@ public: int bouncecount; // Strife's grenades only bounce twice before exploding fixed_t gravity; // [GRB] Gravity factor int FastChaseStrafeCount; - // [KS] These temporary-use properties are needed to allow A_LookEx to pass it's parameters to LookFor*InBlock in - // P_BlockmapSearch so that friendly enemies and monsters that look for other monsters can find their targets properly. - // If there's a cleaner way of doing this, feel free to remove these and use that method instead. + // [KS] These temporary-use properties are needed to allow A_LookEx to pass its parameters to + // LookFor*InBlock in P_BlockmapSearch so that friendly enemies and monsters that look for + // other monsters can find their targets properly. If there's a cleaner way of doing this, + // feel free to remove these and use that method instead. fixed_t LookExMinDist; // Minimum sight distance fixed_t LookExMaxDist; // Maximum sight distance angle_t LookExFOV; // Field of Vision @@ -696,7 +694,7 @@ public: // a linked list of sectors where this object appears struct msecnode_s *touching_sectorlist; // phares 3/14/98 - AInventory *Inventory; // [RH] This actor's inventory + TObjPtr Inventory; // [RH] This actor's inventory DWORD InventoryID; // A unique ID to keep track of inventory items //Added by MC: @@ -769,10 +767,12 @@ public: enum { S_NULL = 2, S_GENERICFREEZEDEATH = 3 }; - TArray dynamiclights; + TArray> dynamiclights; void * lightassociations; bool hasmodel; subsector_s * subsector; + + size_t PropagateMark(); }; class FActorIterator diff --git a/src/b_bot.h b/src/b_bot.h index 1dd48864..607fd01f 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -117,7 +117,7 @@ public: botinfo_t *botinfo; int spawn_tries; int wanted_botnum; - AActor *firstthing; + TObjPtr firstthing; bool m_Thinking; @@ -141,8 +141,8 @@ protected: bool ctf; int loaded_bots; int t_join; - AActor *body1; - AActor *body2; + TObjPtr body1; + TObjPtr body2; bool observer; //Consoleplayer is observer. }; diff --git a/src/b_func.cpp b/src/b_func.cpp index e77761bc..8e4fe03d 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -453,16 +453,26 @@ void DCajunMaster::SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum) if (hostnum == 1) { if (body1) + { body1->SetOrigin (x, y, z); + } else + { body1 = Spawn ("CajunBodyNode", x, y, z, NO_REPLACE); + GC::WriteBarrier(this, body1); + } } else if (hostnum == 2) { if (body2) + { body2->SetOrigin (x, y, z); + } else + { body2 = Spawn ("CajunBodyNode", x, y, z, NO_REPLACE); + GC::WriteBarrier(this, body2); + } } } diff --git a/src/b_game.cpp b/src/b_game.cpp index d8dcccf1..cdb2e52e 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -94,9 +94,11 @@ DCajunMaster::~DCajunMaster() ForgetBots(); if (getspawned != NULL) { - delete getspawned; + getspawned->Destroy(); getspawned = NULL; } + // FIXME: Make this object proper + ObjectFlags |= OF_Cleanup | OF_YesReallyDelete; } //This function is called every tick (from g_game.c), diff --git a/src/c_console.cpp b/src/c_console.cpp index 9a3920a1..9ea68023 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -229,7 +229,7 @@ CUSTOM_CVAR (Int, msgmidcolor2, 4, CVAR_ARCHIVE) static void maybedrawnow (bool tick, bool force) { // FIXME: Does not work right with hw2d - if (ConsoleDrawing || !gotconback || screen->IsLocked () || screen->Accel2D) + if (ConsoleDrawing || !gotconback || screen == NULL || screen->IsLocked () || screen->Accel2D) { return; } diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index fb43ec27..2d5abf05 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -1250,23 +1250,23 @@ CCMD (key) // These all begin with '+' as opposed to '-'. void C_ExecCmdLineParams () { - for (int currArg = 1; currArg < Args.NumArgs(); ) + for (int currArg = 1; currArg < Args->NumArgs(); ) { - if (*Args.GetArg (currArg++) == '+') + if (*Args->GetArg (currArg++) == '+') { FString cmdString; int cmdlen = 1; int argstart = currArg - 1; - while (currArg < Args.NumArgs()) + while (currArg < Args->NumArgs()) { - if (*Args.GetArg (currArg) == '-' || *Args.GetArg (currArg) == '+') + if (*Args->GetArg (currArg) == '-' || *Args->GetArg (currArg) == '+') break; currArg++; cmdlen++; } - cmdString = BuildString (cmdlen, Args.GetArgList (argstart)); + cmdString = BuildString (cmdlen, Args->GetArgList (argstart)); if (!cmdString.IsEmpty()) { C_DoCommand (&cmdString[1]); diff --git a/src/cmdlib.h b/src/cmdlib.h index a6a45097..af31bb26 100644 --- a/src/cmdlib.h +++ b/src/cmdlib.h @@ -23,7 +23,7 @@ #include // the dec offsetof macro doesnt work very well... -#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier) +#define myoffsetof(type,identifier) ((size_t)&((type *)1)->identifier - 1) int Q_filelength (FILE *f); bool FileExists (const char *filename); diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 9a728287..071e2a5e 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -161,7 +161,7 @@ public: void DoPickupSpecial (AActor *toucher); private: const PClass *DetermineType (); - AInventory *RealPickup; + TObjPtr RealPickup; }; IMPLEMENT_POINTY_CLASS (ADehackedPickup) diff --git a/src/d_main.cpp b/src/d_main.cpp index dae6892d..0a614962 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -745,7 +745,6 @@ void D_DoomLoop () if (singletics) { I_StartTic (); - DObject::BeginFrame (); D_ProcessEvents (); G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]); //Added by MC: For some of that bot stuff. The main bot function. @@ -774,7 +773,7 @@ void D_DoomLoop () G_Ticker (); gametic++; maketic++; - DObject::EndFrame (); + GC::CheckGC (); Net_NewMakeTic (); } else @@ -1644,7 +1643,7 @@ static EIWADType IdentifyVersion (const char *zdoom_wad) { WadStuff wads[sizeof(IWADNames)/sizeof(char *)]; size_t foundwads[NUM_IWAD_TYPES] = { 0 }; - const char *iwadparm = Args.CheckValue ("-iwad"); + const char *iwadparm = Args->CheckValue ("-iwad"); size_t numwads; int pickwad; size_t i; @@ -1914,7 +1913,7 @@ static const char *BaseFileSearch (const char *file, const char *ext, bool lookf bool ConsiderPatches (const char *arg, const char *ext) { bool noDef = false; - DArgs *files = Args.GatherFiles (arg, ext, false); + DArgs *files = Args->GatherFiles (arg, ext, false); if (files->NumArgs() > 0) { @@ -1930,7 +1929,7 @@ bool ConsiderPatches (const char *arg, const char *ext) } noDef = true; } - delete files; + files->Destroy(); return noDef; } @@ -2038,8 +2037,7 @@ void D_DoomMain (void) const IWADInfo *iwad_info; srand(I_MSTime()); - - atterm (DObject::StaticShutdown); + PClass::StaticInit (); atterm (C_DeinitConsole); @@ -2121,19 +2119,19 @@ void D_DoomMain (void) execFiles = new DArgs; GameConfig->AddAutoexec (execFiles, GameNames[gameinfo.gametype]); D_MultiExec (execFiles, true); - delete execFiles; + execFiles->Destroy(); // Run .cfg files at the start of the command line. - execFiles = Args.GatherFiles (NULL, ".cfg", false); + execFiles = Args->GatherFiles (NULL, ".cfg", false); D_MultiExec (execFiles, true); - delete execFiles; + execFiles->Destroy(); C_ExecCmdLineParams (); // [RH] do all +set commands on the command line - DArgs *files = Args.GatherFiles ("-file", ".wad", true); - DArgs *files1 = Args.GatherFiles (NULL, ".zip", false); - DArgs *files2 = Args.GatherFiles (NULL, ".pk3", false); - DArgs *files3 = Args.GatherFiles (NULL, ".txt", false); + DArgs *files = Args->GatherFiles ("-file", ".wad", true); + DArgs *files1 = Args->GatherFiles (NULL, ".zip", false); + DArgs *files2 = Args->GatherFiles (NULL, ".pk3", false); + DArgs *files3 = Args->GatherFiles (NULL, ".txt", false); if (files->NumArgs() > 0 || files1->NumArgs() > 0 || files2->NumArgs() > 0 || files3->NumArgs() > 0) { // Check for -file in shareware @@ -2160,10 +2158,10 @@ void D_DoomMain (void) D_AddWildFile (files3->GetArg (i)); } } - delete files; - delete files1; - delete files2; - delete files3; + files->Destroy(); + files1->Destroy(); + files2->Destroy(); + files3->Destroy(); Printf ("W_Init: Init WADfiles.\n"); Wads.InitMultipleFiles (&wadfiles); @@ -2193,18 +2191,18 @@ void D_DoomMain (void) Printf ("P_Init: Checking cmd-line parameters...\n"); flags = dmflags; - if (Args.CheckParm ("-nomonsters")) flags |= DF_NO_MONSTERS; - if (Args.CheckParm ("-respawn")) flags |= DF_MONSTERS_RESPAWN; - if (Args.CheckParm ("-fast")) flags |= DF_FAST_MONSTERS; + if (Args->CheckParm ("-nomonsters")) flags |= DF_NO_MONSTERS; + if (Args->CheckParm ("-respawn")) flags |= DF_MONSTERS_RESPAWN; + if (Args->CheckParm ("-fast")) flags |= DF_FAST_MONSTERS; - devparm = !!Args.CheckParm ("-devparm"); + devparm = !!Args->CheckParm ("-devparm"); - if (Args.CheckParm ("-altdeath")) + if (Args->CheckParm ("-altdeath")) { deathmatch = 1; flags |= DF_ITEMS_RESPAWN; } - else if (Args.CheckParm ("-deathmatch")) + else if (Args->CheckParm ("-deathmatch")) { deathmatch = 1; flags |= DF_WEAPONS_STAY | DF_ITEMS_RESPAWN; @@ -2223,27 +2221,27 @@ void D_DoomMain (void) } autostart = false; - const char *val = Args.CheckValue ("-skill"); + const char *val = Args->CheckValue ("-skill"); if (val) { gameskill = val[0] - '1'; autostart = true; } - p = Args.CheckParm ("-warp"); - if (p && p < Args.NumArgs() - 1) + p = Args->CheckParm ("-warp"); + if (p && p < Args->NumArgs() - 1) { int ep, map; if (gameinfo.flags & GI_MAPxx) { ep = 1; - map = atoi (Args.GetArg(p+1)); + map = atoi (Args->GetArg(p+1)); } else { - ep = atoi (Args.GetArg(p+1)); - map = p < Args.NumArgs() - 2 ? atoi (Args.GetArg(p+2)) : 10; + ep = atoi (Args->GetArg(p+1)); + map = p < Args->NumArgs() - 2 ? atoi (Args->GetArg(p+2)) : 10; if (map < 1 || map > 9) { map = ep; @@ -2256,19 +2254,19 @@ void D_DoomMain (void) } // [RH] Hack to handle +map - p = Args.CheckParm ("+map"); - if (p && p < Args.NumArgs()-1) + p = Args->CheckParm ("+map"); + if (p && p < Args->NumArgs()-1) { - MapData * map = P_OpenMapData(Args.GetArg (p+1)); + MapData * map = P_OpenMapData(Args->GetArg (p+1)); if (map == NULL) { - Printf ("Can't find map %s\n", Args.GetArg (p+1)); + Printf ("Can't find map %s\n", Args->GetArg (p+1)); } else { delete map; - strncpy (startmap, Args.GetArg (p+1), 8); - Args.GetArg (p)[0] = '-'; + strncpy (startmap, Args->GetArg (p+1), 8); + Args->GetArg (p)[0] = '-'; autostart = true; } } @@ -2281,7 +2279,7 @@ void D_DoomMain (void) // We do not need to support -cdrom under Unix, because all the files // that would go to c:\\zdoomdat are already stored in .zdoom inside // the user's home directory. - if (Args.CheckParm("-cdrom")) + if (Args->CheckParm("-cdrom")) { Printf (GStrings("D_CDROM")); mkdir (CDROM_DIR, 0); @@ -2293,7 +2291,7 @@ void D_DoomMain (void) UCVarValue value; static char one_hundred[] = "100"; - value.String = Args.CheckValue ("-turbo"); + value.String = Args->CheckValue ("-turbo"); if (value.String == NULL) value.String = one_hundred; else @@ -2302,7 +2300,7 @@ void D_DoomMain (void) turbo.SetGenericRepDefault (value, CVAR_String); } - v = Args.CheckValue ("-timer"); + v = Args->CheckValue ("-timer"); if (v) { double time = strtod (v, NULL); @@ -2310,7 +2308,7 @@ void D_DoomMain (void) timelimit = (float)time; } - v = Args.CheckValue ("-avg"); + v = Args->CheckValue ("-avg"); if (v) { Printf ("Austin Virtual Gaming: Levels will end after 20 minutes\n"); @@ -2420,10 +2418,10 @@ void D_DoomMain (void) } //Added by MC: - bglobal.getspawned = Args.GatherFiles ("-bots", "", false); + bglobal.getspawned = Args->GatherFiles ("-bots", "", false); if (bglobal.getspawned->NumArgs() == 0) { - delete bglobal.getspawned; + bglobal.getspawned->Destroy(); bglobal.getspawned = NULL; } else @@ -2459,13 +2457,11 @@ void D_DoomMain (void) // [RH] Run any saved commands from the command line or autoexec.cfg now. gamestate = GS_FULLCONSOLE; Net_NewMakeTic (); - DObject::BeginFrame (); DThinker::RunThinkers (); - DObject::EndFrame (); gamestate = GS_STARTUP; // start the apropriate game based on parms - v = Args.CheckValue ("-record"); + v = Args->CheckValue ("-record"); if (v) { @@ -2477,24 +2473,23 @@ void D_DoomMain (void) StartScreen = NULL; V_Init2(); - files = Args.GatherFiles ("-playdemo", ".lmp", false); + files = Args->GatherFiles ("-playdemo", ".lmp", false); if (files->NumArgs() > 0) { singledemo = true; // quit after one demo G_DeferedPlayDemo (files->GetArg (0)); - delete files; D_DoomLoop (); // never returns } - delete files; + files->Destroy(); - v = Args.CheckValue ("-timedemo"); + v = Args->CheckValue ("-timedemo"); if (v) { G_TimeDemo (v); D_DoomLoop (); // never returns } - v = Args.CheckValue ("-loadgame"); + v = Args->CheckValue ("-loadgame"); if (v) { file = v; diff --git a/src/d_net.cpp b/src/d_net.cpp index bbf79f5d..1bb32254 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -921,6 +921,8 @@ void NetUpdate (void) BYTE *cmddata; bool resendOnly; + GC::CheckGC(); + if (ticdup == 0) { return; @@ -1586,7 +1588,7 @@ void D_CheckNetGame (void) consoleplayer = doomcom.consoleplayer; - v = Args.CheckValue ("-netmode"); + v = Args->CheckValue ("-netmode"); if (v != NULL) { NetMode = atoi (v) != 0 ? NET_PacketServer : NET_PeerToPeer; @@ -1595,7 +1597,7 @@ void D_CheckNetGame (void) // [RH] Setup user info D_SetupUserInfo (); - if (Args.CheckParm ("-debugfile")) + if (Args->CheckParm ("-debugfile")) { char filename[20]; sprintf (filename,"debug%i.txt",consoleplayer); @@ -1834,12 +1836,11 @@ void TryRunTics (void) D_DoAdvanceDemo (); } if (debugfile) fprintf (debugfile, "run tic %d\n", gametic); - DObject::BeginFrame (); C_Ticker (); M_Ticker (); I_GetTime (true); G_Ticker (); - DObject::EndFrame (); + GC::CheckGC (); gametic++; NetUpdate (); // check for new console commands diff --git a/src/d_player.h b/src/d_player.h index 4da112a4..a2f13c65 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -105,8 +105,8 @@ public: int crouchsprite; int MaxHealth; int RunHealth; - AInventory *InvFirst; // first inventory item displayed on inventory bar - AInventory *InvSel; // selected inventory item + TObjPtr InvFirst; // first inventory item displayed on inventory bar + TObjPtr InvSel; // selected inventory item // [GRB] Player class properties fixed_t JumpZ; @@ -192,6 +192,7 @@ public: void Serialize (FArchive &arc); void FixPointers (const DObject *obj, DObject *replacement); + size_t PropagateMark(); void SetLogNumber (int num); void SetLogText (const char *text); diff --git a/src/decallib.cpp b/src/decallib.cpp index e157cf2b..1a0e193e 100644 --- a/src/decallib.cpp +++ b/src/decallib.cpp @@ -110,7 +110,7 @@ struct DDecalThinker : public DThinker public: DDecalThinker (DBaseDecal *decal) : DThinker (STAT_DECALTHINKER), TheDecal (decal) {} void Serialize (FArchive &arc); - DBaseDecal *TheDecal; + TObjPtr TheDecal; protected: DDecalThinker () : DThinker (STAT_DECALTHINKER) {} }; @@ -1143,6 +1143,7 @@ void DDecalFader::Tick () { TheDecal->Destroy (); // remove the decal Destroy (); // remove myself + return; } if (StartTrans == -1) { diff --git a/src/dobject.cpp b/src/dobject.cpp index ebce1083..4bd7d4a2 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -47,6 +47,7 @@ #include "r_state.h" #include "stats.h" #include "a_sharedglobal.h" +#include "dsectoreffect.h" #include "autosegs.h" @@ -343,148 +344,120 @@ CCMD (dumpclasses) Printf ("%d classes shown, %d omitted\n", shown, omitted); } -TArray DObject::Objects (TArray::NoInit); -TArray DObject::FreeIndices (TArray::NoInit); -TArray DObject::ToDestroy (TArray::NoInit); -bool DObject::Inactive; - void DObject::InPlaceConstructor (void *mem) { new ((EInPlace *)mem) DObject; } DObject::DObject () -: ObjectFlags(0), Class(0) +: Class(0), ObjectFlags(0) { - if (FreeIndices.Pop (Index)) - Objects[Index] = this; - else - Index = Objects.Push (this); + ObjectFlags = GC::CurrentWhite & OF_WhiteBits; + ObjNext = GC::Root; + GC::Root = this; } DObject::DObject (PClass *inClass) -: ObjectFlags(0), Class(inClass) +: Class(inClass), ObjectFlags(0) { - if (FreeIndices.Pop (Index)) - Objects[Index] = this; - else - Index = Objects.Push (this); + ObjectFlags = GC::CurrentWhite & OF_WhiteBits; + ObjNext = GC::Root; + GC::Root = this; } DObject::~DObject () { - if (!Inactive) + if (!(ObjectFlags & OF_Cleanup)) { - if (!(ObjectFlags & OF_MassDestruction)) - { - RemoveFromArray (); - DestroyScan (this); - } - else if (!(ObjectFlags & OF_Cleanup)) - { - // object is queued for deletion, but is not being deleted - // by the destruction process, so remove it from the - // ToDestroy array and do other necessary stuff. - unsigned int i; + DObject **probe; + PClass *type = GetClass(); - for (i = ToDestroy.Size() - 1; i-- > 0; ) + if (!(ObjectFlags & OF_YesReallyDelete)) + { + Printf ("Warning: '%s' is freed outside the GC process.\n", + type != NULL ? type->TypeName.GetChars() : "==some object=="); + } + + // Find all pointers that reference this object and NULL them. + PointerSubstitution(this, NULL); + + // Now unlink this object from the GC list. + for (probe = &GC::Root; *probe != NULL; probe = &((*probe)->ObjNext)) + { + if (*probe == this) { - if (ToDestroy[i] == this) + *probe = ObjNext; + if (&ObjNext == GC::SweepPos) { - ToDestroy[i] = NULL; + GC::SweepPos = probe; + } + break; + } + } + + // If it's gray, also unlink it from the gray list. + if (this->IsGray()) + { + for (probe = &GC::Gray; *probe != NULL; probe = &((*probe)->GCNext)) + { + if (*probe == this) + { + *probe = GCNext; break; } } - DestroyScan (this); } } } void DObject::Destroy () { - if (!Inactive) - { - if (!(ObjectFlags & OF_MassDestruction)) - { - RemoveFromArray (); - ObjectFlags |= OF_MassDestruction; - ToDestroy.Push (this); - } - } - else - delete this; + ObjectFlags |= OF_EuthanizeMe; } -void DObject::BeginFrame () +size_t DObject::PropagateMark() { - StaleCycles = 0; - StaleCount = 0; -} - -void DObject::EndFrame () -{ - clock (StaleCycles); - if (ToDestroy.Size ()) + const PClass *info = GetClass(); + const size_t *offsets = info->FlatPointers; + if (offsets == NULL) { - StaleCount += (int)ToDestroy.Size (); - DestroyScan (); - //Printf ("Destroyed %d objects\n", ToDestroy.Size()); - - DObject *obj; - while (ToDestroy.Pop (obj)) - { - if (obj) - { - obj->ObjectFlags |= OF_Cleanup; - delete obj; - } - } + const_cast(info)->BuildFlatPointers(); + offsets = info->FlatPointers; } - unclock (StaleCycles); -} - -void DObject::RemoveFromArray () -{ - if (Objects.Size() == Index + 1) + while (*offsets != ~(size_t)0) { - DObject *dummy; - Objects.Pop (dummy); - } - else if (Objects.Size() > Index) - { - Objects[Index] = NULL; - FreeIndices.Push (Index); + GC::Mark((DObject **)((BYTE *)this + *offsets)); + offsets++; } + return info->Size; } void DObject::PointerSubstitution (DObject *old, DObject *notOld) { - unsigned int i, highest; - highest = Objects.Size (); + DObject *probe; + int i; - for (i = 0; i <= highest; i++) + // Go through all objects. + for (probe = GC::Root; probe != NULL; probe = probe->ObjNext) { - DObject *current = i < highest ? Objects[i] : &bglobal; - if (current) + const PClass *info = probe->GetClass(); + const size_t *offsets = info->FlatPointers; + if (offsets == NULL) { - const PClass *info = current->GetClass(); - const size_t *offsets = info->FlatPointers; - if (offsets == NULL) + const_cast(info)->BuildFlatPointers(); + offsets = info->FlatPointers; + } + while (*offsets != ~(size_t)0) + { + if (*(DObject **)((BYTE *)probe + *offsets) == old) { - const_cast(info)->BuildFlatPointers(); - offsets = info->FlatPointers; - } - while (*offsets != ~(size_t)0) - { - if (*(DObject **)((BYTE *)current + *offsets) == old) - { - *(DObject **)((BYTE *)current + *offsets) = notOld; - } - offsets++; + *(DObject **)((BYTE *)probe + *offsets) = notOld; } + offsets++; } } + // Go through the bodyque. for (i = 0; i < BODYQUESIZE; ++i) { if (bodyque[i] == old) @@ -493,125 +466,32 @@ void DObject::PointerSubstitution (DObject *old, DObject *notOld) } } - // This is an ugly hack, but it's the best I can do for now. + // Go through players. for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) players[i].FixPointers (old, notOld); } + // Go through sectors. if (sectors != NULL) { - for (i = 0; i < (unsigned int)numsectors; ++i) + for (i = 0; i < numsectors; ++i) { - if (sectors[i].SoundTarget == old) - { - sectors[i].SoundTarget = static_cast(notOld); - } - if (sectors[i].CeilingSkyBox == old) - { - sectors[i].CeilingSkyBox = static_cast(notOld); - } - if (sectors[i].FloorSkyBox == old) - { - sectors[i].FloorSkyBox = static_cast(notOld); - } +#define SECTOR_CHECK(f,t) \ + if (sectors[i].f == static_cast(old)) { sectors[i].f = static_cast(notOld); } + SECTOR_CHECK( SoundTarget, AActor ); + SECTOR_CHECK( CeilingSkyBox, ASkyViewpoint ); + SECTOR_CHECK( FloorSkyBox, ASkyViewpoint ); + SECTOR_CHECK( SecActTarget, ASectorAction ); + SECTOR_CHECK( floordata, DSectorEffect ); + SECTOR_CHECK( ceilingdata, DSectorEffect ); + SECTOR_CHECK( lightingdata, DSectorEffect ); +#undef SECTOR_CHECK } } } -// Search for references to a single object and NULL them. -// It should not be listed in ToDestroy. -void DObject::DestroyScan (DObject *obj) -{ - PointerSubstitution (obj, NULL); -} - -// Search for references to all objects scheduled for -// destruction and NULL them. -void DObject::DestroyScan () -{ - unsigned int i, highest; - int j, destroycount; - DObject **destroybase; - destroycount = (int)ToDestroy.Size (); - if (destroycount == 0) - return; - destroybase = &ToDestroy[0] + destroycount; - destroycount = -destroycount; - highest = Objects.Size (); - - for (i = 0; i <= highest; i++) - { - DObject *current = i < highest ? Objects[i] : &bglobal; - if (current) - { - const PClass *info = current->GetClass(); - const size_t *offsets = info->FlatPointers; - if (offsets == NULL) - { - const_cast(info)->BuildFlatPointers(); - offsets = info->FlatPointers; - } - while (*offsets != ~(size_t)0) - { - j = destroycount; - do - { - if (*(DObject **)((BYTE *)current + *offsets) == *(destroybase + j)) - { - *(DObject **)((BYTE *)current + *offsets) = NULL; - } - } while (++j); - offsets++; - } - } - } - - j = destroycount; - do - { - for (i = 0; i < BODYQUESIZE; ++i) - { - if (bodyque[i] == *(destroybase + j)) - { - bodyque[i] = NULL; - } - } - - } while (++j); - - // This is an ugly hack, but it's the best I can do for now. - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - j = destroycount; - do - { - players[i].FixPointers (*(destroybase + j), NULL); - } while (++j); - } - } - - for (i = 0; i < (unsigned int)numsectors; ++i) - { - j = destroycount; - do - { - if (sectors[i].SoundTarget == *(destroybase + j)) - { - sectors[i].SoundTarget = NULL; - } - } while (++j); - } -} - -void DObject::StaticShutdown () -{ - Inactive = true; -} - void DObject::Serialize (FArchive &arc) { ObjectFlags |= OF_SerialSuccess; diff --git a/src/dobject.h b/src/dobject.h index f18f1e03..b0ef5d70 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -2,7 +2,7 @@ ** dobject.h ** **--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit +** Copyright 1998-2008 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -48,7 +48,6 @@ class FArchive; class DObject; class DArgs; -class DBoundingBox; class DCanvas; class DConsoleCommand; class DConsoleAlias; @@ -211,12 +210,205 @@ private: \ enum EObjectFlags { - OF_MassDestruction = 0x00000001, // Object is queued for deletion - OF_Cleanup = 0x00000002, // Object is being deconstructed as a result of a queued deletion - OF_JustSpawned = 0x00000004, // Thinker was spawned this tic - OF_SerialSuccess = 0x10000000 // For debugging Serialize() calls + // GC flags + OF_White0 = 1 << 0, // Object is white (type 0) + OF_White1 = 1 << 1, // Object is white (type 1) + OF_Black = 1 << 2, // Object is black + OF_Fixed = 1 << 3, // Object is fixed (should not be collected) + OF_Rooted = 1 << 4, // Object is soft-rooted + OF_EuthanizeMe = 1 << 5, // Object wants to die + OF_Cleanup = 1 << 6, // Object is now being deleted by the collector + OF_YesReallyDelete = 1 << 7, // Object is being deleted outside the collector, and this is okay, so don't print a warning + + OF_WhiteBits = OF_White0 | OF_White1, + OF_MarkBits = OF_WhiteBits | OF_Black, + + // Other flags + OF_JustSpawned = 1 << 8, // Thinker was spawned this tic + OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls }; +template class TObjPtr; + +namespace GC +{ + enum EGCState + { + GCS_Pause, + GCS_Propagate, + GCS_Sweep, + GCS_Finalize + }; + + // Number of bytes currently allocated through M_Malloc/M_Realloc. + extern size_t AllocBytes; + + // Amount of memory to allocate before triggering a collection. + extern size_t Threshold; + + // List of gray objects. + extern DObject *Gray; + + // List of every object. + extern DObject *Root; + + // Current white value for potentially-live objects. + extern DWORD CurrentWhite; + + // Current collector state. + extern EGCState State; + + // Position of GC sweep in the list of objects. + extern DObject **SweepPos; + + // Size of GC pause. + extern int Pause; + + // Size of GC steps. + extern int StepMul; + + // Current white value for known-dead objects. + static inline DWORD OtherWhite() + { + return CurrentWhite ^ OF_WhiteBits; + } + + // Frees all objects, whether they're dead or not. + void FreeAll(); + + // Does one collection step. + void Step(); + + // Does a complete collection. + void FullGC(); + + // Handles the grunt work for a write barrier. + void Barrier(DObject *pointing, DObject *pointed); + + // Handles a write barrier. + static inline void WriteBarrier(DObject *pointing, DObject *pointed); + + // Handles a write barrier for a pointer that isn't inside an object. + static inline void WriteBarrier(DObject *pointed); + + // Handles a read barrier. + template inline T *ReadBarrier(T *&obj) + { + if (obj == NULL || !(obj->ObjectFlags & OF_EuthanizeMe)) + { + return obj; + } + return obj = NULL; + } + + // Check if it's time to collect, and do a collection step if it is. + static inline void CheckGC() + { + if (AllocBytes >= Threshold) + Step(); + } + + // Marks a white object gray. If the object wants to die, the pointer + // is NULLed instead. + void Mark(DObject **obj); + + // Soft-roots an object. + void AddSoftRoot(DObject *obj); + + // Unroots an object. + void DelSoftRoot(DObject *obj); + + template void Mark(T *&obj) { Mark((DObject **)&obj); } + template void Mark(TObjPtr &obj); +} + +// A template class to help with handling read barriers. It does not +// handle write barriers, because those can be handled more efficiently +// with knowledge of the object that holds the pointer. +template +class TObjPtr +{ + T *p; +public: + TObjPtr() throw() + { + } + TObjPtr(T *q) throw() + : p(q) + { + } + TObjPtr(const TObjPtr &q) throw() + : p(q.p) + { + } + T *operator=(T *q) throw() + { + return p = q; + // The caller must now perform a write barrier. + } + operator T*() throw() + { + return GC::ReadBarrier(p); + } + T &operator*() + { + T *q = GC::ReadBarrier(p); + assert(q != NULL); + return *q; + } + T **operator&() throw() + { + // Does not perform a read barrier. The only real use for this is with + // the DECLARE_POINTER macro, where a read barrier would be a very bad + // thing. + return &p; + } + T *operator->() throw() + { + return GC::ReadBarrier(p); + } + bool operator<(T *u) throw() + { + return GC::ReadBarrier(p) < u; + } + bool operator<=(T *u) throw() + { + return GC::ReadBarrier(p) <= u; + } + bool operator>(T *u) throw() + { + return GC::ReadBarrier(p) > u; + } + bool operator>=(T *u) throw() + { + return GC::ReadBarrier(p) >= u; + } + bool operator!=(T *u) throw() + { + return GC::ReadBarrier(p) != u; + } + bool operator==(T *u) throw() + { + return GC::ReadBarrier(p) == u; + } + + template friend inline FArchive &operator<<(FArchive &arc, TObjPtr &o); +}; + +template inline FArchive &operator<<(FArchive &arc, TObjPtr &o) +{ + return arc << o.p; +} + +// Use barrier_cast instead of static_cast when you need to cast +// the contents of a TObjPtr to a related type. +template inline T barrier_cast(TObjPtr &o) +{ + return static_cast(static_cast(o)); +} + +template void GC::Mark(TObjPtr &obj) { GC::Mark((DObject **)&obj); } + class DObject { public: @@ -227,12 +419,13 @@ public: private: typedef DObject ThisClass; - // Per-instance variables. There are three. -public: - DWORD ObjectFlags; // Flags for this object + // Per-instance variables. There are four. private: PClass *Class; // This object's type - unsigned int Index; // This object's index in the global object table +public: + DObject *ObjNext; // Keep track of all allocated objects + DObject *GCNext; // Next object in this collection list + DWORD ObjectFlags; // Flags for this object public: DObject (); @@ -250,16 +443,11 @@ public: virtual void Destroy (); - static void BeginFrame (); - static void EndFrame (); - // If you need to replace one object with another and want to // change any pointers from the old object to the new object, // use this method. static void PointerSubstitution (DObject *old, DObject *notOld); - static void StaticShutdown (); - PClass *GetClass() const { if (Class == NULL) @@ -286,6 +474,60 @@ public: M_Free(mem); } + // GC fiddling + + // An object is white if either white bit is set. + bool IsWhite() const + { + return !!(ObjectFlags & OF_WhiteBits); + } + + bool IsBlack() const + { + return !!(ObjectFlags & OF_Black); + } + + // An object is gray if it isn't white or black. + bool IsGray() const + { + return !(ObjectFlags & OF_MarkBits); + } + + // An object is dead if it's the other white. + bool IsDead() const + { + return !!(ObjectFlags & GC::OtherWhite() & OF_WhiteBits); + } + + void ChangeWhite() + { + ObjectFlags ^= OF_WhiteBits; + } + + void MakeWhite() + { + ObjectFlags = (ObjectFlags & ~OF_MarkBits) | (GC::CurrentWhite & OF_WhiteBits); + } + + void White2Gray() + { + ObjectFlags &= ~OF_WhiteBits; + } + + void Black2Gray() + { + ObjectFlags &= ~OF_Black; + } + + void Gray2Black() + { + ObjectFlags |= OF_Black; + } + + // Marks all objects pointed to by this one. Returns the (approximate) + // amount of memory used by this object. + virtual size_t PropagateMark(); + protected: // This form of placement new and delete is for use *only* by PClass's // CreateNew() method. Do not use them for some other purpose. @@ -296,22 +538,26 @@ protected: void operator delete (void *mem, EInPlace *) { - free (mem); + M_Free (mem); } - -private: - static TArray Objects; - static TArray FreeIndices; - static TArray ToDestroy; - - static void DestroyScan (DObject *obj); - static void DestroyScan (); - - void RemoveFromArray (); - - static bool Inactive; }; +static inline void GC::WriteBarrier(DObject *pointing, DObject *pointed) +{ + if (pointed != NULL && pointed->IsWhite() && pointing->IsBlack()) + { + Barrier(pointing, pointed); + } +} + +static inline void GC::WriteBarrier(DObject *pointed) +{ + if (pointed != NULL && State == GCS_Propagate && pointed->IsWhite()) + { + Barrier(NULL, pointed); + } +} + #include "dobjtype.h" inline bool DObject::IsKindOf (const PClass *base) const diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp new file mode 100644 index 00000000..ec0ede4b --- /dev/null +++ b/src/dobjgc.cpp @@ -0,0 +1,623 @@ +/* +** dobjgc.cpp +** The garbage collector. Based largely on Lua's. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ +/****************************************************************************** +* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + +// HEADER FILES ------------------------------------------------------------ + +#include "dobject.h" +#include "templates.h" +#include "b_bot.h" +#include "p_local.h" +#include "g_game.h" +#include "r_data.h" +#include "a_sharedglobal.h" +#include "sbar.h" +#include "stats.h" +#include "c_dispatch.h" +#include "p_acs.h" + +// MACROS ------------------------------------------------------------------ + +/* +@@ DEFAULT_GCPAUSE defines the default pause between garbage-collector cycles +@* as a percentage. +** CHANGE it if you want the GC to run faster or slower (higher values +** mean larger pauses which mean slower collection.) You can also change +** this value dynamically. +*/ +#define DEFAULT_GCPAUSE 150 // 150% (wait for memory to increase by half before next GC) + +/* +@@ DEFAULT_GCMUL defines the default speed of garbage collection relative to +@* memory allocation as a percentage. +** CHANGE it if you want to change the granularity of the garbage +** collection. (Higher values mean coarser collections. 0 represents +** infinity, where each step performs a full collection.) You can also +** change this value dynamically. +*/ +#define DEFAULT_GCMUL 400 // GC runs 'quadruple the speed' of memory allocation + + +#define GCSTEPSIZE 1024u +#define GCSWEEPMAX 40 +#define GCSWEEPCOST 10 +#define GCFINALIZECOST 100 + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +namespace GC +{ +size_t AllocBytes; +size_t Threshold; +size_t Estimate; +DObject *Gray; +DObject *Root; +DObject *SoftRoots; +DObject **SweepPos; +DWORD CurrentWhite = OF_White0 | OF_Fixed; +EGCState State = GCS_Pause; +int Pause = DEFAULT_GCPAUSE; +int StepMul = DEFAULT_GCMUL; +int StepCount; +size_t Dept; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// SetThreshold +// +// Sets the new threshold after a collection is finished. +// +//========================================================================== + +void SetThreshold() +{ + Threshold = (Estimate / 100) * Pause; +} + +//========================================================================== +// +// PropagateMark +// +// Marks the top-most gray object black and marks all objects it points to +// gray. +// +//========================================================================== + +size_t PropagateMark() +{ + DObject *obj = Gray; + assert(obj->IsGray()); + obj->Gray2Black(); + Gray = obj->GCNext; + return obj->PropagateMark(); +} + +//========================================================================== +// +// PropagateAll +// +// Empties the gray list by propagating every single object in it. +// +//========================================================================== + +static size_t PropagateAll() +{ + size_t m = 0; + while (Gray != NULL) + { + m += PropagateMark(); + } + return m; +} + +//========================================================================== +// +// SweepList +// +// Runs a limited sweep on a list, returning the location where to resume +// the sweep at next time. +// +//========================================================================== + +static DObject **SweepList(DObject **p, size_t count) +{ + static int scount; + DObject *curr; + int deadmask = OtherWhite(); + + while ((curr = *p) != NULL && count-- > 0) + { + if ((curr->ObjectFlags ^ OF_WhiteBits) & deadmask) // not dead? + { + assert(!curr->IsDead() || (curr->ObjectFlags & OF_Fixed)); + curr->MakeWhite(); // make it white (for next cycle) + p = &curr->ObjNext; + } + else // must erase 'curr' + { + assert(curr->IsDead()); + *p = curr->ObjNext; + if (!(curr->ObjectFlags & OF_EuthanizeMe)) + { // The object must be destroyed before it can be finalized. + assert(!curr->IsKindOf(RUNTIME_CLASS(DThinker))); + curr->Destroy(); + } + curr->ObjectFlags |= OF_Cleanup; + delete curr; + } + } + return p; +} + +//========================================================================== +// +// Mark +// +// Mark a single object gray. +// +//========================================================================== + +void Mark(DObject **obj) +{ + DObject *lobj = *obj; + if (lobj != NULL) + { + if (lobj->ObjectFlags & OF_EuthanizeMe) + { + *obj = NULL; + } + else if (lobj->IsWhite()) + { + lobj->White2Gray(); + lobj->GCNext = Gray; + Gray = lobj; + } + } +} + +//========================================================================== +// +// MarkRoot +// +// Mark the root set of objects. +// +//========================================================================== + +static void MarkRoot() +{ + int i; + + Gray = NULL; + Mark(Args); + Mark(screen); + Mark(StatusBar); + DThinker::MarkRoots(); + Mark(DACSThinker::ActiveThinker); + for (i = 0; i < BODYQUESIZE; ++i) + { + Mark(bodyque[i]); + } + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + players[i].PropagateMark(); + } + if (sectors != NULL) + { + for (i = 0; i < numsectors; ++i) + { + Mark(sectors[i].SoundTarget); + Mark(sectors[i].CeilingSkyBox); + Mark(sectors[i].FloorSkyBox); + Mark(sectors[i].SecActTarget); + Mark(sectors[i].floordata); + Mark(sectors[i].ceilingdata); + Mark(sectors[i].lightingdata); + } + } + { // Silly bots + DObject *foo = &bglobal; + Mark(foo); + } + // Add soft roots + if (SoftRoots != NULL) + { + DObject **probe = &SoftRoots->ObjNext; + while (*probe != NULL) + { + DObject *soft = *probe; + probe = &soft->ObjNext; + if ((soft->ObjectFlags & (OF_Rooted | OF_EuthanizeMe)) == OF_Rooted) + { + Mark(soft); + } + } + } + State = GCS_Propagate; + StepCount = 0; +} + +//========================================================================== +// +// Atomic +// +// If their were any propagations that needed to be done atomicly, they +// would go here. It also sets things up for the sweep state. +// +//========================================================================== + +static void Atomic() +{ + // Flip current white + CurrentWhite = OtherWhite(); + SweepPos = &Root; + State = GCS_Sweep; + Estimate = AllocBytes; +} + +//========================================================================== +// +// SingleStep +// +// Performs one step of the collector. +// +//========================================================================== + +static size_t SingleStep() +{ + switch (State) + { + case GCS_Pause: + MarkRoot(); // Start a new collection + return 0; + + case GCS_Propagate: + if (Gray != NULL) + { + return PropagateMark(); + } + else + { // no more gray objects + Atomic(); // finish mark phase + return 0; + } + + case GCS_Sweep: { + size_t old = AllocBytes; + SweepPos = SweepList(SweepPos, GCSWEEPMAX); + if (*SweepPos == NULL) + { // Nothing more to sweep? + State = GCS_Finalize; + } + assert(old >= AllocBytes); + Estimate -= old - AllocBytes; + return GCSWEEPMAX * GCSWEEPCOST; + } + + case GCS_Finalize: + State = GCS_Pause; // end collection + Dept = 0; + return 0; + + default: + assert(0); + return 0; + } +} + +//========================================================================== +// +// Step +// +// Performs enough single steps to cover GCSTEPSIZE * StepMul% bytes of +// memory. +// +//========================================================================== + +void Step() +{ + size_t lim = (GCSTEPSIZE/100) * StepMul; + size_t olim; + if (lim == 0) + { + lim = (~(size_t)0) / 2; // no limit + } + Dept += AllocBytes - Threshold; + do + { + olim = lim; + lim -= SingleStep(); + } while (olim > lim && State != GCS_Pause); + if (State != GCS_Pause) + { + if (Dept < GCSTEPSIZE) + { + Threshold = AllocBytes + GCSTEPSIZE; // - lim/StepMul + } + else + { + Dept -= GCSTEPSIZE; + Threshold = AllocBytes; + } + } + else + { + assert(AllocBytes >= Estimate); + SetThreshold(); + } + StepCount++; +} + +//========================================================================== +// +// FullGC +// +// Collects everything in one fell swoop. +// +//========================================================================== + +void FullGC() +{ + if (State <= GCS_Propagate) + { + // Reset sweep mark to sweep all elements (returning them to white) + SweepPos = &Root; + // Reset other collector lists + Gray = NULL; + State = GCS_Sweep; + } + // Finish any pending sweep phase + while (State != GCS_Finalize) + { + SingleStep(); + } + MarkRoot(); + while (State != GCS_Pause) + { + SingleStep(); + } + SetThreshold(); +} + +//========================================================================== +// +// Barrier +// +// Implements a write barrier to maintain the invariant that a black node +// never points to a white node by making the node pointed at gray. +// +//========================================================================== + +void Barrier(DObject *pointing, DObject *pointed) +{ + assert(pointing == NULL || (pointing->IsBlack() && !pointing->IsDead())); + assert(pointed->IsWhite() && !pointed->IsDead()); + assert(State != GCS_Finalize && State != GCS_Pause); + // The invariant only needs to be maintained in the propagate state. + if (State == GCS_Propagate) + { + pointed->White2Gray(); + pointed->GCNext = Gray; + Gray = pointed; + } + // In other states, we can mark the pointing object white so this + // barrier won't be triggered again, saving a few cycles in the future. + else if (pointing != NULL) + { + pointing->MakeWhite(); + } +} + +//========================================================================== +// +// AddSoftRoot +// +// Marks an object as a soft root. A soft root behaves exactly like a root +// in MarkRoot, except it can be added at run-time. +// +//========================================================================== + +void AddSoftRoot(DObject *obj) +{ + DObject **probe; + + // Are there any soft roots yet? + if (SoftRoots == NULL) + { + // Create a new object to root the soft roots off of, and stick + // it at the end of the object list, so we know that anything + // before it is not a soft root. + SoftRoots = new DObject; + SoftRoots->ObjectFlags |= OF_Fixed; + probe = &Root; + while (*probe != NULL) + { + probe = &(*probe)->ObjNext; + } + Root = SoftRoots->ObjNext; + SoftRoots->ObjNext = NULL; + *probe = SoftRoots; + } + // Mark this object as rooted and move it after the SoftRoots marker. + probe = &Root; + while (*probe != NULL && *probe != obj) + { + probe = &(*probe)->ObjNext; + } + *probe = (*probe)->ObjNext; + obj->ObjNext = SoftRoots->ObjNext; + SoftRoots->ObjNext = obj; + obj->ObjectFlags |= OF_Rooted; + WriteBarrier(obj); +} + +//========================================================================== +// +// DelSoftRoot +// +// Unroots an object so that it must be reachable or it will get collected. +// +//========================================================================== + +void DelSoftRoot(DObject *obj) +{ + DObject **probe; + + if (!(obj->ObjectFlags & OF_Rooted)) + { // Not rooted, so nothing to do. + return; + } + obj->ObjectFlags &= ~OF_Rooted; + // Move object out of the soft roots part of the list. + probe = &SoftRoots; + while (*probe != NULL && *probe != obj) + { + probe = &(*probe)->ObjNext; + } + if (*probe == obj) + { + *probe = obj->ObjNext; + obj->ObjNext = Root; + Root = obj; + } +} + +} + +ADD_STAT(gc) +{ + static const char *StateStrings[] = { + " Pause ", + "Propagate", + " Sweep ", + "Finalize " }; + FString out; + out.Format("[%s] Alloc:%6uK Thresh:%6uK Est:%6uK Steps: %d", + StateStrings[GC::State], + (GC::AllocBytes + 1023) >> 10, + (GC::Threshold + 1023) >> 10, + (GC::Estimate + 1023) >> 10, + GC::StepCount); + if (GC::State != GC::GCS_Pause) + { + out.AppendFormat(" %uK", (GC::Dept + 1023) >> 10); + } + return out; +} + +//========================================================================== +// +// CCMD gc +// +// Controls various aspects of the collector. +// +//========================================================================== + +CCMD(gc) +{ + if (argv.argc() == 1) + { + Printf ("Usage: gc stop|now|full|pause [size]|stepmul [size]\n"); + return; + } + if (stricmp(argv[1], "stop") == 0) + { + GC::Threshold = ~(size_t)0 - 2; + } + else if (stricmp(argv[1], "now") == 0) + { + GC::Threshold = GC::AllocBytes; + } + else if (stricmp(argv[1], "full") == 0) + { + GC::FullGC(); + } + else if (stricmp(argv[1], "pause") == 0) + { + if (argv.argc() == 2) + { + Printf ("Current GC pause is %d\n", GC::Pause); + } + else + { + GC::Pause = MAX(1,atoi(argv[2])); + } + } + else if (stricmp(argv[1], "stepmul") == 0) + { + if (argv.argc() == 2) + { + Printf ("Current GC stepmul is %d\n", GC::StepMul); + } + else + { + GC::StepMul = MAX(100, atoi(argv[2])); + } + } +} diff --git a/src/dsectoreffect.cpp b/src/dsectoreffect.cpp index 5aa97540..3a4a4c43 100644 --- a/src/dsectoreffect.cpp +++ b/src/dsectoreffect.cpp @@ -33,7 +33,7 @@ DSectorEffect::DSectorEffect () m_Sector = NULL; } -DSectorEffect::~DSectorEffect () +void DSectorEffect::Destroy() { if (m_Sector) { @@ -52,6 +52,7 @@ DSectorEffect::~DSectorEffect () m_Sector->lightingdata = NULL; } } + Super::Destroy(); } DSectorEffect::DSectorEffect (sector_t *sector) diff --git a/src/dsectoreffect.h b/src/dsectoreffect.h index 1c35bbd7..64e1e658 100644 --- a/src/dsectoreffect.h +++ b/src/dsectoreffect.h @@ -10,9 +10,9 @@ class DSectorEffect : public DThinker DECLARE_CLASS (DSectorEffect, DThinker) public: DSectorEffect (sector_t *sector); - ~DSectorEffect (); void Serialize (FArchive &arc); + void Destroy(); sector_t *GetSector() const { return m_Sector; } diff --git a/src/dthinker.cpp b/src/dthinker.cpp index fa6bfe05..bd8990bb 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -155,6 +155,14 @@ DThinker::DThinker (int statnum) throw() { statnum = MAX_STATNUM; } + if (FreshThinkers[statnum].TailPred->Pred != NULL) + { + GC::WriteBarrier(static_cast(FreshThinkers[statnum].Tail, this)); + } + else + { + GC::WriteBarrier(this); + } FreshThinkers[statnum].AddTail (this); } @@ -179,6 +187,16 @@ void DThinker::Destroy () Super::Destroy (); } +void DThinker::Remove() +{ + if (Pred->Pred != NULL && Succ->Succ != NULL) + { + GC::WriteBarrier(static_cast(Pred), static_cast(Succ)); + GC::WriteBarrier(static_cast(Succ), static_cast(Pred)); + } + Node::Remove(); +} + void DThinker::PostBeginPlay () { } @@ -205,6 +223,8 @@ DThinker *DThinker::FirstThinker (int statnum) void DThinker::ChangeStatNum (int statnum) { + List *list; + if ((unsigned)statnum > MAX_STATNUM) { statnum = MAX_STATNUM; @@ -212,12 +232,44 @@ void DThinker::ChangeStatNum (int statnum) Remove (); if ((ObjectFlags & OF_JustSpawned) && statnum >= STAT_FIRST_THINKING) { - FreshThinkers[statnum].AddTail (this); + list = &FreshThinkers[statnum]; } else { - Thinkers[statnum].AddTail (this); + list = &Thinkers[statnum]; } + if (list->TailPred->Pred != NULL) + { + GC::WriteBarrier(static_cast(list->Tail, this)); + } + else + { + GC::WriteBarrier(this); + } + list->AddTail(this); +} + +// Mark the first thinker of each list +void DThinker::MarkRoots() +{ + for (int i = 0; i <= MAX_STATNUM; ++i) + { + DThinker *t = static_cast(Thinkers[i].Head); + GC::Mark(t); + t = static_cast(FreshThinkers[i].Head); + GC::Mark(t); + } +} + +size_t DThinker::PropagateMark() +{ + // Mark the next thinker in my list + if (Succ != NULL && Succ->Succ != NULL) + { + DThinker *t = static_cast(Succ); + GC::Mark(t); + } + return Super::PropagateMark(); } // Destroy every thinker @@ -225,7 +277,6 @@ void DThinker::DestroyAllThinkers () { int i; - DObject::BeginFrame (); for (i = 0; i <= MAX_STATNUM; i++) { if (i != STAT_TRAVELLING) @@ -234,7 +285,7 @@ void DThinker::DestroyAllThinkers () DestroyThinkersInList (FreshThinkers[i].Head); } } - DObject::EndFrame (); + GC::FullGC (); } // Destroy all thinkers except for player-controlled actors @@ -244,7 +295,6 @@ void DThinker::DestroyMostThinkers () { int i; - DObject::BeginFrame (); for (i = 0; i <= MAX_STATNUM; i++) { if (i != STAT_TRAVELLING) @@ -253,7 +303,7 @@ void DThinker::DestroyMostThinkers () DestroyMostThinkersInList (FreshThinkers[i], i); } } - DObject::EndFrame (); + GC::FullGC (); } void DThinker::DestroyThinkersInList (Node *node) @@ -327,6 +377,14 @@ int DThinker::TickThinkers (List *list, List *dest) if (dest != NULL) { // Move thinker from this list to the destination list node->Remove (); + if (dest->TailPred->Pred != NULL) + { + GC::WriteBarrier(static_cast(dest->Tail, thinker)); + } + else + { + GC::WriteBarrier(thinker); + } dest->AddTail (node); } thinker->PostBeginPlay (); @@ -336,7 +394,7 @@ int DThinker::TickThinkers (List *list, List *dest) I_Error ("There is a thinker in the fresh list that has already ticked.\n"); } - if (!(thinker->ObjectFlags & OF_MassDestruction)) + if (!(thinker->ObjectFlags & OF_EuthanizeMe)) { // Only tick thinkers not scheduled for destruction thinker->Tick (); } diff --git a/src/dthinker.h b/src/dthinker.h index f7905c5b..9cfd9791 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -49,13 +49,14 @@ class FThinkerIterator; enum { MAX_STATNUM = 127 }; // Doubly linked list of thinkers -class DThinker : public DObject, public Node +class DThinker : public DObject, protected Node { DECLARE_CLASS (DThinker, DObject) public: DThinker (int statnum = MAX_STATNUM) throw(); void Destroy (); + size_t PropagateMark(); virtual ~DThinker (); virtual void Tick (); virtual void PostBeginPlay (); // Called just before the first tick @@ -67,6 +68,7 @@ public: static void DestroyAllThinkers (); static void DestroyMostThinkers (); static void SerializeAll (FArchive &arc, bool keepPlayers); + static void MarkRoots(); static DThinker *FirstThinker (int statnum); @@ -75,6 +77,7 @@ private: static void DestroyMostThinkersInList (List &list, int stat); static int TickThinkers (List *list, List *dest); // Returns: # of thinkers ticked static void SaveList(FArchive &arc, Node *node); + void Remove(); static List Thinkers[MAX_STATNUM+1]; // Current thinkers static List FreshThinkers[MAX_STATNUM+1]; // Newly created thinkers diff --git a/src/farchive.cpp b/src/farchive.cpp index 7d17c8d4..2087b7be 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -1004,9 +1004,17 @@ FArchive &FArchive::SerializePointer (void *ptrbase, BYTE **ptr, DWORD elemSize) FArchive &FArchive::SerializeObject (DObject *&object, PClass *type) { if (IsStoring ()) + { + if (object != (DObject*)~0) + { + GC::ReadBarrier(object); + } return WriteObject (object); + } else + { return ReadObject (object, type); + } } FArchive &FArchive::WriteObject (DObject *obj) diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 33f482ea..b370fb57 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -1925,6 +1925,11 @@ void FParser::SF_FadeLight(void) } } +//========================================================================== +// +// +// +//========================================================================== void FParser::SF_FloorTexture(void) { int tagnum, secnum; @@ -2066,12 +2071,13 @@ void FParser::SF_StartSkill(void) I_Error("startskill is not supported by this implementation!\n"); } -////////////////////////////////////////////////////////////////////////// +//========================================================================== // // Doors // - // opendoor(sectag, [delay], [speed]) +// +//========================================================================== void FParser::SF_OpenDoor(void) { @@ -2204,13 +2210,13 @@ inline line_t * P_FindLine(int tag,int * searchPosition) return *searchPosition>=0? &lines[*searchPosition]:NULL; } -/* -FParser::SF_SetLineBlocking() - - Sets a line blocking or unblocking - - setlineblocking(tag, [1|0]); -*/ +//========================================================================== +// +// FParser::SF_SetLineBlocking() +// +// Sets a line blocking or unblocking +// +//========================================================================== void FParser::SF_SetLineBlocking(void) { line_t *line; @@ -2236,12 +2242,10 @@ void FParser::SF_SetLineBlocking(void) //========================================================================== // -// +// similar, but monster blocking // //========================================================================== -// similar, but monster blocking - void FParser::SF_SetLineMonsterBlocking(void) { line_t *line; @@ -2261,17 +2265,19 @@ void FParser::SF_SetLineMonsterBlocking(void) } } -/* -FParser::SF_SetLineTexture - #2 in a not-so-long line of ACS-inspired functions - This one is *much* needed, IMO - - Eternity: setlinetexture(tag, side, position, texture) - Legacy: setlinetexture(tag, texture, side, sections) - -*/ +//========================================================================== +// +//FParser::SF_SetLineTexture +// +// #2 in a not-so-long line of ACS-inspired functions +// This one is *much* needed, IMO +// +// Eternity: setlinetexture(tag, side, position, texture) +// Legacy: setlinetexture(tag, texture, side, sections) +// +//========================================================================== void FParser::SF_SetLineTexture(void) { @@ -2357,11 +2363,10 @@ void FParser::SF_SetLineTexture(void) //========================================================================== // -// +// SoM: Max, Min, Abs math functions. // //========================================================================== -// SoM: Max, Min, Abs math functions. void FParser::SF_Max(void) { fixed_t n1, n2; @@ -2417,13 +2422,15 @@ void FParser::SF_Abs(void) } } -/* -FParser::SF_Gameskill, FParser::SF_Gamemode - - Access functions are more elegant for these than variables, - especially for the game mode, which doesn't exist as a numeric - variable already. -*/ +//========================================================================== +// +// FParser::SF_Gameskill, FParser::SF_Gamemode +// +// Access functions are more elegant for these than variables, +// especially for the game mode, which doesn't exist as a numeric +// variable already. +// +//========================================================================== void FParser::SF_Gameskill(void) { @@ -2452,13 +2459,16 @@ void FParser::SF_Gamemode(void) t_return.value.i = 2; // deathmatch } -/* -FParser::SF_IsPlayerObj() +//========================================================================== +// +// FParser::SF_IsPlayerObj() +// +// A function suggested by SoM to help the script coder prevent +// exceptions related to calling player functions on non-player +// objects. +// +//========================================================================== - A function suggested by SoM to help the script coder prevent - exceptions related to calling player functions on non-player - objects. -*/ void FParser::SF_IsPlayerObj(void) { AActor *mo; @@ -2476,6 +2486,11 @@ void FParser::SF_IsPlayerObj(void) //============================================================================ // +// Inventory stuff - mostly new to GZDoom +// +// all the original functions are still supported but they have not +// been expanded from their original and are limited as a result +// // Since FraggleScript is rather hard coded to the original inventory // handling of Doom this is rather messy. // @@ -2614,14 +2629,14 @@ static int FS_CheckInventory (AActor *activator, const char *type) //========================================================================== // -// +// This function is just kept for backwards compatibility +// and intentionally limited to thr standard keys! +// Use Give/Take/CheckInventory instead! // //========================================================================== void FParser::SF_PlayerKeys(void) { - // This function is just kept for backwards compatibility and intentionally limited to thr standard keys! - // Use Give/Take/CheckInventory instead! static const char * const DoomKeys[]={"BlueCard", "YellowCard", "RedCard", "BlueSkull", "YellowSkull", "RedSkull"}; int playernum, keynum, givetake; const char * keyname; @@ -2658,14 +2673,13 @@ void FParser::SF_PlayerKeys(void) //========================================================================== // -// +// This function is just kept for backwards compatibility and intentionally limited! +// Use Give/Take/CheckInventory instead! // //========================================================================== void FParser::SF_PlayerAmmo(void) { - // This function is just kept for backwards compatibility and intentionally limited! - // Use Give/Take/CheckInventory instead! int playernum, amount; const PClass * ammotype; @@ -2751,7 +2765,9 @@ void FParser::SF_MaxPlayerAmmo() //========================================================================== // -// +// This function is just kept for backwards compatibility and +// intentionally limited to the standard weapons! +// Use Give/Take/CheckInventory instead! // //========================================================================== @@ -2762,8 +2778,6 @@ void FParser::SF_PlayerWeapon() "PlasmaRifle", "BFG9000", "Chainsaw", "SuperShotgun" }; - // This function is just kept for backwards compatibility and intentionally limited to the standard weapons! - // Use Give/Take/CheckInventory instead! int playernum; int weaponnum; int newweapon; @@ -2830,7 +2844,8 @@ void FParser::SF_PlayerWeapon() //========================================================================== // -// +// This function is just kept for backwards compatibility and +// intentionally limited to the standard weapons! // //========================================================================== @@ -2839,7 +2854,6 @@ void FParser::SF_PlayerSelectedWeapon() int playernum; int weaponnum; - // This function is just kept for backwards compatibility and intentionally limited to the standard weapons! static const char * const WeaponNames[]={ "Fist", "Pistol", "Shotgun", "Chaingun", "RocketLauncher", @@ -2991,13 +3005,11 @@ void FParser::SF_SetWeapon() } } -// removed FParser::SF_PlayerMaxAmmo - - - +//========================================================================== // // movecamera(camera, targetobj, targetheight, movespeed, targetangle, anglespeed) // +//========================================================================== void FParser::SF_MoveCamera(void) { @@ -3219,12 +3231,10 @@ void FParser::SF_ExitSecret(void) //========================================================================== // -// +// Type forcing functions -- useful with arrays et al // //========================================================================== -// Type forcing functions -- useful with arrays et al - void FParser::SF_MobjValue(void) { if (CheckArgs(1)) @@ -3448,7 +3458,7 @@ void FParser::SF_SpawnMissile() void FParser::SF_MapThingNumExist() { - TArray &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; int intval; @@ -3456,7 +3466,7 @@ void FParser::SF_MapThingNumExist() { intval = intvalue(t_argv[0]); - if (intval < 0 || intval >= int(SpawnedThings.Size()) || !SpawnedThings[intval]->actor) + if (intval < 0 || intval >= int(SpawnedThings.Size()) || !SpawnedThings[intval]) { t_return.type = svt_int; t_return.value.i = 0; @@ -3477,7 +3487,7 @@ void FParser::SF_MapThingNumExist() void FParser::SF_MapThings() { - TArray &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; t_return.type = svt_int; t_return.value.i = SpawnedThings.Size(); @@ -4330,7 +4340,7 @@ void FParser::SF_WallGlow() //========================================================================== // -// Spawns a projectile at a map spot +// // //========================================================================== @@ -4354,8 +4364,12 @@ DRunningScript *FParser::SaveCurrentScript() runscr->next = th->RunningScripts->next; runscr->prev = th->RunningScripts; runscr->prev->next = runscr; + GC::WriteBarrier(runscr->prev, runscr); if(runscr->next) + { runscr->next->prev = runscr; + GC::WriteBarrier(runscr->next, runscr); + } // save the script variables for(i=0; inext = th->RunningScripts->next; runscr->prev = th->RunningScripts; runscr->prev->next = runscr; + GC::WriteBarrier(runscr->prev, runscr); if(runscr->next) + { runscr->next->prev = runscr; + GC::WriteBarrier(runscr->next, runscr); + } // save the script variables for(i=0; iSpawnedThings.Push(acp); + DFraggleThinker::ActiveThinker->SpawnedThings.Push(NULL); } } @@ -291,7 +288,7 @@ void T_RegisterSpawnThing(AActor * ac) { if (DFraggleThinker::ActiveThinker) { - TArray &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; - SpawnedThings[SpawnedThings.Size()-1]->actor=ac; + TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + SpawnedThings[SpawnedThings.Size()-1] = ac; } } diff --git a/src/fragglescript/t_oper.cpp b/src/fragglescript/t_oper.cpp index 47f08a84..b807ac0d 100644 --- a/src/fragglescript/t_oper.cpp +++ b/src/fragglescript/t_oper.cpp @@ -51,6 +51,12 @@ EvaluateExpression(right, (b)+1, (c)); }\ +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + FParser::operator_t FParser::operators[]= { {"=", &FParser::OPequals, backward}, @@ -81,11 +87,11 @@ int FParser::num_operators = sizeof(FParser::operators) / sizeof(FParser::operat /***************** logic *********************/ -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPequals(svalue_t &result, int start, int n, int stop) { @@ -105,11 +111,11 @@ void FParser::OPequals(svalue_t &result, int start, int n, int stop) } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPor(svalue_t &result, int start, int n, int stop) { @@ -132,11 +138,11 @@ void FParser::OPor(svalue_t &result, int start, int n, int stop) } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPand(svalue_t &result, int start, int n, int stop) { @@ -157,11 +163,11 @@ void FParser::OPand(svalue_t &result, int start, int n, int stop) result.value.i = exprtrue; } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPcmp(svalue_t &result, int start, int n, int stop) { @@ -196,11 +202,11 @@ void FParser::OPcmp(svalue_t &result, int start, int n, int stop) result.value.i = (intvalue(left) == intvalue(right)); } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPnotcmp(svalue_t &result, int start, int n, int stop) { @@ -209,11 +215,11 @@ void FParser::OPnotcmp(svalue_t &result, int start, int n, int stop) result.value.i = !result.value.i; } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPlessthan(svalue_t &result, int start, int n, int stop) { @@ -230,11 +236,11 @@ void FParser::OPlessthan(svalue_t &result, int start, int n, int stop) } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPgreaterthan(svalue_t &result, int start, int n, int stop) { @@ -250,11 +256,11 @@ void FParser::OPgreaterthan(svalue_t &result, int start, int n, int stop) result.value.i = (intvalue(left) > intvalue(right)); } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPnot(svalue_t &result, int start, int n, int stop) { @@ -264,13 +270,11 @@ void FParser::OPnot(svalue_t &result, int start, int n, int stop) result.type = svt_int; } -/************** simple math ***************/ - -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPplus(svalue_t &result, int start, int n, int stop) { @@ -307,11 +311,11 @@ void FParser::OPplus(svalue_t &result, int start, int n, int stop) } } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPminus(svalue_t &result, int start, int n, int stop) { @@ -341,11 +345,11 @@ void FParser::OPminus(svalue_t &result, int start, int n, int stop) } } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPmultiply(svalue_t &result,int start, int n, int stop) { @@ -366,11 +370,11 @@ void FParser::OPmultiply(svalue_t &result,int start, int n, int stop) } } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPdivide(svalue_t &result, int start, int n, int stop) { @@ -405,11 +409,11 @@ void FParser::OPdivide(svalue_t &result, int start, int n, int stop) } } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPremainder(svalue_t &result, int start, int n, int stop) { @@ -429,13 +433,11 @@ void FParser::OPremainder(svalue_t &result, int start, int n, int stop) /********** binary operators **************/ -// binary or | - -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPor_bin(svalue_t &result, int start, int n, int stop) { @@ -448,13 +450,11 @@ void FParser::OPor_bin(svalue_t &result, int start, int n, int stop) } -// binary and & - -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPand_bin(svalue_t &result, int start, int n, int stop) { @@ -466,11 +466,11 @@ void FParser::OPand_bin(svalue_t &result, int start, int n, int stop) result.value.i = intvalue(left) & intvalue(right); } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPnot_bin(svalue_t &result, int start, int n, int stop) { @@ -481,11 +481,11 @@ void FParser::OPnot_bin(svalue_t &result, int start, int n, int stop) } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPincrement(svalue_t &result, int start, int n, int stop) { @@ -546,11 +546,11 @@ void FParser::OPincrement(svalue_t &result, int start, int n, int stop) } } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPdecrement(svalue_t &result, int start, int n, int stop) { @@ -611,11 +611,11 @@ void FParser::OPdecrement(svalue_t &result, int start, int n, int stop) } } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPlessthanorequal(svalue_t &result, int start, int n, int stop) { @@ -631,11 +631,11 @@ void FParser::OPlessthanorequal(svalue_t &result, int start, int n, int stop) result.value.i = (intvalue(left) <= intvalue(right)); } -//========================================================================== +//----------------------------------------------------------------------------- // // // -//========================================================================== +//----------------------------------------------------------------------------- void FParser::OPgreaterthanorequal(svalue_t &result, int start, int n, int stop) { diff --git a/src/fragglescript/t_prepro.cpp b/src/fragglescript/t_prepro.cpp index 8e019b62..4430ad29 100644 --- a/src/fragglescript/t_prepro.cpp +++ b/src/fragglescript/t_prepro.cpp @@ -154,6 +154,7 @@ DFsSection *DFsScript::NewSection(const char *brace) newsec->start_index = MakeIndex(brace); newsec->next = sections[n]; sections[n] = newsec; + GC::WriteBarrier(this, newsec); return newsec; } diff --git a/src/fragglescript/t_script.cpp b/src/fragglescript/t_script.cpp index e481ca52..23643614 100644 --- a/src/fragglescript/t_script.cpp +++ b/src/fragglescript/t_script.cpp @@ -320,10 +320,6 @@ void DRunningScript::Serialize(FArchive &arc) // The main thinker // //========================================================================== -IMPLEMENT_POINTY_CLASS(DActorPointer) - DECLARE_POINTER(actor) -END_POINTERS - IMPLEMENT_POINTY_CLASS(DFraggleThinker) DECLARE_POINTER(RunningScripts) DECLARE_POINTER(LevelScript) @@ -373,10 +369,6 @@ void DFraggleThinker::Destroy() LevelScript->Destroy(); LevelScript = NULL; - for(unsigned i=0; iDestroy(); - } SpawnedThings.Clear(); ActiveThinker = NULL; Super::Destroy(); @@ -488,7 +480,12 @@ void DFraggleThinker::Tick() // unhook from chain current->prev->next = current->next; - if(current->next) current->next->prev = current->prev; + GC::WriteBarrier(current->prev, current->next); + if(current->next) + { + current->next->prev = current->prev; + GC::WriteBarrier(current->next, current->prev); + } next = current->next; // save before freeing // continue the script @@ -503,6 +500,23 @@ void DFraggleThinker::Tick() } } + +//========================================================================== +// +// We have to mark the SpawnedThings array manually because it's not +// in the list of declared pointers. +// +//========================================================================== + +size_t DFraggleThinker::PropagateMark() +{ + for(unsigned i=0;inext = th->RunningScripts->next; runscr->prev = th->RunningScripts; runscr->prev->next = runscr; + GC::WriteBarrier(runscr->prev, runscr); if(runscr->next) + { runscr->next->prev = runscr; + GC::WriteBarrier(runscr->next, runscr); + } // save the script variables for(i=0; inext; // save for after freeing - //current->ObjectFlags |= OF_YESREALLYDELETE; + current->ObjectFlags |= OF_YesReallyDelete; delete current; current = next; // go to next in chain } } + GC::DelSoftRoot(global_script); + global_script->ObjectFlags |= OF_YesReallyDelete; delete global_script; } @@ -627,6 +647,7 @@ AT_GAME_SET(FS_Init) // I'd rather link the special here than make another source file depend on FS! LineSpecials[54]=LS_FS_Execute; global_script = new DFsScript; + GC::AddSoftRoot(global_script); init_functions(); atterm(FS_Close); } diff --git a/src/fragglescript/t_script.h b/src/fragglescript/t_script.h index 3842b37e..622a21c4 100644 --- a/src/fragglescript/t_script.h +++ b/src/fragglescript/t_script.h @@ -59,33 +59,6 @@ inline bool isop(int c) // //========================================================================== -class DActorPointer : public DObject -{ - DECLARE_CLASS(DActorPointer, DObject) - HAS_OBJECT_POINTERS - -public: - - AActor * actor; - - DActorPointer() - { - actor=NULL; - } - - void Serialize(FArchive & ar) - { - Super::Serialize(ar); - ar << actor; - } -}; - -//========================================================================== -// -// -// -//========================================================================== - enum { svt_string, @@ -168,11 +141,11 @@ struct DFsVariable : public DObject public: FString Name; - DFsVariable *next; // for hashing + TObjPtr next; // for hashing int type; // vt_string or vt_int: same as in svalue_t FString string; - AActor *actor; + TObjPtr actor; union value_t { @@ -190,7 +163,7 @@ public: DFsVariable(const char *_name = ""); - svalue_t GetValue() const; + svalue_t GetValue(); void SetValue(const svalue_t &newvalue); void Serialize(FArchive &ar); }; @@ -234,7 +207,7 @@ public: int start_index; int end_index; int loop_index; - DFsSection *next; // for hashing + TObjPtr next; // for hashing DFsSection() { @@ -313,27 +286,27 @@ public: // {} sections - DFsSection *sections[SECTIONSLOTS]; + TObjPtr sections[SECTIONSLOTS]; // variables: - DFsVariable *variables[VARIABLESLOTS]; + TObjPtr variables[VARIABLESLOTS]; // ptr to the parent script // the parent script is the script above this level // eg. individual linetrigger scripts are children // of the levelscript, which is a child of the // global_script - DFsScript *parent; + TObjPtr parent; // haleyjd: 8-17 // child scripts. // levelscript holds ptrs to all of the level's scripts // here. - DFsScript *children[MAXSCRIPTS]; + TObjPtr children[MAXSCRIPTS]; - AActor *trigger; // object which triggered this script + TObjPtr trigger; // object which triggered this script bool lastiftrue; // haleyjd: whether last "if" statement was // true or false @@ -654,7 +627,7 @@ public: void Destroy(); void Serialize(FArchive &arc); - DFsScript *script; + TObjPtr script; // where we are int save_point; @@ -663,10 +636,10 @@ public: int wait_data; // data for wait: tagnum, counter, script number etc // saved variables - DFsVariable *variables[VARIABLESLOTS]; + TObjPtr variables[VARIABLESLOTS]; - DRunningScript *prev, *next; // for chain - AActor *trigger; + TObjPtr prev, next; // for chain + TObjPtr trigger; }; //----------------------------------------------------------------------------- @@ -680,9 +653,9 @@ class DFraggleThinker : public DThinker HAS_OBJECT_POINTERS public: - DFsScript *LevelScript; - DRunningScript *RunningScripts; - TArray SpawnedThings; + TObjPtr LevelScript; + TObjPtr RunningScripts; + TArray> SpawnedThings; DFraggleThinker(); void Destroy(); @@ -690,6 +663,7 @@ public: void Serialize(FArchive & arc); void Tick(); + size_t PropagateMark(); bool wait_finished(DRunningScript *script); static DFraggleThinker *ActiveThinker; diff --git a/src/fragglescript/t_spec.cpp b/src/fragglescript/t_spec.cpp index f1ba3593..7b36c7e3 100644 --- a/src/fragglescript/t_spec.cpp +++ b/src/fragglescript/t_spec.cpp @@ -421,6 +421,7 @@ void FParser::spec_script() // add to scripts list of parent Script->children[scriptnum] = newscript; + GC::WriteBarrier(Script, newscript); // copy newscript data // workout newscript size: -2 to ignore { and } diff --git a/src/fragglescript/t_variable.cpp b/src/fragglescript/t_variable.cpp index 8562e5da..3988e92c 100644 --- a/src/fragglescript/t_variable.cpp +++ b/src/fragglescript/t_variable.cpp @@ -151,7 +151,7 @@ AActor *actorvalue(const svalue_t &svalue) } else { - TArray &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; + TArray> &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings; // this requires some creativity. We use the intvalue // as the thing number of a thing in the level. intval = intvalue(svalue); @@ -161,14 +161,14 @@ AActor *actorvalue(const svalue_t &svalue) return NULL; } // Inventory items in the player's inventory have to be considered non-present. - if (SpawnedThings[intval]->actor != NULL && - SpawnedThings[intval]->actor->IsKindOf(RUNTIME_CLASS(AInventory)) && - static_cast(SpawnedThings[intval]->actor)->Owner != NULL) + if (SpawnedThings[intval] != NULL && + SpawnedThings[intval]->IsKindOf(RUNTIME_CLASS(AInventory)) && + barrier_cast(SpawnedThings[intval])->Owner != NULL) { return NULL; } - return SpawnedThings[intval]->actor; + return SpawnedThings[intval]; } } @@ -203,7 +203,7 @@ DFsVariable::DFsVariable(const char * _name) // //========================================================================== -svalue_t DFsVariable::GetValue() const +svalue_t DFsVariable::GetValue() { svalue_t returnvar; @@ -323,6 +323,7 @@ DFsVariable *DFsScript::NewVariable(const char *name, int vtype) int n = variable_hash(name); newvar->next = variables[n]; variables[n] = newvar; + GC::WriteBarrier(this, newvar); return newvar; } diff --git a/src/g_doom/a_bossbrain.cpp b/src/g_doom/a_bossbrain.cpp index 16a64af6..4ad3ae21 100644 --- a/src/g_doom/a_bossbrain.cpp +++ b/src/g_doom/a_bossbrain.cpp @@ -315,7 +315,7 @@ void A_SpawnFly (AActor *self) if (newmobj->SeeState != NULL && P_LookForPlayers (newmobj, true)) newmobj->SetState (newmobj->SeeState); - if (!(newmobj->ObjectFlags & OF_MassDestruction)) + if (!(newmobj->ObjectFlags & OF_EuthanizeMe)) { // telefrag anything in this spot P_TeleportMove (newmobj, newmobj->x, newmobj->y, newmobj->z, true); diff --git a/src/g_doom/doom_sbar.cpp b/src/g_doom/doom_sbar.cpp index 31538f7b..c97b3961 100644 --- a/src/g_doom/doom_sbar.cpp +++ b/src/g_doom/doom_sbar.cpp @@ -27,10 +27,11 @@ EXTERN_CVAR (Bool, vid_fps) -class FDoomStatusBar : public FBaseStatusBar +class DDoomStatusBar : public DBaseStatusBar { + DECLARE_CLASS(DDoomStatusBar, DBaseStatusBar) public: - FDoomStatusBar () : FBaseStatusBar (32) + DDoomStatusBar () : DBaseStatusBar (32) { static const char *sharedLumpNames[] = { @@ -44,8 +45,8 @@ public: }; FTexture *tex; - FBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES); - tex = FBaseStatusBar::Images[imgBNumbers]; + DBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES); + tex = DBaseStatusBar::Images[imgBNumbers]; BigWidth = tex->GetWidth(); BigHeight = tex->GetHeight(); @@ -53,7 +54,7 @@ public: bEvilGrin = false; } - ~FDoomStatusBar () + ~DDoomStatusBar () { } @@ -126,7 +127,7 @@ public: void MultiplayerChanged () { - FBaseStatusBar::MultiplayerChanged (); + DBaseStatusBar::MultiplayerChanged (); if (multiplayer) { // set face background color @@ -138,7 +139,7 @@ public: { player_t *oldplayer = CPlayer; - FBaseStatusBar::AttachToPlayer (player); + DBaseStatusBar::AttachToPlayer (player); if (oldplayer != CPlayer) { SetFace (&skins[CPlayer->userinfo.skin]); @@ -153,14 +154,14 @@ public: void Tick () { - FBaseStatusBar::Tick (); + DBaseStatusBar::Tick (); RandomNumber = M_Random (); UpdateFace (); } void Draw (EHudState state) { - FBaseStatusBar::Draw (state); + DBaseStatusBar::Draw (state); if (state == HUD_Fullscreen) { @@ -375,7 +376,7 @@ private: void DrawArm (int on, int picnum, int x, int y, bool drawBackground) { int w; - FTexture *pic = on ? FBaseStatusBar::Images[imgSmNumbers + 2 + picnum] : Images[imgGNUM2 + picnum]; + FTexture *pic = on ? DBaseStatusBar::Images[imgSmNumbers + 2 + picnum] : Images[imgGNUM2 + picnum]; if (pic != NULL) { @@ -555,7 +556,7 @@ private: void DrawInventoryBar () { - const AInventory *item; + AInventory *item; int i; // If the player has no artifacts, don't draw the bar @@ -602,7 +603,7 @@ private: void DrawFullScreenStuff () { - const AInventory *item; + AInventory *item; int i; int ammotop; @@ -1041,7 +1042,9 @@ private: bool bEvilGrin; }; -FDoomStatusBar::FDoomStatusBarTexture::FDoomStatusBarTexture () +IMPLEMENT_CLASS(DDoomStatusBar) + +DDoomStatusBar::FDoomStatusBarTexture::FDoomStatusBarTexture () { BaseTexture = TexMan["STBAR"]; if (BaseTexture==NULL) @@ -1057,7 +1060,7 @@ FDoomStatusBar::FDoomStatusBarTexture::FDoomStatusBarTexture () STBFremap = NULL; } -const BYTE *FDoomStatusBar::FDoomStatusBarTexture::GetColumn (unsigned int column, const Span **spans_out) +const BYTE *DDoomStatusBar::FDoomStatusBarTexture::GetColumn (unsigned int column, const Span **spans_out) { if (Pixels == NULL) { @@ -1068,7 +1071,7 @@ const BYTE *FDoomStatusBar::FDoomStatusBarTexture::GetColumn (unsigned int colum return Pixels + column*Height; } -const BYTE *FDoomStatusBar::FDoomStatusBarTexture::GetPixels () +const BYTE *DDoomStatusBar::FDoomStatusBarTexture::GetPixels () { if (Pixels == NULL) { @@ -1077,7 +1080,7 @@ const BYTE *FDoomStatusBar::FDoomStatusBarTexture::GetPixels () return Pixels; } -void FDoomStatusBar::FDoomStatusBarTexture::Unload () +void DDoomStatusBar::FDoomStatusBarTexture::Unload () { if (Pixels != NULL) { @@ -1086,13 +1089,13 @@ void FDoomStatusBar::FDoomStatusBarTexture::Unload () } } -FDoomStatusBar::FDoomStatusBarTexture::~FDoomStatusBarTexture () +DDoomStatusBar::FDoomStatusBarTexture::~FDoomStatusBarTexture () { Unload (); } -void FDoomStatusBar::FDoomStatusBarTexture::MakeTexture () +void DDoomStatusBar::FDoomStatusBarTexture::MakeTexture () { Pixels = new BYTE[Width*Height]; const BYTE *pix = BaseTexture->GetPixels(); @@ -1103,7 +1106,7 @@ void FDoomStatusBar::FDoomStatusBarTexture::MakeTexture () if (multiplayer) DrawToBar("STFBANY", 143, 1, STBFremap? STBFremap->Remap : NULL); } -int FDoomStatusBar::FDoomStatusBarTexture::CopyTrueColorPixels(BYTE *buffer, int buf_pitch, int buf_height, int x, int y) +int DDoomStatusBar::FDoomStatusBarTexture::CopyTrueColorPixels(BYTE *buffer, int buf_pitch, int buf_height, int x, int y) { FTexture *tex; @@ -1136,7 +1139,7 @@ int FDoomStatusBar::FDoomStatusBarTexture::CopyTrueColorPixels(BYTE *buffer, int -void FDoomStatusBar::FDoomStatusBarTexture::DrawToBar (const char *name, int x, int y, const BYTE *colormap_in) +void DDoomStatusBar::FDoomStatusBarTexture::DrawToBar (const char *name, int x, int y, const BYTE *colormap_in) { FTexture *pic; BYTE colormap[256]; @@ -1165,14 +1168,14 @@ void FDoomStatusBar::FDoomStatusBarTexture::DrawToBar (const char *name, int x, } } -void FDoomStatusBar::FDoomStatusBarTexture::SetPlayerRemap(FRemapTable *remap) +void DDoomStatusBar::FDoomStatusBarTexture::SetPlayerRemap(FRemapTable *remap) { Unload(); KillNative(); STBFremap = remap; } -FBaseStatusBar *CreateDoomStatusBar () +DBaseStatusBar *CreateDoomStatusBar () { - return new FDoomStatusBar; + return new DDoomStatusBar; } diff --git a/src/g_game.cpp b/src/g_game.cpp index a0d3ec1c..53aa2906 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1850,7 +1850,7 @@ FString G_BuildSaveName (const char *prefix, int slot) const char *leader; const char *slash = ""; - if (NULL != (leader = Args.CheckValue ("-savedir"))) + if (NULL != (leader = Args->CheckValue ("-savedir"))) { size_t len = strlen (leader); if (leader[len-1] != '\\' && leader[len-1] != '/') @@ -1859,7 +1859,7 @@ FString G_BuildSaveName (const char *prefix, int slot) } } #ifndef unix - else if (Args.CheckParm ("-cdrom")) + else if (Args->CheckParm ("-cdrom")) { leader = CDROM_DIR "/"; } @@ -2205,7 +2205,7 @@ void G_RecordDemo (char* name) strcpy (demoname, name); FixPathSeperator (demoname); DefaultExtension (demoname, ".lmp"); - v = Args.CheckValue ("-maxdemo"); + v = Args->CheckValue ("-maxdemo"); maxdemosize = 0x20000; demobuffer = (BYTE *)M_Malloc (maxdemosize); @@ -2510,8 +2510,8 @@ void G_DoPlayDemo (void) // void G_TimeDemo (char* name) { - nodrawers = !!Args.CheckParm ("-nodraw"); - noblit = !!Args.CheckParm ("-noblit"); + nodrawers = !!Args->CheckParm ("-nodraw"); + noblit = !!Args->CheckParm ("-noblit"); timingdemo = true; singletics = true; diff --git a/src/g_heretic/a_hereticmisc.cpp b/src/g_heretic/a_hereticmisc.cpp index 773ad6ca..c0f44e9c 100644 --- a/src/g_heretic/a_hereticmisc.cpp +++ b/src/g_heretic/a_hereticmisc.cpp @@ -32,7 +32,7 @@ class APod : public AActor HAS_OBJECT_POINTERS public: void BeginPlay (); - AActor *Generator; + TObjPtr Generator; void Serialize (FArchive &arc); }; diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index ec4abd3f..8874729b 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -803,7 +803,7 @@ public: protected: bool DoRespawn (); int NumMaceSpots; - AActor *FirstSpot; + TObjPtr FirstSpot; private: friend void A_SpawnMace (AActor *self); diff --git a/src/g_heretic/heretic_sbar.cpp b/src/g_heretic/heretic_sbar.cpp index d2b02d6e..058779c9 100644 --- a/src/g_heretic/heretic_sbar.cpp +++ b/src/g_heretic/heretic_sbar.cpp @@ -84,10 +84,12 @@ const BYTE *FHereticShader::GetPixels () } -class FHereticStatusBar : public FBaseStatusBar +class DHereticStatusBar : public DBaseStatusBar { + DECLARE_CLASS(DHereticStatusBar, DBaseStatusBar) + HAS_OBJECT_POINTERS public: - FHereticStatusBar () : FBaseStatusBar (42) + DHereticStatusBar () : DBaseStatusBar (42) { static const char *hereticLumpNames[NUM_HERETICSB_IMAGES] = { @@ -118,7 +120,7 @@ public: hereticLumpNames[5] = "LIFEBAR"; } - FBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES); + DBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES); Images.Init (hereticLumpNames, NUM_HERETICSB_IMAGES); oldarti = NULL; @@ -136,7 +138,7 @@ public: ArtifactFlash = 0; } - ~FHereticStatusBar () + ~DHereticStatusBar () { } @@ -144,7 +146,7 @@ public: { int curHealth; - FBaseStatusBar::Tick (); + DBaseStatusBar::Tick (); if (level.time & 1) { ChainWiggle = pr_chainwiggle() & 1; @@ -174,7 +176,7 @@ public: void Draw (EHudState state) { - FBaseStatusBar::Draw (state); + DBaseStatusBar::Draw (state); if (state == HUD_Fullscreen) { @@ -316,6 +318,7 @@ private: || (oldarti != NULL && oldartiCount != oldarti->Amount)) { oldarti = CPlayer->mo->InvSel; + GC::WriteBarrier(this, oldarti); oldartiCount = oldarti != NULL ? oldarti->Amount : 0; ArtiRefresh = screen->GetPageCount (); } @@ -422,6 +425,8 @@ private: oldammo2 = ammo2; oldammocount1 = ammocount1; oldammocount2 = ammocount2; + GC::WriteBarrier(this, ammo1); + GC::WriteBarrier(this, ammo2); AmmoRefresh = screen->GetPageCount (); } if (AmmoRefresh) @@ -475,7 +480,7 @@ private: void DrawInventoryBar () { - const AInventory *item; + AInventory *item; int i; DrawImage (Images[imgINVBAR], 34, 2); @@ -517,7 +522,7 @@ private: void DrawFullScreenStuff () { - const AInventory *item; + AInventory *item; FTexture *pic; int i; @@ -713,8 +718,8 @@ private: static const char patcharti[][10]; static const char ammopic[][10]; - AInventory *oldarti; - AAmmo *oldammo1, *oldammo2; + TObjPtr oldarti; + TObjPtr oldammo1, oldammo2; int oldammocount1, oldammocount2; int oldartiCount; int oldfrags; @@ -773,7 +778,13 @@ private: char ArmorRefresh; }; -FBaseStatusBar *CreateHereticStatusBar () +IMPLEMENT_POINTY_CLASS(DHereticStatusBar) + DECLARE_POINTER(oldarti) + DECLARE_POINTER(oldammo1) + DECLARE_POINTER(oldammo2) +END_POINTERS + +DBaseStatusBar *CreateHereticStatusBar () { - return new FHereticStatusBar; + return new DHereticStatusBar; } diff --git a/src/g_hexen/a_clericholy.cpp b/src/g_hexen/a_clericholy.cpp index e9b5c6ad..177bbde2 100644 --- a/src/g_hexen/a_clericholy.cpp +++ b/src/g_hexen/a_clericholy.cpp @@ -386,6 +386,7 @@ bool AHolySpirit::SpecialBlastHandling (AActor *source, fixed_t strength) { tracer = target; target = source; + GC::WriteBarrier(this, source); } return true; } diff --git a/src/g_hexen/a_heresiarch.cpp b/src/g_hexen/a_heresiarch.cpp index 7a17c986..e0d969cf 100644 --- a/src/g_hexen/a_heresiarch.cpp +++ b/src/g_hexen/a_heresiarch.cpp @@ -706,7 +706,7 @@ void A_SorcBallOrbit(AActor *ball) int x,y; angle_t angle, baseangle; int mode = ball->target->args[3]; - AHeresiarch *parent = static_cast (ball->target); + AHeresiarch *parent = barrier_cast(ball->target); int dist = parent->radius - (ball->radius<<1); angle_t prevangle = ball->special1; diff --git a/src/g_hexen/a_hexenglobal.h b/src/g_hexen/a_hexenglobal.h index 9720ffdf..dbc16d2a 100644 --- a/src/g_hexen/a_hexenglobal.h +++ b/src/g_hexen/a_hexenglobal.h @@ -66,7 +66,7 @@ protected: virtual bool MatchPlayerClass (AActor *toucher); const PClass *FourthWeaponClass; int PieceValue; - AInventory *TempFourthWeapon; + TObjPtr TempFourthWeapon; bool PrivateShouldStay (); }; diff --git a/src/g_hexen/a_spike.cpp b/src/g_hexen/a_spike.cpp index 775e0af7..fa4ff486 100644 --- a/src/g_hexen/a_spike.cpp +++ b/src/g_hexen/a_spike.cpp @@ -103,7 +103,7 @@ public: void Activate (AActor *activator); void Deactivate (AActor *activator); - ADirtClump *DirtClump; + TObjPtr DirtClump; }; IMPLEMENT_POINTY_CLASS (AThrustFloor) diff --git a/src/g_hexen/a_teleportother.cpp b/src/g_hexen/a_teleportother.cpp index 75610b3b..2a0cd8b5 100644 --- a/src/g_hexen/a_teleportother.cpp +++ b/src/g_hexen/a_teleportother.cpp @@ -249,7 +249,7 @@ int ATelOtherFX1::DoSpecialDamage (AActor *target, int damage) { target->RemoveFromHash (); LineSpecials[target->special] (NULL, level.flags & LEVEL_ACTOWNSPECIAL - ? target : this->target, false, target->args[0], target->args[1], + ? target : (AActor *)(this->target), false, target->args[0], target->args[1], target->args[2], target->args[3], target->args[4]); target->special = 0; } diff --git a/src/g_hexen/hexen_sbar.cpp b/src/g_hexen/hexen_sbar.cpp index ebff79bb..1a12a82a 100644 --- a/src/g_hexen/hexen_sbar.cpp +++ b/src/g_hexen/hexen_sbar.cpp @@ -119,10 +119,12 @@ void FManaBar::MakeTexture () memset (Pixels + 25+24+24, color0, run); } -class FHexenStatusBar : public FBaseStatusBar +class DHexenStatusBar : public DBaseStatusBar { + DECLARE_CLASS(DHexenStatusBar, DBaseStatusBar) + HAS_OBJECT_POINTERS public: - FHexenStatusBar () : FBaseStatusBar (38) + DHexenStatusBar () : DBaseStatusBar (38) { static const char *hexenLumpNames[NUM_HEXENSB_IMAGES] = { @@ -166,7 +168,7 @@ public: "INRED5", "INRED6", "INRED7", "INRED8", "INRED9" }; - FBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES + 10); + DBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES + 10); Images.Init (hexenLumpNames, NUM_HEXENSB_IMAGES); ClassImages[0].Init (classLumpNames[0], NUM_HEXENCLASSSB_IMAGES); ClassImages[1].Init (classLumpNames[1], NUM_HEXENCLASSSB_IMAGES); @@ -201,7 +203,7 @@ public: AmmoRefresh = 0; } - ~FHexenStatusBar () + ~DHexenStatusBar () { } @@ -209,7 +211,7 @@ public: { int curHealth; - FBaseStatusBar::Tick (); + DBaseStatusBar::Tick (); if (CPlayer->mo == NULL) { curHealth = 0; @@ -242,7 +244,7 @@ public: void Draw (EHudState state) { - FBaseStatusBar::Draw (state); + DBaseStatusBar::Draw (state); if (state == HUD_Fullscreen) { @@ -309,7 +311,7 @@ public: void AttachToPlayer (player_s *player) { - FBaseStatusBar::AttachToPlayer (player); + DBaseStatusBar::AttachToPlayer (player); if (player->mo != NULL) { if (player->mo->IsKindOf (PClass::FindClass(NAME_MagePlayer))) @@ -395,6 +397,7 @@ private: || (oldarti != NULL && oldartiCount != oldarti->Amount)) { oldarti = CPlayer->mo->InvSel; + GC::WriteBarrier(this, oldarti); oldartiCount = oldarti != NULL ? oldarti->Amount : 0; ArtiRefresh = screen->GetPageCount (); } @@ -521,12 +524,14 @@ private: AmmoRefresh = screen->GetPageCount (); oldammo1 = ammo1; oldammocount1 = ammocount1; + GC::WriteBarrier(this, ammo1); } if (ammo2 != oldammo2 || ammocount2 != oldammocount2) { AmmoRefresh = screen->GetPageCount (); oldammo2 = ammo2; oldammocount2 = ammocount2; + GC::WriteBarrier(this, ammo2); } if (AmmoRefresh) @@ -705,7 +710,7 @@ private: void DrawInventoryBar () { - const AInventory *item; + AInventory *item; int i; DrawImage (Images[imgINVBAR], 38, 0); @@ -772,6 +777,7 @@ private: if (keys[i] != oldkeys[i]) { oldkeys[i] = keys[i]; + GC::WriteBarrier(this, keys[i]); different = true; } } @@ -880,7 +886,7 @@ private: void DrawFullScreenStuff () { - const AInventory *item; + AInventory *item; int i; // Health @@ -1051,9 +1057,9 @@ private: static const char patcharti[][10]; static const char ammopic[][10]; - AInventory *oldarti; - AAmmo *oldammo1, *oldammo2; - AKey *oldkeys[5]; + TObjPtr oldarti; + TObjPtr oldammo1, oldammo2; + TObjPtr oldkeys[5]; int oldammocount1, oldammocount2; int oldartiCount; int oldfrags; @@ -1155,7 +1161,18 @@ private: FManaBar ManaVial2Pic; }; -FBaseStatusBar *CreateHexenStatusBar () +IMPLEMENT_POINTY_CLASS(DHexenStatusBar) + DECLARE_POINTER(oldarti) + DECLARE_POINTER(oldammo1) + DECLARE_POINTER(oldammo2) + DECLARE_POINTER(oldkeys[0]) + DECLARE_POINTER(oldkeys[1]) + DECLARE_POINTER(oldkeys[2]) + DECLARE_POINTER(oldkeys[3]) + DECLARE_POINTER(oldkeys[4]) +END_POINTERS + +DBaseStatusBar *CreateHexenStatusBar () { - return new FHexenStatusBar; + return new DHexenStatusBar; } diff --git a/src/g_level.cpp b/src/g_level.cpp index 965e1d4f..caeaeb94 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1540,12 +1540,12 @@ void G_InitNew (const char *mapname, bool bTitleLevel) if (StatusBar != NULL) { - delete StatusBar; + StatusBar->Destroy(); StatusBar = NULL; } if (bTitleLevel) { - StatusBar = new FBaseStatusBar (0); + StatusBar = new DBaseStatusBar (0); } else if (SBarInfoScript != NULL) { @@ -1592,9 +1592,10 @@ void G_InitNew (const char *mapname, bool bTitleLevel) } else { - StatusBar = new FBaseStatusBar (0); + StatusBar = new DBaseStatusBar (0); } } + GC::WriteBarrier(StatusBar); StatusBar->AttachToPlayer (&players[consoleplayer]); StatusBar->NewGame (); setsizeneeded = true; diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp index e708e5a3..d043389b 100644 --- a/src/g_shared/a_action.cpp +++ b/src/g_shared/a_action.cpp @@ -332,7 +332,7 @@ public: DCorpsePointer (AActor *ptr); void Destroy (); void Serialize (FArchive &arc); - AActor *Corpse; + TObjPtr Corpse; DWORD Count; // Only the first corpse pointer's count is valid. private: DCorpsePointer () {} diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index 72b1db45..9b692087 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -52,10 +52,10 @@ static int ImpactCount; CVAR (Bool, cl_spreaddecals, true, CVAR_ARCHIVE) -// They also overload floorclip to be the fractional distance from the -// left edge of the side. This distance is stored as a 2.30 fixed pt number. +IMPLEMENT_POINTY_CLASS (DBaseDecal) + DECLARE_POINTER(WallNext) +END_POINTERS -IMPLEMENT_CLASS (DBaseDecal) IMPLEMENT_CLASS (DImpactDecal) DBaseDecal::DBaseDecal () diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 3837a0af..c091c513 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -137,7 +137,7 @@ bool P_UndoPlayerMorph (player_t *player, bool force) { return false; } - mo = static_cast(pmo->tracer); + mo = barrier_cast(pmo->tracer); mo->SetOrigin (pmo->x, pmo->y, pmo->z); mo->flags |= MF_SOLID; pmo->flags &= ~MF_SOLID; diff --git a/src/g_shared/a_movingcamera.cpp b/src/g_shared/a_movingcamera.cpp index 775260b9..50015f34 100644 --- a/src/g_shared/a_movingcamera.cpp +++ b/src/g_shared/a_movingcamera.cpp @@ -61,7 +61,7 @@ public: void Serialize (FArchive &arc); - AInterpolationPoint *Next; + TObjPtr Next; }; IMPLEMENT_POINTY_CLASS (AInterpolationPoint) @@ -181,7 +181,7 @@ protected: void Serialize (FArchive &arc); bool bActive, bJustStepped; - AInterpolationPoint *PrevNode, *CurrNode; + TObjPtr PrevNode, CurrNode; float Time; // Runs from 0.0 to 1.0 between CurrNode and CurrNode->Next int HoldTime; }; @@ -289,8 +289,8 @@ void APathFollower::Activate (AActor *activator) { if (!bActive) { - CurrNode = static_cast(target); - PrevNode = static_cast(lastenemy); + CurrNode = barrier_cast(target); + PrevNode = barrier_cast(lastenemy); if (CurrNode != NULL) { @@ -638,7 +638,7 @@ public: protected: bool Interpolate (); - AActor *Activator; + TObjPtr Activator; }; IMPLEMENT_POINTY_CLASS (AMovingCamera) diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index a7660bdf..a85f022b 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -101,7 +101,7 @@ bool AAmmo::HandlePickup (AInventory *item) (Owner->player->ReadyWeapon == NULL || (Owner->player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))) { - AWeapon *best = static_cast(Owner)->BestWeapon (GetClass()); + AWeapon *best = barrier_cast(Owner)->BestWeapon (GetClass()); if (best != NULL && (Owner->player->ReadyWeapon == NULL || best->SelectionOrder < Owner->player->ReadyWeapon->SelectionOrder)) { @@ -1052,7 +1052,7 @@ PalEntry AInventory::GetBlend () // //=========================================================================== -AInventory *AInventory::PrevItem () const +AInventory *AInventory::PrevItem () { AInventory *item = Owner->Inventory; @@ -1071,7 +1071,7 @@ AInventory *AInventory::PrevItem () const // //=========================================================================== -AInventory *AInventory::PrevInv () const +AInventory *AInventory::PrevInv () { AInventory *lastgood = NULL; AInventory *item = Owner->Inventory; @@ -1094,7 +1094,7 @@ AInventory *AInventory::PrevInv () const // //=========================================================================== -AInventory *AInventory::NextInv () const +AInventory *AInventory::NextInv () { AInventory *item = Inventory; diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 3ceaa9a0..ad0e3a65 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -124,11 +124,11 @@ public: virtual const char *PickupMessage (); virtual void PlayPickupSound (AActor *toucher); - AInventory *PrevItem () const; // Returns the item preceding this one in the list. - AInventory *PrevInv () const; // Returns the previous item with IF_INVBAR set. - AInventory *NextInv () const; // Returns the next item with IF_INVBAR set. + AInventory *PrevItem(); // Returns the item preceding this one in the list. + AInventory *PrevInv(); // Returns the previous item with IF_INVBAR set. + AInventory *NextInv(); // Returns the next item with IF_INVBAR set. - AActor *Owner; // Who owns this item? NULL if it's still a pickup. + TObjPtr Owner; // Who owns this item? NULL if it's still a pickup. int Amount; // Amount of item this instance has int MaxAmount; // Max amount of item this instance can have int RespawnTics; // Tics from pickup time to respawn time @@ -217,8 +217,8 @@ public: fixed_t MoveCombatDist; // Used by bots, but do they *really* need it? // In-inventory instance variables - AAmmo *Ammo1, *Ammo2; - AWeapon *SisterWeapon; + TObjPtr Ammo1, Ammo2; + TObjPtr SisterWeapon; bool bAltFire; // Set when this weapon's alternate fire is used. diff --git a/src/g_shared/a_sectoraction.cpp b/src/g_shared/a_sectoraction.cpp index bddc0f15..3b82aff0 100644 --- a/src/g_shared/a_sectoraction.cpp +++ b/src/g_shared/a_sectoraction.cpp @@ -88,7 +88,7 @@ void ASectorAction::Deactivate (AActor *source) bool ASectorAction::TriggerAction (AActor *triggerer, int activationType) { if (tracer != NULL) - return static_cast(tracer)->TriggerAction (triggerer, activationType); + return barrier_cast(tracer)->TriggerAction (triggerer, activationType); else return false; } diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index 8f5e7898..0e27e223 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -24,6 +24,7 @@ struct F3DFloor; class DBaseDecal : public DThinker { DECLARE_CLASS (DBaseDecal, DThinker) + HAS_OBJECT_POINTERS public: DBaseDecal (); DBaseDecal (fixed_t z); @@ -149,7 +150,7 @@ protected: float Blends[2][4]; int TotalTics; int StartTic; - AActor *ForWho; + TObjPtr ForWho; void SetBlend (float time); DFlashFader (); @@ -165,7 +166,7 @@ public: void Serialize (FArchive &arc); void Tick (); - AActor *m_Spot; + TObjPtr m_Spot; fixed_t m_TremorRadius, m_DamageRadius; int m_Intensity; int m_Countdown; @@ -197,7 +198,7 @@ public: void Die (AActor *source, AActor *inflictor); void Destroy (); - AActor *UnmorphedMe; + TObjPtr UnmorphedMe; int UnmorphTime; DWORD FlagsSave; }; diff --git a/src/g_shared/a_weaponpiece.cpp b/src/g_shared/a_weaponpiece.cpp index 2e987c33..aa47874e 100644 --- a/src/g_shared/a_weaponpiece.cpp +++ b/src/g_shared/a_weaponpiece.cpp @@ -27,7 +27,11 @@ IMPLEMENT_STATELESS_ACTOR (AWeaponHolder, Any, -1, 0) PROP_Inventory_FlagsSet (IF_UNDROPPABLE) END_DEFAULTS -IMPLEMENT_STATELESS_ACTOR (AWeaponPiece, Any, -1, 0) + +IMPLEMENT_POINTY_CLASS (AWeaponPiece) + DECLARE_POINTER (FullWeapon) +END_POINTERS +BEGIN_STATELESS_DEFAULTS (AWeaponPiece, Any, -1, 0) END_DEFAULTS diff --git a/src/g_shared/a_weaponpiece.h b/src/g_shared/a_weaponpiece.h index f0d46577..d6df4a71 100644 --- a/src/g_shared/a_weaponpiece.h +++ b/src/g_shared/a_weaponpiece.h @@ -2,6 +2,7 @@ class AWeaponPiece : public AInventory { DECLARE_CLASS (AWeaponPiece, AInventory) + HAS_OBJECT_POINTERS protected: bool PrivateShouldStay (); public: @@ -13,5 +14,5 @@ public: int PieceValue; const PClass * WeaponClass; - AWeapon * FullWeapon; + TObjPtr FullWeapon; }; diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index 6cdcab3f..8f35819f 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -370,7 +370,7 @@ bool AWeapon::CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo) bool gotSome = CheckAmmo (PrimaryFire, false) || CheckAmmo (AltFire, false); if (!gotSome && autoSwitch) { - static_cast (Owner)->PickNewWeapon (NULL); + barrier_cast(Owner)->PickNewWeapon (NULL); } return gotSome; } @@ -402,7 +402,7 @@ bool AWeapon::CheckAmmo (int fireMode, bool autoSwitch, bool requireAmmo) // out of ammo, pick a weapon to change to if (autoSwitch) { - static_cast (Owner)->PickNewWeapon (NULL); + barrier_cast(Owner)->PickNewWeapon (NULL); } return false; } diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp index 0f17da02..5bb9be2b 100644 --- a/src/g_shared/hudmessages.cpp +++ b/src/g_shared/hudmessages.cpp @@ -41,7 +41,10 @@ EXTERN_CVAR (Int, con_scaletext) -IMPLEMENT_CLASS (DHUDMessage) +IMPLEMENT_POINTY_CLASS (DHUDMessage) + DECLARE_POINTER(Next) +END_POINTERS + IMPLEMENT_CLASS (DHUDMessageFadeOut) IMPLEMENT_CLASS (DHUDMessageFadeInOut) IMPLEMENT_CLASS (DHUDMessageTypeOnFadeOut) diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index d6c3ee70..f7101d97 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -55,6 +55,7 @@ class AWeapon; class DHUDMessage : public DObject { DECLARE_CLASS (DHUDMessage, DObject) + HAS_OBJECT_POINTERS public: DHUDMessage (const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float holdTime); @@ -84,11 +85,11 @@ protected: DHUDMessage () : SourceText(NULL) {} private: - DHUDMessage *Next; + TObjPtr Next; DWORD SBarID; char *SourceText; - friend class FBaseStatusBar; + friend class DBaseStatusBar; }; class DHUDMessageFadeOut : public DHUDMessage @@ -149,8 +150,10 @@ protected: class FTexture; class AAmmo; -class FBaseStatusBar +class DBaseStatusBar : public DObject { + DECLARE_CLASS (DBaseStatusBar, DObject) + HAS_OBJECT_POINTERS public: // Popup screens for Strife's status bar enum @@ -162,8 +165,8 @@ public: POP_Status }; - FBaseStatusBar (int reltop); - virtual ~FBaseStatusBar (); + DBaseStatusBar (int reltop); + void Destroy (); void SetScaled (bool scale); @@ -246,20 +249,21 @@ public: player_s *CPlayer; private: + DBaseStatusBar() {} bool RepositionCoords (int &x, int &y, int xo, int yo, const int w, const int h) const; - void DrawMessages (int bottom) const; + void DrawMessages (int bottom); void DrawConsistancy () const; static BYTE DamageToAlpha[114]; - DHUDMessage *Messages; + TObjPtr Messages; bool ShowLog; }; -extern FBaseStatusBar *StatusBar; +extern DBaseStatusBar *StatusBar; -FBaseStatusBar *CreateDoomStatusBar (); -FBaseStatusBar *CreateHereticStatusBar (); -FBaseStatusBar *CreateHexenStatusBar (); -FBaseStatusBar *CreateStrifeStatusBar (); -FBaseStatusBar *CreateCustomStatusBar (); +DBaseStatusBar *CreateDoomStatusBar (); +DBaseStatusBar *CreateHereticStatusBar (); +DBaseStatusBar *CreateHexenStatusBar (); +DBaseStatusBar *CreateStrifeStatusBar (); +DBaseStatusBar *CreateCustomStatusBar (); diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index 4f546004..31e38ae4 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -1238,10 +1238,11 @@ SBarInfoBlock::SBarInfoBlock() } //SBarInfo Display -class FSBarInfo : public FBaseStatusBar +class DSBarInfo : public DBaseStatusBar { + DECLARE_CLASS(DSBarInfo, DBaseStatusBar) public: - FSBarInfo () : FBaseStatusBar (SBarInfoScript->height), + DSBarInfo () : DBaseStatusBar (SBarInfoScript->height), shader_horz_normal(false, false), shader_horz_reverse(false, true), shader_vert_normal(true, false), @@ -1279,7 +1280,7 @@ public: artiflash = 4; } - ~FSBarInfo () + ~DSBarInfo () { Images.Uninit(); Faces.Uninit(); @@ -1287,7 +1288,7 @@ public: void Draw (EHudState state) { - FBaseStatusBar::Draw(state); + DBaseStatusBar::Draw(state); int hud = 2; if(state == HUD_StatusBar) { @@ -1341,7 +1342,7 @@ public: void AttachToPlayer (player_t *player) { player_t *oldplayer = CPlayer; - FBaseStatusBar::AttachToPlayer(player); + DBaseStatusBar::AttachToPlayer(player); if (oldplayer != CPlayer) { SetFace(&skins[CPlayer->userinfo.skin], "STF"); @@ -1350,7 +1351,7 @@ public: void Tick () { - FBaseStatusBar::Tick(); + DBaseStatusBar::Tick(); if(level.time & 1) chainWiggle = pr_chainwiggle() & 1; getNewFace(M_Random()); @@ -2310,7 +2311,7 @@ private: void DrawInventoryBar(int type, int num, int x, int y, bool alwaysshow, int counterx, int countery, EColorRange translation, bool drawArtiboxes, bool noArrows, bool alwaysshowcounter) { //yes, there is some Copy & Paste here too - const AInventory *item; + AInventory *item; int i; // If the player has no artifacts, don't draw the bar @@ -2408,7 +2409,9 @@ private: FBarShader shader_vert_reverse; }; -FBaseStatusBar *CreateCustomStatusBar () +IMPLEMENT_CLASS(DSBarInfo); + +DBaseStatusBar *CreateCustomStatusBar () { - return new FSBarInfo; + return new DSBarInfo; } diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 52d5299f..c8209eb4 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -53,6 +53,10 @@ #define XHAIRPICKUPSIZE (FRACUNIT*2+XHAIRSHRINKSIZE) #define POWERUPICONSIZE 32 +IMPLEMENT_POINTY_CLASS(DBaseStatusBar) + DECLARE_POINTER(Messages) +END_POINTERS + EXTERN_CVAR (Bool, am_showmonsters) EXTERN_CVAR (Bool, am_showsecrets) EXTERN_CVAR (Bool, am_showitems) @@ -62,7 +66,7 @@ EXTERN_CVAR (Bool, noisedebug) EXTERN_CVAR (Bool, hud_scale) EXTERN_CVAR (Int, con_scaletext) -FBaseStatusBar *StatusBar; +DBaseStatusBar *StatusBar; extern int setblocks; @@ -126,7 +130,7 @@ CVAR (Bool, idmypos, false, 0); // [RH] Amount of red flash for up to 114 damage points. Calculated by hand // using a logarithmic scale and my trusty HP48G. -BYTE FBaseStatusBar::DamageToAlpha[114] = +BYTE DBaseStatusBar::DamageToAlpha[114] = { 0, 8, 16, 23, 30, 36, 42, 47, 53, 58, 62, 67, 71, 75, 79, 83, 87, 90, 94, 97, 100, 103, 107, 109, 112, 115, 118, 120, 123, 125, @@ -144,7 +148,7 @@ BYTE FBaseStatusBar::DamageToAlpha[114] = // //--------------------------------------------------------------------------- -FBaseStatusBar::FBaseStatusBar (int reltop) +DBaseStatusBar::DBaseStatusBar (int reltop) { Centering = false; FixedOrigin = false; @@ -160,11 +164,11 @@ FBaseStatusBar::FBaseStatusBar (int reltop) //--------------------------------------------------------------------------- // -// Destructor +// PROP Destroy // //--------------------------------------------------------------------------- -FBaseStatusBar::~FBaseStatusBar () +void DBaseStatusBar::Destroy () { DHUDMessage *msg; @@ -172,9 +176,10 @@ FBaseStatusBar::~FBaseStatusBar () while (msg) { DHUDMessage *next = msg->Next; - delete msg; + msg->Destroy(); msg = next; } + Super::Destroy(); } //--------------------------------------------------------------------------- @@ -183,7 +188,7 @@ FBaseStatusBar::~FBaseStatusBar () // //--------------------------------------------------------------------------- -void FBaseStatusBar::SetScaled (bool scale) +void DBaseStatusBar::SetScaled (bool scale) { Scaled = RelTop != 0 && (SCREENWIDTH != 320 && scale); if (!Scaled) @@ -225,7 +230,7 @@ void FBaseStatusBar::SetScaled (bool scale) // //--------------------------------------------------------------------------- -void FBaseStatusBar::AttachToPlayer (player_s *player) +void DBaseStatusBar::AttachToPlayer (player_s *player) { CPlayer = player; SB_state = screen->GetPageCount (); @@ -237,7 +242,7 @@ void FBaseStatusBar::AttachToPlayer (player_s *player) // //--------------------------------------------------------------------------- -int FBaseStatusBar::GetPlayer () +int DBaseStatusBar::GetPlayer () { return int(CPlayer - players); } @@ -248,7 +253,7 @@ int FBaseStatusBar::GetPlayer () // //--------------------------------------------------------------------------- -void FBaseStatusBar::MultiplayerChanged () +void DBaseStatusBar::MultiplayerChanged () { SB_state = screen->GetPageCount (); } @@ -259,7 +264,7 @@ void FBaseStatusBar::MultiplayerChanged () // //--------------------------------------------------------------------------- -void FBaseStatusBar::Tick () +void DBaseStatusBar::Tick () { DHUDMessage *msg = Messages; DHUDMessage **prev = &Messages; @@ -271,7 +276,7 @@ void FBaseStatusBar::Tick () if (msg->Tick ()) { *prev = next; - delete msg; + msg->Destroy(); } else { @@ -297,15 +302,16 @@ void FBaseStatusBar::Tick () // //--------------------------------------------------------------------------- -void FBaseStatusBar::AttachMessage (DHUDMessage *msg, DWORD id) +void DBaseStatusBar::AttachMessage (DHUDMessage *msg, DWORD id) { DHUDMessage *old = NULL; DHUDMessage **prev; + DObject *container = this; old = (id == 0 || id == 0xFFFFFFFF) ? NULL : DetachMessage (id); if (old != NULL) { - delete old; + old->Destroy(); } prev = &Messages; @@ -315,12 +321,14 @@ void FBaseStatusBar::AttachMessage (DHUDMessage *msg, DWORD id) // it gets drawn back to front.) while (*prev != NULL && (*prev)->SBarID > id) { + container = *prev; prev = &(*prev)->Next; } msg->Next = *prev; msg->SBarID = id; *prev = msg; + GC::WriteBarrier(container, msg); } //--------------------------------------------------------------------------- @@ -329,7 +337,7 @@ void FBaseStatusBar::AttachMessage (DHUDMessage *msg, DWORD id) // //--------------------------------------------------------------------------- -DHUDMessage *FBaseStatusBar::DetachMessage (DHUDMessage *msg) +DHUDMessage *DBaseStatusBar::DetachMessage (DHUDMessage *msg) { DHUDMessage *probe = Messages; DHUDMessage **prev = &Messages; @@ -349,7 +357,7 @@ DHUDMessage *FBaseStatusBar::DetachMessage (DHUDMessage *msg) return probe; } -DHUDMessage *FBaseStatusBar::DetachMessage (DWORD id) +DHUDMessage *DBaseStatusBar::DetachMessage (DWORD id) { DHUDMessage *probe = Messages; DHUDMessage **prev = &Messages; @@ -375,7 +383,7 @@ DHUDMessage *FBaseStatusBar::DetachMessage (DWORD id) // //--------------------------------------------------------------------------- -void FBaseStatusBar::DetachAllMessages () +void DBaseStatusBar::DetachAllMessages () { DHUDMessage *probe = Messages; @@ -383,7 +391,7 @@ void FBaseStatusBar::DetachAllMessages () while (probe != NULL) { DHUDMessage *next = probe->Next; - delete probe; + probe->Destroy(); probe = next; } } @@ -394,7 +402,7 @@ void FBaseStatusBar::DetachAllMessages () // //--------------------------------------------------------------------------- -bool FBaseStatusBar::CheckMessage (DHUDMessage *msg) +bool DBaseStatusBar::CheckMessage (DHUDMessage *msg) { DHUDMessage *probe = Messages; while (probe && probe != msg) @@ -410,7 +418,7 @@ bool FBaseStatusBar::CheckMessage (DHUDMessage *msg) // //--------------------------------------------------------------------------- -void FBaseStatusBar::ShowPlayerName () +void DBaseStatusBar::ShowPlayerName () { EColorRange color; @@ -427,7 +435,7 @@ void FBaseStatusBar::ShowPlayerName () // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrawImage (FTexture *img, +void DBaseStatusBar::DrawImage (FTexture *img, int x, int y, FRemapTable *translation) const { if (img != NULL) @@ -448,7 +456,7 @@ void FBaseStatusBar::DrawImage (FTexture *img, // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrawDimImage (FTexture *img, +void DBaseStatusBar::DrawDimImage (FTexture *img, int x, int y, bool dimmed) const { if (img != NULL) @@ -469,7 +477,7 @@ void FBaseStatusBar::DrawDimImage (FTexture *img, // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrawFadedImage (FTexture *img, +void DBaseStatusBar::DrawFadedImage (FTexture *img, int x, int y, fixed_t shade) const { if (img != NULL) @@ -491,7 +499,7 @@ void FBaseStatusBar::DrawFadedImage (FTexture *img, // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrawPartialImage (FTexture *img, int wx, int ww) const +void DBaseStatusBar::DrawPartialImage (FTexture *img, int wx, int ww) const { if (img != NULL) { @@ -511,7 +519,7 @@ void FBaseStatusBar::DrawPartialImage (FTexture *img, int wx, int ww) const // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrINumber (signed int val, int x, int y, int imgBase) const +void DBaseStatusBar::DrINumber (signed int val, int x, int y, int imgBase) const { int oldval; @@ -551,7 +559,7 @@ void FBaseStatusBar::DrINumber (signed int val, int x, int y, int imgBase) const // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrBNumber (signed int val, int x, int y, int size) const +void DBaseStatusBar::DrBNumber (signed int val, int x, int y, int size) const { bool neg; int i, w; @@ -611,7 +619,7 @@ void FBaseStatusBar::DrBNumber (signed int val, int x, int y, int size) const // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrSmallNumber (int val, int x, int y) const +void DBaseStatusBar::DrSmallNumber (int val, int x, int y) const { int digit = 0; @@ -642,7 +650,7 @@ void FBaseStatusBar::DrSmallNumber (int val, int x, int y) const // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrINumberOuter (signed int val, int x, int y, bool center, int w) const +void DBaseStatusBar::DrINumberOuter (signed int val, int x, int y, bool center, int w) const { bool negative = false; @@ -706,7 +714,7 @@ void FBaseStatusBar::DrINumberOuter (signed int val, int x, int y, bool center, // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrBNumberOuter (signed int val, int x, int y, int size) const +void DBaseStatusBar::DrBNumberOuter (signed int val, int x, int y, int size) const { int xpos; int w; @@ -813,7 +821,7 @@ void FBaseStatusBar::DrBNumberOuter (signed int val, int x, int y, int size) con // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrBNumberOuterFont (signed int val, int x, int y, int size) const +void DBaseStatusBar::DrBNumberOuterFont (signed int val, int x, int y, int size) const { int xpos; int w, v; @@ -914,7 +922,7 @@ void FBaseStatusBar::DrBNumberOuterFont (signed int val, int x, int y, int size) // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrSmallNumberOuter (int val, int x, int y, bool center) const +void DBaseStatusBar::DrSmallNumberOuter (int val, int x, int y, bool center) const { int digit = 0; @@ -946,7 +954,7 @@ void FBaseStatusBar::DrSmallNumberOuter (int val, int x, int y, bool center) con // //--------------------------------------------------------------------------- -void FBaseStatusBar::RefreshBackground () const +void DBaseStatusBar::RefreshBackground () const { int x, x2, y, ratio; @@ -981,7 +989,7 @@ void FBaseStatusBar::RefreshBackground () const // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrawCrosshair () +void DBaseStatusBar::DrawCrosshair () { static DWORD prevcolor = 0xffffffff; static int palettecolor = 0; @@ -1072,7 +1080,7 @@ void FBaseStatusBar::DrawCrosshair () // //--------------------------------------------------------------------------- -void FBaseStatusBar::FlashCrosshair () +void DBaseStatusBar::FlashCrosshair () { CrosshairSize = XHAIRPICKUPSIZE; } @@ -1083,7 +1091,7 @@ void FBaseStatusBar::FlashCrosshair () // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrawMessages (int bottom) const +void DBaseStatusBar::DrawMessages (int bottom) { DHUDMessage *msg = Messages; while (msg) @@ -1100,7 +1108,7 @@ void FBaseStatusBar::DrawMessages (int bottom) const // //--------------------------------------------------------------------------- -void FBaseStatusBar::Draw (EHudState state) +void DBaseStatusBar::Draw (EHudState state) { char line[64+10]; @@ -1254,7 +1262,7 @@ void FBaseStatusBar::Draw (EHudState state) } -void FBaseStatusBar::DrawLog () +void DBaseStatusBar::DrawLog () { int hudwidth, hudheight; @@ -1318,7 +1326,7 @@ void FBaseStatusBar::DrawLog () } } -bool FBaseStatusBar::MustDrawLog(EHudState) +bool DBaseStatusBar::MustDrawLog(EHudState) { return true; } @@ -1329,7 +1337,7 @@ bool FBaseStatusBar::MustDrawLog(EHudState) // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrawTopStuff (EHudState state) +void DBaseStatusBar::DrawTopStuff (EHudState state) { if (demoplayback && demover != DEMOGAMEVERSION) { @@ -1360,7 +1368,7 @@ void FBaseStatusBar::DrawTopStuff (EHudState state) // //--------------------------------------------------------------------------- -void FBaseStatusBar::DrawPowerups () +void DBaseStatusBar::DrawPowerups () { // Each icon gets a 32x32 block to draw itself in. int x, y; @@ -1388,7 +1396,7 @@ SV_AddBlend [RH] This is from Q2. ============= */ -void FBaseStatusBar::AddBlend (float r, float g, float b, float a, float v_blend[4]) +void DBaseStatusBar::AddBlend (float r, float g, float b, float a, float v_blend[4]) { float a2, a3; @@ -1409,7 +1417,7 @@ void FBaseStatusBar::AddBlend (float r, float g, float b, float a, float v_blend // //--------------------------------------------------------------------------- -void FBaseStatusBar::BlendView (float blend[4]) +void DBaseStatusBar::BlendView (float blend[4]) { int cnt; @@ -1480,7 +1488,7 @@ void FBaseStatusBar::BlendView (float blend[4]) (int)(blend[2] * 255.0f), (int)(blend[3] * 256.0f)); } -void FBaseStatusBar::DrawConsistancy () const +void DBaseStatusBar::DrawConsistancy () const { static bool firsttime = true; int i; @@ -1524,37 +1532,37 @@ void FBaseStatusBar::DrawConsistancy () const } } -void FBaseStatusBar::FlashItem (const PClass *itemtype) +void DBaseStatusBar::FlashItem (const PClass *itemtype) { } -void FBaseStatusBar::SetFace (void *) +void DBaseStatusBar::SetFace (void *) { } -void FBaseStatusBar::NewGame () +void DBaseStatusBar::NewGame () { } -void FBaseStatusBar::SetInteger (int pname, int param) +void DBaseStatusBar::SetInteger (int pname, int param) { } -void FBaseStatusBar::ShowPop (int popnum) +void DBaseStatusBar::ShowPop (int popnum) { ShowLog = (popnum == POP_Log && !ShowLog); } -void FBaseStatusBar::ReceivedWeapon (AWeapon *weapon) +void DBaseStatusBar::ReceivedWeapon (AWeapon *weapon) { } -void FBaseStatusBar::Serialize (FArchive &arc) +void DBaseStatusBar::Serialize (FArchive &arc) { arc << Messages; } -void FBaseStatusBar::ScreenSizeChanged () +void DBaseStatusBar::ScreenSizeChanged () { st_scale.Callback (); SB_state = screen->GetPageCount (); @@ -1576,7 +1584,7 @@ void FBaseStatusBar::ScreenSizeChanged () // //--------------------------------------------------------------------------- -AInventory *FBaseStatusBar::ValidateInvFirst (int numVisible) const +AInventory *DBaseStatusBar::ValidateInvFirst (int numVisible) const { AInventory *item; int i; @@ -1660,14 +1668,14 @@ AInventory *FBaseStatusBar::ValidateInvFirst (int numVisible) const //============================================================================ // -// FBaseStatusBar :: GetCurrentAmmo +// DBaseStatusBar :: GetCurrentAmmo // // Returns the types and amounts of ammo used by the current weapon. If the // weapon only uses one type of ammo, it is always returned as ammo1. // //============================================================================ -void FBaseStatusBar::GetCurrentAmmo (AAmmo *&ammo1, AAmmo *&ammo2, int &ammocount1, int &ammocount2) const +void DBaseStatusBar::GetCurrentAmmo (AAmmo *&ammo1, AAmmo *&ammo2, int &ammocount1, int &ammocount2) const { if (CPlayer->ReadyWeapon != NULL) { diff --git a/src/g_strife/strife_sbar.cpp b/src/g_strife/strife_sbar.cpp index 2583e36a..83cf9a37 100644 --- a/src/g_strife/strife_sbar.cpp +++ b/src/g_strife/strife_sbar.cpp @@ -180,10 +180,11 @@ void FHealthBar::FillBar (int min, int max, BYTE light, BYTE dark) } } -class FStrifeStatusBar : public FBaseStatusBar +class DStrifeStatusBar : public DBaseStatusBar { + DECLARE_CLASS(DStrifeStatusBar, DBaseStatusBar) public: - FStrifeStatusBar () : FBaseStatusBar (32) + DStrifeStatusBar () : DBaseStatusBar (32) { static const char *sharedLumpNames[] = { @@ -196,11 +197,11 @@ public: "INVFONG7", "INVFONG8", "INVFONG9" }; - FBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES); + DBaseStatusBar::Images.Init (sharedLumpNames, NUM_BASESB_IMAGES); DoCommonInit (); } - ~FStrifeStatusBar () + ~DStrifeStatusBar () { } @@ -216,7 +217,7 @@ public: void Draw (EHudState state) { - FBaseStatusBar::Draw (state); + DBaseStatusBar::Draw (state); if (state == HUD_Fullscreen) { @@ -235,7 +236,7 @@ public: void ShowPop (int popnum) { - FBaseStatusBar::ShowPop(popnum); + DBaseStatusBar::ShowPop(popnum); if (popnum == CurrentPop) { if (popnum == POP_Keys) @@ -310,7 +311,7 @@ private: void Tick () { - FBaseStatusBar::Tick (); + DBaseStatusBar::Tick (); if (ItemFlash > 0) { @@ -845,7 +846,9 @@ private: fixed_t ItemFlash; }; -FBaseStatusBar *CreateStrifeStatusBar () +IMPLEMENT_CLASS(DStrifeStatusBar); + +DBaseStatusBar *CreateStrifeStatusBar () { - return new FStrifeStatusBar; + return new DStrifeStatusBar; } diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index b29e0873..7db13ed7 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -295,6 +295,16 @@ void FGameConfigFile::DoGlobalSetup () vsync->ResetToDefault (); } } + if (last < 206) + { // spc_amp is now a float, not an int. + FBaseCVar *amp = FindCVar ("spc_amp", NULL); + if (amp != NULL) + { + UCVarValue val = amp->GetGenericRep(CVAR_Float); + val.Float /= 16.f; + amp->SetGenericRep(val, CVAR_Float); + } + } } } } @@ -515,7 +525,7 @@ FString FGameConfigFile::GetConfigPath (bool tryProg) char *pathval; FString path; - pathval = Args.CheckValue ("-config"); + pathval = Args->CheckValue ("-config"); if (pathval != NULL) return FString(pathval); @@ -568,7 +578,7 @@ FString FGameConfigFile::GetConfigPath (bool tryProg) if (path.IsEmpty()) { - if (Args.CheckParm ("-cdrom")) + if (Args->CheckParm ("-cdrom")) return CDROM_DIR "\\zdoom.ini"; path = progdir; @@ -615,7 +625,7 @@ void FGameConfigFile::AddAutoexec (DArgs *list, const char *game) FString path; #ifndef unix - if (Args.CheckParm ("-cdrom")) + if (Args->CheckParm ("-cdrom")) { path = CDROM_DIR "\\autoexec.cfg"; } diff --git a/src/gl/gl_data.cpp b/src/gl/gl_data.cpp index e9eed115..561e0f69 100644 --- a/src/gl/gl_data.cpp +++ b/src/gl/gl_data.cpp @@ -162,7 +162,7 @@ static void PrepareSectorData() { int i; int j; - DBoundingBox bbox; + FBoundingBox bbox; size_t /*ii,*/ jj; TArray undetermined; subsector_t * ss; diff --git a/src/gl/gl_dynlight.cpp b/src/gl/gl_dynlight.cpp index 8d545efe..1bcd058c 100644 --- a/src/gl/gl_dynlight.cpp +++ b/src/gl/gl_dynlight.cpp @@ -1022,7 +1022,7 @@ void gl_SetActorLights(AActor *actor) (LightAssociations[i]->Frame()==frame || LightAssociations[i]->Frame()==-1)) { // I'm skipping the single rotations because that really doesn't make sense! - if (count < actor->dynamiclights.Size()) light = (ADynamicLight*)actor->dynamiclights[count]; + if (count < actor->dynamiclights.Size()) light = barrier_cast(actor->dynamiclights[count]); else { light = Spawn(actor->x, actor->y, actor->z, NO_REPLACE); diff --git a/src/gl/gl_lights.h b/src/gl/gl_lights.h index 63d886c3..226e7f32 100644 --- a/src/gl/gl_lights.h +++ b/src/gl/gl_lights.h @@ -114,7 +114,7 @@ public: BYTE lighttype; bool owned; bool halo; - AActor *Owner; // NOTE: This is *NOT* subject to pointer cleanup!!! + AActor *Owner; // NOTE: This is *NOT* subject to pointer cleanup or garbage collection!!! // intermediate texture coordinate data // this is stored in the light object to avoid recalculating it @@ -249,9 +249,6 @@ enum STAT_DLIGHT=64 }; -extern TArray PointLights; - - // // Light helper methods diff --git a/src/i_net.cpp b/src/i_net.cpp index 7c4b38d9..6e080df8 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -564,7 +564,7 @@ void HostGame (int i) int node; int gotack[MAXNETNODES+1]; - if ((i == Args.NumArgs() - 1) || !(numplayers = atoi (Args.GetArg(i+1)))) + if ((i == Args->NumArgs() - 1) || !(numplayers = atoi (Args->GetArg(i+1)))) { // No player count specified, assume 2 numplayers = 2; } @@ -742,15 +742,15 @@ bool Guest_WaitForOthers (void *userdata) void JoinGame (int i) { - if ((i == Args.NumArgs() - 1) || - (Args.GetArg(i+1)[0] == '-') || - (Args.GetArg(i+1)[0] == '+')) + if ((i == Args->NumArgs() - 1) || + (Args->GetArg(i+1)[0] == '-') || + (Args->GetArg(i+1)[0] == '+')) I_FatalError ("You need to specify the host machine's address"); StartNetwork (true); // Host is always node 1 - BuildAddress (&sendaddress[1], Args.GetArg(i+1)); + BuildAddress (&sendaddress[1], Args->GetArg(i+1)); sendplayer[1] = 0; doomcom.numnodes = 2; @@ -789,7 +789,7 @@ void I_InitNetwork (void) memset (&doomcom, 0, sizeof(doomcom)); // set up for network - v = Args.CheckValue ("-dup"); + v = Args->CheckValue ("-dup"); if (v) { doomcom.ticdup = clamp (atoi (v), 1, MAXTICDUP); @@ -799,12 +799,12 @@ void I_InitNetwork (void) doomcom.ticdup = 1; } - if (Args.CheckParm ("-extratic")) + if (Args->CheckParm ("-extratic")) doomcom.extratics = 1; else doomcom.extratics = 0; - v = Args.CheckValue ("-port"); + v = Args->CheckValue ("-port"); if (v) { DOOMPORT = atoi (v); @@ -814,11 +814,11 @@ void I_InitNetwork (void) // parse network game options, // player 1: -host // player x: -join - if ( (i = Args.CheckParm ("-host")) ) + if ( (i = Args->CheckParm ("-host")) ) { HostGame (i); } - else if ( (i = Args.CheckParm ("-join")) ) + else if ( (i = Args->CheckParm ("-join")) ) { JoinGame (i); } diff --git a/src/m_alloc.cpp b/src/m_alloc.cpp index 897f2e54..4d9e0fbf 100644 --- a/src/m_alloc.cpp +++ b/src/m_alloc.cpp @@ -33,18 +33,8 @@ */ #include "i_system.h" -#include "stats.h" #include -ADD_STAT(mem) -{ - FString out; - out.Format("Alloc: %uKB", (AllocBytes + 1023) >> 10); - return out; -} - -size_t AllocBytes; - #ifndef _MSC_VER #define _NORMAL_BLOCK 0 #define _malloc_dbg(s,b,f,l) malloc(s) @@ -62,7 +52,7 @@ void *M_Malloc(size_t size) if (block == NULL) I_FatalError("Could not malloc %u bytes", size); - AllocBytes += _msize(block); + GC::AllocBytes += _msize(block); return block; } @@ -70,14 +60,14 @@ void *M_Realloc(void *memblock, size_t size) { if (memblock != NULL) { - AllocBytes -= _msize(memblock); + GC::AllocBytes -= _msize(memblock); } void *block = realloc(memblock, size); if (block == NULL) { I_FatalError("Could not realloc %u bytes", size); } - AllocBytes += _msize(block); + GC::AllocBytes += _msize(block); return block; } #else @@ -92,7 +82,7 @@ void *M_Malloc_Dbg(size_t size, const char *file, int lineno) if (block == NULL) I_FatalError("Could not malloc %u bytes", size); - AllocBytes += _msize(block); + GC::AllocBytes += _msize(block); return block; } @@ -100,14 +90,14 @@ void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno) { if (memblock != NULL) { - AllocBytes -= _msize(memblock); + GC::AllocBytes -= _msize(memblock); } void *block = _realloc_dbg(memblock, size, _NORMAL_BLOCK, file, lineno); if (block == NULL) { I_FatalError("Could not realloc %u bytes", size); } - AllocBytes += _msize(block); + GC::AllocBytes += _msize(block); return block; } #endif @@ -116,7 +106,7 @@ void M_Free (void *block) { if (block != NULL) { - AllocBytes -= _msize(block); + GC::AllocBytes -= _msize(block); free(block); } } diff --git a/src/m_alloc.h b/src/m_alloc.h index 8d7866a7..17a6adeb 100644 --- a/src/m_alloc.h +++ b/src/m_alloc.h @@ -53,7 +53,4 @@ void *M_Realloc (void *memblock, size_t size); void M_Free (void *memblock); -// Number of bytes currently allocated through M_Malloc/M_Realloc -extern size_t AllocBytes; - #endif //__M_ALLOC_H__ diff --git a/src/m_argv.h b/src/m_argv.h index 65973de9..b0d44076 100644 --- a/src/m_argv.h +++ b/src/m_argv.h @@ -71,6 +71,6 @@ private: void CopyArgs (int argc, char **argv); }; -extern DArgs Args; +extern DArgs *Args; #endif //__M_ARGV_H__ diff --git a/src/m_bbox.cpp b/src/m_bbox.cpp index 42b54361..7be4e90c 100644 --- a/src/m_bbox.cpp +++ b/src/m_bbox.cpp @@ -26,20 +26,18 @@ #include "m_bbox.h" -IMPLEMENT_CLASS (DBoundingBox) - -DBoundingBox::DBoundingBox () +FBoundingBox::FBoundingBox () { ClearBox (); } -void DBoundingBox::ClearBox () +void FBoundingBox::ClearBox () { m_Box[BOXTOP] = m_Box[BOXRIGHT] = FIXED_MIN; m_Box[BOXBOTTOM] = m_Box[BOXLEFT] = FIXED_MAX; } -void DBoundingBox::AddToBox (fixed_t x, fixed_t y) +void FBoundingBox::AddToBox (fixed_t x, fixed_t y) { if (x < m_Box[BOXLEFT]) m_Box[BOXLEFT] = x; diff --git a/src/m_bbox.h b/src/m_bbox.h index 0ef2853a..7a813ffa 100644 --- a/src/m_bbox.h +++ b/src/m_bbox.h @@ -23,7 +23,6 @@ #define __M_BBOX_H__ #include "doomtype.h" -#include "dobject.h" #include "m_fixed.h" @@ -37,11 +36,10 @@ enum }; // bbox coordinates -class DBoundingBox : public DObject +class FBoundingBox { - DECLARE_CLASS (DBoundingBox, DObject) public: - DBoundingBox(); + FBoundingBox(); void ClearBox (); void AddToBox (fixed_t x, fixed_t y); diff --git a/src/m_misc.cpp b/src/m_misc.cpp index 3909d32e..7246fcdf 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -150,9 +150,9 @@ void M_FindResponseFile (void) { int i; - for (i = 1; i < Args.NumArgs(); i++) + for (i = 1; i < Args->NumArgs(); i++) { - if (Args.GetArg(i)[0] == '@') + if (Args->GetArg(i)[0] == '@') { char **argv; char *file; @@ -165,14 +165,14 @@ void M_FindResponseFile (void) int index; // READ THE RESPONSE FILE INTO MEMORY - handle = fopen (Args.GetArg(i) + 1,"rb"); + handle = fopen (Args->GetArg(i) + 1,"rb"); if (!handle) { // [RH] Make this a warning, not an error. - Printf ("No such response file (%s)!", Args.GetArg(i) + 1); + Printf ("No such response file (%s)!", Args->GetArg(i) + 1); continue; } - Printf ("Found response file %s!\n", Args.GetArg(i) + 1); + Printf ("Found response file %s!\n", Args->GetArg(i) + 1); fseek (handle, 0, SEEK_END); size = ftell (handle); fseek (handle, 0, SEEK_SET); @@ -182,7 +182,7 @@ void M_FindResponseFile (void) fclose (handle); argsize = ParseCommandLine (file, &argcinresp, NULL); - argc = argcinresp + Args.NumArgs() - 1; + argc = argcinresp + Args->NumArgs() - 1; if (argc != 0) { @@ -191,21 +191,21 @@ void M_FindResponseFile (void) ParseCommandLine (file, NULL, argv+i); for (index = 0; index < i; ++index) - argv[index] = Args.GetArg (index); + argv[index] = Args->GetArg (index); - for (index = i + 1, i += argcinresp; index < Args.NumArgs (); ++index) - argv[i++] = Args.GetArg (index); + for (index = i + 1, i += argcinresp; index < Args->NumArgs (); ++index) + argv[i++] = Args->GetArg (index); - DArgs newargs (i, argv); - Args = newargs; + Args->Destroy(); + Args = new DArgs(i, argv); } delete[] file; // DISPLAY ARGS - Printf ("%d command-line args:\n", Args.NumArgs ()); - for (k = 1; k < Args.NumArgs (); k++) - Printf ("%s\n", Args.GetArg (k)); + Printf ("%d command-line args:\n", Args->NumArgs ()); + for (k = 1; k < Args->NumArgs (); k++) + Printf ("%s\n", Args->GetArg (k)); break; } @@ -652,7 +652,7 @@ void M_ScreenShot (const char *filename) if (filename == NULL || filename[0] == '\0') { #ifndef unix - if (Args.CheckParm ("-cdrom")) + if (Args->CheckParm ("-cdrom")) { autoname = CDROM_DIR "\\"; } diff --git a/src/modplug/load_669.cpp b/src/modplug/load_669.cpp deleted file mode 100644 index a0dbfabd..00000000 --- a/src/modplug/load_669.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque , - * Adam Goode (endian and char fixes for PPC) -*/ - -//////////////////////////////////////////////////////////// -// 669 Composer / UNIS 669 module loader -//////////////////////////////////////////////////////////// - -#include "stdafx.h" -#include "sndfile.h" - -//#pragma warning(disable:4244) - -typedef struct tagFILEHEADER669 -{ - WORD sig; // 'if' or 'JN' - signed char songmessage[108]; // Song Message - BYTE samples; // number of samples (1-64) - BYTE patterns; // number of patterns (1-128) - BYTE restartpos; - BYTE orders[128]; - BYTE tempolist[128]; - BYTE breaks[128]; -} FILEHEADER669; - - -typedef struct tagSAMPLE669 -{ - BYTE filename[13]; - BYTE length[4]; // when will somebody think about DWORD align ??? - BYTE loopstart[4]; - BYTE loopend[4]; -} SAMPLE669; - - -BOOL CSoundFile::Read669(const BYTE *lpStream, DWORD dwMemLength) -//--------------------------------------------------------------- -{ - BOOL b669Ext; - const FILEHEADER669 *pfh = (const FILEHEADER669 *)lpStream; - const SAMPLE669 *psmp = (const SAMPLE669 *)(lpStream + 0x1F1); - DWORD dwMemPos = 0; - - if ((!lpStream) || (dwMemLength < sizeof(FILEHEADER669))) return FALSE; - if ((bswapLE16(pfh->sig) != 0x6669) && (bswapLE16(pfh->sig) != 0x4E4A)) return FALSE; - b669Ext = (bswapLE16(pfh->sig) == 0x4E4A) ? TRUE : FALSE; - if ((!pfh->samples) || (pfh->samples > 64) || (pfh->restartpos >= 128) - || (!pfh->patterns) || (pfh->patterns > 128)) return FALSE; - DWORD dontfuckwithme = 0x1F1 + pfh->samples * sizeof(SAMPLE669) + pfh->patterns * 0x600; - if (dontfuckwithme > dwMemLength) return FALSE; - for (UINT ichk=0; ichksamples; ichk++) - { - DWORD len = bswapLE32(*((DWORD *)(&psmp[ichk].length))); - dontfuckwithme += len; - } - if (dontfuckwithme > dwMemLength) return FALSE; - // That should be enough checking: this must be a 669 module. - m_nType = MOD_TYPE_669; - m_dwSongFlags |= SONG_LINEARSLIDES; - m_nMinPeriod = 28 << 2; - m_nMaxPeriod = 1712 << 3; - m_nDefaultTempo = 125; - m_nDefaultSpeed = 6; - m_nChannels = 8; - memcpy(m_szNames[0], pfh->songmessage, 16); - m_nSamples = pfh->samples; - for (UINT nins=1; nins<=m_nSamples; nins++, psmp++) - { - DWORD len = bswapLE32(*((DWORD *)(&psmp->length))); - DWORD loopstart = bswapLE32(*((DWORD *)(&psmp->loopstart))); - DWORD loopend = bswapLE32(*((DWORD *)(&psmp->loopend))); - if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH; - if ((loopend > len) && (!loopstart)) loopend = 0; - if (loopend > len) loopend = len; - if (loopstart + 4 >= loopend) loopstart = loopend = 0; - Ins[nins].nLength = len; - Ins[nins].nLoopStart = loopstart; - Ins[nins].nLoopEnd = loopend; - if (loopend) Ins[nins].uFlags |= CHN_LOOP; - memcpy(m_szNames[nins], psmp->filename, 13); - Ins[nins].nVolume = 256; - Ins[nins].nGlobalVol = 64; - Ins[nins].nPan = 128; - } - // Song Message - m_lpszSongComments = new char[109]; - memcpy(m_lpszSongComments, pfh->songmessage, 108); - m_lpszSongComments[108] = 0; - // Reading Orders - memcpy(Order, pfh->orders, 128); - m_nRestartPos = pfh->restartpos; - if (Order[m_nRestartPos] >= pfh->patterns) m_nRestartPos = 0; - // Reading Pattern Break Locations - for (UINT npan=0; npan<8; npan++) - { - ChnSettings[npan].nPan = (npan & 1) ? 0x30 : 0xD0; - ChnSettings[npan].nVolume = 64; - } - // Reading Patterns - dwMemPos = 0x1F1 + pfh->samples * 25; - for (UINT npat=0; npatpatterns; npat++) - { - Patterns[npat] = AllocatePattern(64, m_nChannels); - if (!Patterns[npat]) break; - PatternSize[npat] = 64; - MODCOMMAND *m = Patterns[npat]; - const BYTE *p = lpStream + dwMemPos; - for (UINT row=0; row<64; row++) - { - MODCOMMAND *mspeed = m; - if ((row == pfh->breaks[npat]) && (row != 63)) - { - for (UINT i=0; i<8; i++) - { - m[i].command = CMD_PATTERNBREAK; - m[i].param = 0; - } - } - for (UINT n=0; n<8; n++, m++, p+=3) - { - UINT note = p[0] >> 2; - UINT instr = ((p[0] & 0x03) << 4) | (p[1] >> 4); - UINT vol = p[1] & 0x0F; - if (p[0] < 0xFE) - { - m->note = note + 37; - m->instr = instr + 1; - } - if (p[0] <= 0xFE) - { - m->volcmd = VOLCMD_VOLUME; - m->vol = (vol << 2) + 2; - } - if (p[2] != 0xFF) - { - UINT command = p[2] >> 4; - UINT param = p[2] & 0x0F; - switch(command) - { - case 0x00: command = CMD_PORTAMENTOUP; break; - case 0x01: command = CMD_PORTAMENTODOWN; break; - case 0x02: command = CMD_TONEPORTAMENTO; break; - case 0x03: command = CMD_MODCMDEX; param |= 0x50; break; - case 0x04: command = CMD_VIBRATO; param |= 0x40; break; - case 0x05: if (param) command = CMD_SPEED; else command = 0; param += 2; break; - case 0x06: if (param == 0) { command = CMD_PANNINGSLIDE; param = 0xFE; } else - if (param == 1) { command = CMD_PANNINGSLIDE; param = 0xEF; } else - command = 0; - break; - default: command = 0; - } - if (command) - { - if (command == CMD_SPEED) mspeed = NULL; - m->command = command; - m->param = param; - } - } - } - if ((!row) && (mspeed)) - { - for (UINT i=0; i<8; i++) if (!mspeed[i].command) - { - mspeed[i].command = CMD_SPEED; - mspeed[i].param = pfh->tempolist[npat] + 2; - break; - } - } - } - dwMemPos += 0x600; - } - // Reading Samples - for (UINT n=1; n<=m_nSamples; n++) - { - UINT len = Ins[n].nLength; - if (dwMemPos >= dwMemLength) break; - if (len > 4) ReadSample(&Ins[n], RS_PCM8U, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos); - dwMemPos += len; - } - return TRUE; -} - - diff --git a/src/modplug/load_abc.cpp b/src/modplug/load_abc.cpp deleted file mode 100644 index 00c31767..00000000 --- a/src/modplug/load_abc.cpp +++ /dev/null @@ -1,5114 +0,0 @@ -/* - - MikMod Sound System - - By Jake Stine of Divine Entertainment (1996-2000) - - Support: - If you find problems with this code, send mail to: - air@divent.org - - Distribution / Code rights: - Use this source code in any fashion you see fit. Giving me credit where - credit is due is optional, depending on your own levels of integrity and - honesty. - - ----------------------------------------- - Module: LOAD_ABC - - ABC module loader. - by Peter Grootswagers (2006) - - - Portability: - All systems - all compilers (hopefully) -*/ - -#include -#include -#include -#include -#include -//#include // for sleep - -#ifdef NEWMIKMOD -#include "mikmod.h" -#include "uniform.h" -typedef UBYTE BYTE; -typedef UWORD WORD; -#else -#include "stdafx.h" -#include "sndfile.h" -#endif - -#include "load_pat.h" - -#define MAXABCINCLUDES 8 -#define MAXCHORDNAMES 80 -#define ABC_ENV_DUMPTRACKS "MMABC_DUMPTRACKS" -#define ABC_ENV_NORANDOMPICK "MMABC_NO_RANDOM_PICK" - -// gchords use tracks with vpos 1 thru 7 -// drums use track with vpos 8 -// voice chords use vpos 0 and vpos from 11 up -#define GCHORDBPOS 1 -#define GCHORDFPOS 2 -#define GCHORDCPOS 3 -#define DRUMPOS 8 -#define DRONEPOS1 9 -#define DRONEPOS2 10 - -// in the patterns a whole note at unmodified tempo is 16 rows -#define ROWSPERNOTE 16 -// a 1/64-th note played in triool equals a 1/96-th note, to be able -// to play them and also to play the 1/64-th we need a resolution of 192 -// because 2/192 = 1/96 and 3/192 = 1/64 -#define RESOLUTION 192 - -#pragma pack(1) - -/************************************************************************** -**************************************************************************/ -#ifdef NEWMIKMOD -static char ABC_Version[] = "ABC+2.0 (draft IV)"; -#endif - -typedef enum { - note, - octave, - smpno, - volume, - effect, - effoper -} ABCEVENT_X_NOTE; - -typedef enum { - none, - trill, - bow, - accent -} ABCEVENT_X_EFFECT; - -typedef enum { - cmdflag, - command, - chordnum, - chordnote, - chordbase, - jumptype -} ABCEVENT_X_CMD; - -typedef enum { - cmdsegno = '$', - cmdcapo = 'B', - cmdchord = 'C', - cmdfine = 'F', - cmdhide = 'H', - cmdjump = 'J', - cmdloop = 'L', - cmdcoda = 'O', - cmdpartbrk = 'P', - cmdsync = 'S', - cmdtempo = 'T', - cmdvariant = 'V', - cmdtocoda = 'X' -} ABCEVENT_CMD; - -typedef enum { - jumpnormal, - jumpfade, - jumpdacapo, - jumpdcfade, - jumpdasegno, - jumpdsfade, - jumpfine, - jumptocoda, - jumpvariant, - jumpnot -} ABCEVENT_JUMPTYPE; - -typedef struct _ABCEVENT -{ - struct _ABCEVENT *next; - ULONG tracktick; - union { - BYTE par[6]; - struct { - BYTE flg; - BYTE cmd; - ULONG lpar; // for variant selections, bit pattern - }; - }; - BYTE part; - BYTE tiednote; -} ABCEVENT; - -typedef struct _ABCTRACK -{ - struct _ABCTRACK *next; - ABCEVENT *head; - ABCEVENT *tail; - ABCEVENT *capostart; - ABCEVENT *tienote; - int transpose; - int octave_shift; - ULONG slidevoltime; // for crescendo and diminuendo - int slidevol; // -2:fade away, -1:diminuendo, 0:none, +1:crescendo - BYTE vno; // 0 is track is free for use, from previous song in multi-songbook - BYTE vpos; // 0 is main voice, other is subtrack for gchords, gchords or drumnotes - BYTE tiedvpos; - BYTE mute; - BYTE chan; // 10 is percussion channel, any other is melodic channel - BYTE volume; - BYTE instr; // current instrument for this track - BYTE legato; - char v[22]; // first twenty characters are significant -} ABCTRACK; - -typedef struct _ABCMACRO -{ - struct _ABCMACRO *next; - char *name; - char *subst; - char *n; -} ABCMACRO; - -/************************************************************************** -**************************************************************************/ - -typedef struct _ABCHANDLE -{ -#ifdef NEWMIKMOD - MM_ALLOC *allochandle; - MM_ALLOC *macrohandle; - MM_ALLOC *trackhandle; - MM_ALLOC *ho; -#endif - ABCMACRO *macro; - ABCMACRO *umacro; - ABCTRACK *track; - long int pickrandom; - unsigned int len; - int speed; - char *line; - char *beatstring; - BYTE beat[4]; // a:first note, b:strong notes, c:weak notes, n:strong note every n - char gchord[80]; // last setting for gchord - char drum[80]; // last setting for drum - char drumins[80]; // last setting for drum - char drumvol[80]; // last setting for drum - ULONG barticks; - // parse variables, declared here to avoid parameter pollution - int abcchordvol, abcchordprog, abcbassvol, abcbassprog; - int ktrans; - int drumon, gchordon, droneon; - int dronegm, dronepitch[2], dronevol[2]; - ABCTRACK *tp, *tpc, *tpr; - ULONG tracktime; -} ABCHANDLE; - -static int global_voiceno, global_octave_shift, global_tempo_factor, global_tempo_divider; -static char global_part; -static ULONG global_songstart; -/* Named guitar chords */ -static char chordname[MAXCHORDNAMES][8]; -static int chordnotes[MAXCHORDNAMES][6]; -static int chordlen[MAXCHORDNAMES]; -static int chordsnamed = 0; - -static char *sig[] = { - " C D EF G A Bc d ef g a b", // 7 sharps C# - " C D EF G AB c d ef g ab ", // 6 sharps F# - " C DE F G AB c de f g ab ", // 5 sharps B - " C DE F GA B c de f ga b ", // 4 sharps E - " CD E F GA B cd e f ga b ", // 3 sharps A - " CD E FG A B cd e fg a b ", // 2 sharps D - " C D E FG A Bc d e fg a b", // 1 sharps G - " C D EF G A Bc d ef g a b", // 0 sharps C - " C D EF G AB c d ef g ab ", // 1 flats F - " C DE F G AB c de f g ab ", // 2 flats Bb - " C DE F GA B c de f ga b ", // 3 flats Eb - " CD E F GA B cd e f ga b ", // 4 flats Ab - " CD E FG A B cd e fg a b ", // 5 flats Db - "C D E FG A Bc d e fg a b ", // 6 flats Gb - "C D EF G A Bc d ef g a b ", // 7 flats Cb -// 0123456789012345678901234 -}; - -static char *keySigs[] = { -/* 0....:....1....:....2....:....3....:....4....:....5. */ - "7 sharps: C# A#m G#Mix D#Dor E#Phr F#Lyd B#Loc ", - "6 sharps: F# D#m C#Mix G#Dor A#Phr BLyd E#Loc ", - "5 sharps: B G#m F#Mix C#Dor D#Phr ELyd A#Loc ", - "4 sharps: E C#m BMix F#Dor G#Phr ALyd D#Loc ", - "3 sharps: A F#m EMix BDor C#Phr DLyd G#Loc ", - "2 sharps: D Bm AMix EDor F#Phr GLyd C#Loc ", - "1 sharp : G Em DMix ADor BPhr CLyd F#Loc ", - "0 sharps: C Am GMix DDor EPhr FLyd BLoc ", - "1 flat : F Dm CMix GDor APhr BbLyd ELoc ", - "2 flats : Bb Gm FMix CDor DPhr EbLyd ALoc ", - "3 flats : Eb Cm BbMix FDor GPhr AbLyd DLoc ", - "4 flats : Ab Fm EbMix BbDor CPhr DbLyd GLoc ", - "5 flats : Db Bbm AbMix EbDor FPhr GbLyd CLoc ", - "6 flats : Gb Ebm DbMix AbDor BbPhr CbLyd FLoc ", - "7 flats : Cb Abm GbMix DbDor EbPhr FbLyd BbLoc ", - 0 -}; - -// local prototypes -static int abc_getnumber(const char *p, int *number); -static ABCTRACK *abc_locate_track(ABCHANDLE *h, const char *voice, int pos); -static void abc_add_event(ABCHANDLE *h, ABCTRACK *tp, ABCEVENT *e); -static void abc_add_setloop(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime); -static void abc_add_setjumploop(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, ABCEVENT_JUMPTYPE j); -static ULONG abc_pattracktime(ABCHANDLE *h, ULONG tracktime); -static int abc_patno(ABCHANDLE *h, ULONG tracktime); - -static void abc_message(const char *s1, const char *s2) -{ - char txt[256]; - if( strlen(s1) + strlen(s2) > 255 ) return; - sprintf(txt, s1, s2); -#ifdef NEWMIKMOD - _mmlog(txt); -#else - fprintf(stderr, "load_abc > %s\n", txt); -#endif -} - -static ULONG modticks(ULONG abcticks) -{ - return abcticks / RESOLUTION; -} - -static ULONG abcticks(ULONG modticks) -{ - return modticks * RESOLUTION; -} - -static ULONG notelen_notediv_to_ticks(int speed, int len, int div) -{ - ULONG u; - u = (ROWSPERNOTE * RESOLUTION * speed * len * global_tempo_factor) / (div * global_tempo_divider); - return u; -} - -static void abc_dumptracks(ABCHANDLE *h, const char *p) -{ - ABCTRACK *t; - ABCEVENT *e; - int n,pat,row,tck; - char nn[3]; - if( !h ) return; - for( t=h->track; t; t=t->next ) { - printf("track %d.%d chan=%d %s\n", (int)(t->vno), (int)(t->vpos), - (int)(t->chan), (char *)(t->v)); - if( strcmp(p,"nonotes") ) - n = 1; - else - n = 0; - for( e=t->head; e; e=e->next ) { - tck = modticks(e->tracktick); - row = tck / h->speed; - pat = row / 64; - tck = tck % h->speed; - row = row % 64; - nn[0] = ( e->tracktick % abcticks(h->speed * 64) ) ? ' ': '-'; - if( e->flg == 1 ) { - printf(" %6d.%02d.%d%c%c %d.%d %s ", - pat, row, tck, nn[0], (int)(e->part), (int)(t->vno), - (int)(t->vpos), (char *)(t->v)); - if( e->cmd == cmdchord ) { - nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[chordnote]]; - nn[1] = "b # # # # # # # # # # #"[e->par[chordnote]]; - nn[2] = '\0'; - if( isspace(nn[1]) ) nn[1] = '\0'; - printf("CMD %c: gchord %s%s", - (char)(e->cmd), nn, chordname[e->par[chordnum]]); - if( e->par[chordbase] != e->par[chordnote] ) { - nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[chordbase]]; - nn[1] = "b # # # # # # # # # # #"[e->par[chordbase]]; - nn[2] = '\0'; - printf("/%s", nn); - } - printf("\n"); - } - else - printf("CMD %c @%p 0x%08lX\n", - (char)(e->cmd), e, - (unsigned long)(e->lpar)); - if( strcmp(p,"nonotes") ) - n = 1; - else - n = 0; - } - else if( n ) { - printf(" %6d.%02d.%d%c%c %d.%d %s ", pat, row, tck, nn[0], e->part, t->vno, t->vpos, t->v); - if( e->par[note] ) { - nn[0] = "CCCDDEFFGGAABccddeffggaabb"[e->par[note]-23]; - nn[1] = "b # # # # # # # # # # #"[e->par[note]-23]; - nn[2] = '\0'; - } - else strcpy(nn,"--"); - printf("NOTE %s octave %d inst %s vol %03d\n", - nn, e->par[octave], pat_gm_name(pat_smptogm(e->par[smpno])),e->par[volume]); - if( strcmp(p,"all") ) - n = 0; - } - } - } -} - -#ifdef NEWMIKMOD - -#define MMFILE MMSTREAM -#define mmfgetc(x) _mm_read_SBYTE(x) -#define mmfeof(x) _mm_feof(x) -#define mmfgets(buf,sz,f) _mm_fgets(f,buf,sz) -#define mmftell(x) _mm_ftell(x) -#define mmfseek(f,p,w) _mm_fseek(f,p,w) -#define mmfopen(s,m) _mm_fopen(s,m) -#define mmfclose(f) _mm_fclose(f) - -#else - -#define MMSTREAM FILE -#define _mm_fopen(name,mode) fopen(name,mode) -#define _mm_fgets(f,buf,sz) fgets(buf,sz,f) -#define _mm_fseek(f,pos,whence) fseek(f,pos,whence) -#define _mm_ftell(f) ftell(f) -#define _mm_read_UBYTES(buf,sz,f) fread(buf,sz,1,f) -#define _mm_read_SBYTES(buf,sz,f) fread(buf,sz,1,f) -#define _mm_feof(f) feof(f) -#define _mm_fclose(f) fclose(f) -#define DupStr(h,buf,sz) strdup(buf) -#define _mm_calloc(h,n,sz) calloc(n,sz) -#define _mm_recalloc(h,buf,sz,elsz) realloc(buf,sz) -#define _mm_free(h,p) free(p) - -typedef struct { - char *mm; - int sz; - int pos; -} MMFILE; - -static MMFILE *mmfopen(const char *name, const char *mode) -{ - FILE *fp; - MMFILE *mmfile; - long len; - if( *mode != 'r' ) return NULL; - fp = fopen(name, mode); - if( !fp ) return NULL; - fseek(fp, 0, SEEK_END); - len = ftell(fp); - mmfile = (MMFILE *)malloc(len+sizeof(MMFILE)); - if( !mmfile ) return NULL; - fseek(fp, 0, SEEK_SET); - fread(&mmfile[1],1,len,fp); - fclose(fp); - mmfile->mm = (char *)&mmfile[1]; - mmfile->sz = len; - mmfile->pos = 0; - return mmfile; -} - -static void mmfclose(MMFILE *mmfile) -{ - free(mmfile); -} - -static bool mmfeof(MMFILE *mmfile) -{ - if( mmfile->pos < 0 ) return TRUE; - if( mmfile->pos < mmfile->sz ) return FALSE; - return TRUE; -} - -static int mmfgetc(MMFILE *mmfile) -{ - int b; - if( mmfeof(mmfile) ) return EOF; - b = mmfile->mm[mmfile->pos]; - mmfile->pos++; - if( b=='\r' && mmfile->mm[mmfile->pos] == '\n' ) { - b = '\n'; - mmfile->pos++; - } - return b; -} - -static void mmfgets(char buf[], unsigned int bufsz, MMFILE *mmfile) -{ - int i,b; - for( i=0; i<(int)bufsz-1; i++ ) { - b = mmfgetc(mmfile); - if( b==EOF ) break; - buf[i] = b; - if( b == '\n' ) break; - } - buf[i] = '\0'; -} - -static long mmftell(MMFILE *mmfile) -{ - return mmfile->pos; -} - -static void mmfseek(MMFILE *mmfile, long p, int whence) -{ - switch(whence) { - case SEEK_SET: - mmfile->pos = p; - break; - case SEEK_CUR: - mmfile->pos += p; - break; - case SEEK_END: - mmfile->pos = mmfile->sz + p; - break; - } -} -#endif - -// ===================================================================================== -static ABCEVENT *abc_new_event(ABCHANDLE *h, ULONG abctick, const char data[]) -// ===================================================================================== -{ - ABCEVENT *retval; - int i; - - retval = (ABCEVENT *)_mm_calloc(h->trackhandle, 1,sizeof(ABCEVENT)); - retval->next = NULL; - retval->tracktick = abctick; - for( i=0; i<6; i++ ) - retval->par[i] = data[i]; - retval->part = global_part; - retval->tiednote = 0; - return retval; -} - -// ============================================================================= -static ABCEVENT *abc_copy_event(ABCHANDLE *h, ABCEVENT *se) -// ============================================================================= -{ - ABCEVENT *e; - e = (ABCEVENT *)_mm_calloc(h->trackhandle, 1,sizeof(ABCEVENT)); - e->next = NULL; - e->tracktick = se->tracktick; - e->flg = se->flg; - e->cmd = se->cmd; - e->lpar = se->lpar; - e->part = se->part; - return e; -} - -// ============================================================================= -static void abc_new_macro(ABCHANDLE *h, const char *m) -// ============================================================================= -{ - ABCMACRO *retval; - const char *p; - char buf[256],*q; - for( p=m; *p && isspace(*p); p++ ) ; - for( q=buf; *p && *p != '='; p++ ) - *q++ = *p; - if( q != buf ) - while( isspace(q[-1]) ) q--; - *q = '\0'; - retval = (ABCMACRO *)_mm_calloc(h->macrohandle, 1,sizeof(ABCTRACK)); - retval->name = DupStr(h->macrohandle, buf,strlen(buf)); - retval->n = strrchr(retval->name, 'n'); // for transposing macro's - for( p++; *p && isspace(*p); p++ ) ; - strncpy(buf,p,200); - for( q=&buf[strlen(buf)-1]; q!=buf && isspace(*q); q-- ) *q = '\0'; - retval->subst = DupStr(h->macrohandle, buf, strlen(buf)); - retval->next = h->macro; - h->macro = retval; -} - -// ============================================================================= -static void abc_new_umacro(ABCHANDLE *h, const char *m) -// ============================================================================= -{ - ABCMACRO *retval, *mp; - const char *p; - char buf[256], let[2], *q; - for( p=m; *p && isspace(*p); p++ ) ; - for( q=buf; *p && *p != '='; p++ ) - *q++ = *p; - if( q != buf ) - while( isspace(q[-1]) ) q--; - *q = '\0'; - if( strlen(buf) > 1 || strchr("~HIJKLMNOPQRSTUVWXY",toupper(buf[0])) == 0 || strchr("xy",buf[0]) ) return; - strcpy(let,buf); - for( p++; *p && isspace(*p); p++ ) ; - strncpy(buf,p,200); - for( q=&buf[strlen(buf)-1]; q!=buf && isspace(*q); q-- ) *q = '\0'; - for( q=buf; *q; q++ ) if( *q == '!' ) *q = '+'; // translate oldstyle to newstyle - if( !strcmp(buf,"+nil+") ) { // delete a macro - mp = NULL; - for( retval=h->umacro; retval; retval = retval->next ) { - if( retval->name[0] == let[0] ) { // delete this one - if( mp ) mp->next = retval->next; - else h->umacro = retval->next; - _mm_free(h->macrohandle, retval); - return; - } - mp = retval; - } - return; - } - retval = (ABCMACRO *)_mm_calloc(h->macrohandle, 1,sizeof(ABCTRACK)); - retval->name = DupStr(h->macrohandle, let,1); - retval->subst = DupStr(h->macrohandle, buf, strlen(buf)); - retval->n = 0; - retval->next = h->umacro; // by placing it up front we mask out the old macro until we +nil+ it - h->umacro = retval; -} - -// ============================================================================= -static ABCTRACK *abc_new_track(ABCHANDLE *h, const char *voice, int pos) -// ============================================================================= -{ - ABCTRACK *retval; - if( !pos ) global_voiceno++; - retval = (ABCTRACK *)_mm_calloc(h->trackhandle, 1,sizeof(ABCTRACK)); - retval->next = NULL; - retval->vno = global_voiceno; - retval->vpos = pos; - retval->tiedvpos = pos; - retval->instr = 1; - strncpy(retval->v, voice, 20); - retval->v[20] = '\0'; - retval->head = NULL; - retval->tail = NULL; - retval->capostart = NULL; - retval->tienote = NULL; - retval->mute = 0; - retval->chan = 0; - retval->transpose = 0; - retval->volume = h->track? h->track->volume: 120; - retval->slidevoltime = 0; - retval->slidevol = 0; - retval->legato = 0; - return retval; -} - -static int abc_numtracks(ABCHANDLE *h) -{ - int n; - ABCTRACK *t; - n=0; - for( t = h->track; t; t=t->next ) - n++; - return n; -} - -static int abc_interval(const char *s, const char *d) -{ - const char *p; - int i,j,k; - int n,oct,m[2]; - for( j=0; j<2; j++ ) { - if( j ) p = d; - else p = s; - switch(p[0]) { - case '^': - n = p[1]; - i = 2; - break; - case '_': - n = p[1]; - i = 2; - break; - case '=': - n = p[1]; - i = 2; - break; - default: - n = p[0]; - i = 1; - break; - } - for( k=0; k<25; k++ ) - if( n == sig[7][k] ) - break; - oct = 4; // ABC note pitch C is C4 and pitch c is C5 - if( k > 12 ) { - oct++; - k -= 12; - } - while( p[i] == ',' || p[i] == '\'' ) { - if( p[i] == ',' ) - oct--; - else - oct++; - i++; - } - m[j] = k + 12 * oct; - } - return m[0] - m[1]; -} - -static int abc_transpose(const char *v) -{ - int i,j,t; - const char *m = "B", *mv = ""; - t = 0; - global_octave_shift = 99; - for( ; *v && *v != ']'; v++ ) { - if( !strnicmp(v,"t=",2) ) { - v+=2; - if( *v=='-' ) { - j = -1; - v++; - } - else j = 1; - v+=abc_getnumber(v,&i); - t += i * j; - global_octave_shift = 0; - } - if( !strnicmp(v,"octave=",7) ) { - v+=7; - if( *v=='-' ) { - j = -1; - v++; - } - else j = 1; - v+=abc_getnumber(v,&i); - t += i * j * 12; - global_octave_shift = 0; - } - if( !strnicmp(v,"transpose=",10) ) { - v+=10; - if( *v=='-' ) { - j = -1; - v++; - } - else j = 1; - v+=abc_getnumber(v,&i); - t += i * j; - global_octave_shift = 0; - } - if( !strnicmp(v,"octave=",7) ) { // used in kv304*.abc - v+=7; - if( *v=='-' ) { - j = -1; - v++; - } - else j = 1; - v+=abc_getnumber(v,&i); - t += i * j * 12; - global_octave_shift = 0; - } - if( !strnicmp(v,"m=",2) ) { - v += 2; - mv = v; // get the pitch for the middle staff line - while( *v && *v != ' ' && *v != ']' ) v++; - global_octave_shift = 0; - } - if( !strnicmp(v,"middle=",7) ) { - v += 7; - mv = v; // get the pitch for the middle staff line - while( *v && *v != ' ' && *v != ']' ) v++; - global_octave_shift = 0; - } - if( !strnicmp(v,"clef=",5) ) - v += 5; - j = 1; - if( !strnicmp(v,"treble",6) ) { - j = 0; - v += 6; - switch( *v ) { - case '1': v++; m = "d"; break; - case '2': v++; - default: m = "B"; break; - case '3': v++; m = "G"; break; - case '4': v++; m = "E"; break; - case '5': v++; m = "C"; break; - } - global_octave_shift = 0; - } - if( j && !strnicmp(v,"bass",4) ) { - m = "D,"; - j = 0; - v += 4; - switch( *v ) { - case '1': v++; m = "C"; break; - case '2': v++; m = "A,"; break; - case '3': v++; m = "F,"; break; - case '4': v++; - default: m = "D,"; break; - case '5': v++; m = "B,,"; break; - } - if( global_octave_shift == 99 ) - global_octave_shift = -2; - } - if( j && !strnicmp(v,"tenor",5) ) { - j = 0; - v += 5; - switch( *v ) { - case '1': v++; m = "G"; break; - case '2': v++; m = "E"; break; - case '3': v++; m = "C"; break; - case '4': v++; - default: m = "A,"; break; - case '5': v++; m = "F,"; break; - } - if( global_octave_shift == 99 ) - global_octave_shift = 1; - } - if( j && !strnicmp(v,"alto",4) ) { - j = 0; - v += 4; - switch( *v ) { - case '1': v++; m = "G"; break; - case '2': v++; m = "E"; break; - case '3': v++; - default: m = "C"; break; - case '4': v++; m = "A,"; break; - case '5': v++; m = "F,"; break; - } - if( global_octave_shift == 99 ) - global_octave_shift = 1; - } - if( j && strchr("+-",*v) && *v && v[1]=='8' ) { - switch(*v) { - case '+': - t += 12; - break; - case '-': - t -= 12; - break; - } - v += 2; - if( !strnicmp(v,"va",2) ) v += 2; - global_octave_shift = 0; - j = 0; - } - if( j ) { - while( *v && *v != ' ' && *v != ']' ) v++; - } - } - if( strlen(mv) > 0 ) // someone set the middle note - t += abc_interval(mv, m); - if( global_octave_shift == 99 ) - global_octave_shift = 0; - return t; -} - -// ============================================================================= -static ABCTRACK *abc_locate_track(ABCHANDLE *h, const char *voice, int pos) -// ============================================================================= -{ - ABCTRACK *tr, *prev, *trunused; - char vc[21]; - int i, trans=0, voiceno=0, instrno = 1, channo = 0; - for( ; *voice == ' '; voice++ ) ; // skip leading spaces - for( i=0; *voice && *voice != ']' && *voice != '%' && !isspace(*voice); voice++ ) // can work with inline voice instructions - vc[i++] = *voice; - vc[i] = '\0'; - prev = NULL; - trunused = NULL; - if( !pos ) trans = abc_transpose(voice); - for( tr=h->track; tr; tr=tr->next ) { - if( tr->vno == 0 ) { - if( !trunused ) trunused = tr; // must reuse mastertrack (h->track) as first - } - else { - if( !strnicmp(tr->v, vc, 20) ) { - if( tr->vpos == pos ) - return tr; - trans = tr->transpose; - global_octave_shift = tr->octave_shift; - voiceno = tr->vno; - instrno = tr->instr; - channo = tr->chan; - } - } - prev = tr; - } - if( trunused ) { - tr = trunused; - if( pos ) { - tr->vno = voiceno; - tr->instr = instrno; - tr->chan = channo; - } - else { - global_voiceno++; - tr->vno = global_voiceno; - tr->instr = 1; - tr->chan = 0; - } - tr->vpos = pos; - tr->tiedvpos = pos; - strncpy(tr->v, vc, 20); - tr->v[20] = '\0'; - tr->mute = 0; - tr->transpose = trans; - tr->octave_shift = global_octave_shift; - tr->volume = h->track->volume; - tr->tienote = NULL; - tr->legato = 0; - return tr; - } - tr = abc_new_track(h, vc, pos); - if( pos ) { - tr->vno = voiceno; - tr->instr = instrno; - tr->chan = channo; - } - tr->transpose = trans; - tr->octave_shift = global_octave_shift; - if( prev ) prev->next = tr; - else h->track = tr; - return tr; -} - -// ============================================================================= -static ABCTRACK *abc_check_track(ABCHANDLE *h, ABCTRACK *tp) -// ============================================================================= -{ - if( !tp ) { - tp = abc_locate_track(h, "", 0); // must work for voiceless abc too... - tp->transpose = h->ktrans; - } - return tp; -} - -static void abc_add_capo(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime) -{ - ABCEVENT *e; - char d[6]; - d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; - d[cmdflag] = 1; - d[command] = cmdcapo; - e = abc_new_event(h, tracktime, d); - tp->capostart = e; - abc_add_event(h, tp, e); // do this last (recursion danger) -} - -static void abc_add_segno(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime) -{ - ABCEVENT *e; - char d[6]; - d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; - d[cmdflag] = 1; - d[command] = cmdsegno; - e = abc_new_event(h, tracktime, d); - abc_add_event(h, tp, e); -} - -static void abc_add_coda(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime) -{ - ABCEVENT *e; - char d[6]; - d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; - d[cmdflag] = 1; - d[command] = cmdcoda; - e = abc_new_event(h, tracktime, d); - abc_add_event(h, tp, e); -} - -static void abc_add_fine(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime) -{ - ABCEVENT *e; - char d[6]; - d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; - d[cmdflag] = 1; - d[command] = cmdfine; - e = abc_new_event(h, tracktime, d); - abc_add_event(h, tp, e); -} - -static void abc_add_tocoda(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime) -{ - ABCEVENT *e; - char d[6]; - d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; - d[cmdflag] = 1; - d[command] = cmdtocoda; - e = abc_new_event(h, tracktime, d); - abc_add_event(h, tp, e); -} - -// first track is dirigent, remove all control events from other tracks -// to keep the information where the events should be relative to note events -// in the same tick the ticks are octated and four added for note events -// the control events that come before the note events get a decremented tick, -// those that come after get an incremented tick, for example: -// ctrl ctrl note ctrl ctrl note -// original: t t t t t+1 t+1 -// recoded: 8t+1 8t+2 8t+4 8t+5 8t+11 8t+12 -static void abc_remove_unnecessary_events(ABCHANDLE *h) -{ - ABCTRACK *tp,*ptp; - ABCEVENT *ep, *el; - ULONG ct, et; - int d; - ptp = NULL; - for( tp=h->track; tp; tp=tp->next ) { - el = NULL; - ep = tp->head; - ct = 0; - d = -3; - while( ep ) { - et = ep->tracktick; - ep->tracktick <<= 3; - ep->tracktick += 4; - if( ep->flg == 1 ) { - ep->tracktick += d; - d++; - if( d == 0 ) d = -1; - if( d == 4 ) d = 3; - if( tp!=h->track ) ep->cmd = cmdhide; - switch( ep->cmd ) { - case cmdhide: - case cmdsync: - if( el ) { - el->next = ep->next; - _mm_free(h->trackhandle,ep); - ep = el->next; - } - else { - tp->head = ep->next; - _mm_free(h->trackhandle,ep); - ep = tp->head; - } - break; - default: - el = ep; - ep = ep->next; - break; - } - } - else { - el = ep; - ep = ep->next; - d = 1; - } - if( et > ct ) - d = -3; - ct = et; - } - if( !tp->head ) { // no need to keep empty tracks... - if( ptp ) { - ptp->next = tp->next; - _mm_free(h->trackhandle,tp); - tp = ptp; - } - else { - h->track = tp->next; - _mm_free(h->trackhandle,tp); - tp = h->track; - } - } - ptp = tp; // remember previous track - } -} - -// set ticks back, and handle partbreaks -static void abc_retick_events(ABCHANDLE *h) -{ - ABCTRACK *tp; - ABCEVENT *ep; - ULONG et, tt=0, at = abcticks(64 * h->speed); - for( tp=h->track; tp; tp=tp->next ) { - // make ticks relative - tt = 0; - for( ep=tp->head; ep; ep=ep->next ) { - et = ep->tracktick >> 3; - ep->tracktick = et - tt; - tt = et; - } - // make ticks absolute again, skipping no-op partbreaks - tt = 0; - for( ep=tp->head; ep; ep=ep->next ) { - ep->tracktick += tt; - tt = ep->tracktick; - if( ep->flg == 1 && ep->cmd == cmdpartbrk ) { - if( tt % at ) { - tt += at; - tt /= at; - tt *= at; - ep->tracktick -= abcticks(h->speed); // break plays current row - } - else ep->cmd = cmdhide; - } - } - } -} - -// make sure every track has the control events it needs, this way it is not -// necessary to have redundant +segno+ +D.C.+ etc in the voices, the first voice -// is the master, it is pointed to by the member 'track' in the ABCHANDLE -static void abc_synchronise_tracks(ABCHANDLE *h) -{ - ABCTRACK *tp; - ULONG tm; // tracktime in master - ABCEVENT *em, *es, *et, *ec; // events in master, slave, slave temporary and copied event - if( !h || !h->track ) return; - abc_remove_unnecessary_events(h); - for( tp = h->track->next; tp; tp = tp->next ) { - for( em=h->track->head; em; em=em->next ) { - if( em->flg == 1 ) { // some kind of control event - switch( em->cmd ) { - case cmdchord: - case cmdhide: - case cmdtempo: - case cmdsync: - break; - default: // check to see if copy is necessary - ec = abc_copy_event(h, em); - tm = em->tracktick; - es = tp->head; // allways search from the begin... - for( et=es; et && et->tracktick <= tm; et=et->next ) - es = et; - if( es == NULL || es->tracktick > tm ) { // special case: head of track - ec->next = es; - tp->head = ec; - } - else { - ec->next = es->next; - es->next = ec; - } - break; - } - } - } - } - abc_retick_events(h); -} - -static void abc_add_event(ABCHANDLE *h, ABCTRACK *tp, ABCEVENT *e) -{ - if( !tp->capostart ) abc_add_capo(h, tp, global_songstart); - if( tp->tail ) { - tp->tail->next = e; - tp->tail = e; - } - else { - tp->head = e; - tp->tail = e; - } -} - -static void abc_add_partbreak(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime) -{ - ABCEVENT *e; - char d[6]; - d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; - d[cmdflag] = 1; - d[command] = cmdpartbrk; - e = abc_new_event(h, tracktime, d); - abc_add_event(h, tp, e); -} - -static void abc_add_tempo_event(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int tempo) -{ - ABCEVENT *e; - char d[6]; - d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; - d[cmdflag] = 1; - d[command] = cmdtempo; - e = abc_new_event(h, tracktime, d); - e->lpar = tempo; - abc_add_event(h, tp, e); -} - -static void abc_add_noteoff(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime) -{ - ABCEVENT *e; - char d[6]; - d[note] = 0; - d[octave] = 0; - d[smpno] = pat_gmtosmp(tp->instr); - d[volume] = 0; - d[effect] = 0; - d[effoper] = 0; - e = abc_new_event(h, tracktime, d); - abc_add_event(h, tp, e); -} - -static int abc_dynamic_volume(ABCTRACK *tp, ULONG tracktime, int vol) -{ - ULONG slidetime; - int voldelta; - if( tp->mute ) return 0; - if( tp->slidevol == 0 ) return vol; - if( tracktime < tp->slidevoltime ) return vol; - slidetime = modticks(tracktime - tp->slidevoltime); - voldelta = (slidetime * 15) / 64 / 6; // slide from say mf up to f in one pattern's time - if( tp->slidevol > -2 && voldelta > 15 ) voldelta = 15; // never to much dynamics - if( tp->slidevol > 0 ) vol += voldelta; - else vol -= voldelta; - if( vol < 2 ) vol = 2; // xmms divides this by 2.... - if( vol > 127 ) vol = 127; - return vol; -} - -static void abc_track_untie_short_chordnotes(ABCHANDLE *h) -{ - ABCTRACK *tp; - int vn; - tp = h->tp; - vn = tp->vno; - for( tp = h->track; tp; tp = tp->next ) - if( tp != h->tp && tp->vno == vn && tp->tienote ) { - abc_message("short notes in chord can not be tied:\n%s", h->line); - tp->tienote = 0; - } -} - -static void abc_track_clear_tiednote(ABCHANDLE *h) -{ - ABCTRACK *tp; - int vn; - tp = h->tp; - vn = tp->vno; - for( tp = h->track; tp; tp = tp->next ) - if( tp->vno == vn ) tp->tienote = 0; -} - -static void abc_track_clear_tiedvpos(ABCHANDLE *h) -{ - ABCTRACK *tp; - int vn; - tp = h->tp; - vn = tp->vno; - for( tp = h->track; tp; tp = tp->next ) - if( tp->vno == vn ) tp->tiedvpos = tp->vpos; -} - -static ABCTRACK *abc_track_with_note_tied(ABCHANDLE *h, ULONG tracktime, int n, int oct) -{ - int vn, vp; - ABCTRACK *tp; - ABCEVENT *e; - tp = h->tp; - vn = tp->vno; - vp = tp->vpos; - for( tp = h->track; tp; tp = tp->next ) { - if( tp->vno == vn ) { - e = tp->tienote; - if( e && e->tracktick < tracktime - && e->par[octave] == oct && abs(e->par[note] - n) < 3 ) { - if( tp->vpos != vp ) tp->tiedvpos = vp; - h->tp = tp; - return tp; - } - } - } - tp = h->tp; - vp = tp->tiedvpos; - if( tp->vpos != vp ) { - // chord note track allready returned in previous call - for( tp = h->track; tp; tp = tp->next ) { - if( tp->vno == vn && tp->vpos == vp ) { - tp->tiedvpos = h->tp->vpos; - h->tp = tp; - return tp; - } - } - } - return h->tp; -} - -static int abc_add_noteon(ABCHANDLE *h, int ch, const char *p, ULONG tracktime, char *barkey, int vol, ABCEVENT_X_EFFECT fx, int fxop) -{ - ABCEVENT *e; - ABCTRACK *tp; - int i,j,k; - int n,oct; - char d[6]; - tp = h->tp; - switch(ch) { - case '^': - if( p[0] == '^' ) { - n = p[1]; - i = 2; - ch = 'x'; - } - else { - n = p[0]; - i = 1; - } - break; - case '_': - if( p[0] == '_' ) { - n = p[1]; - i = 2; - ch = 'b'; - } - else { - n = p[0]; - i = 1; - } - break; - case '=': - n = p[0]; - i = 1; - break; - default: - n = ch; - i = 0; - break; - } - for( k=0; k<51; k++ ) { - if( n == barkey[k] ) - break; - } - j = k; - if( k > 24 ) - k -= 25; // had something like A# over Bb key F signature.... - if( i ) { - // propagate accidentals if necessary - // DON'T do redundant accidentals they're always relative to C-scale - for( k=0; k<25; k++ ) { - if( n == sig[7][k] ) - break; - } - if( k < 25 ) { // only do real notes... - switch(ch) { - case 'x': - k++; - case '^': - k++; - break; - case 'b': - k--; - case '_': - k--; - break; - case '=': - break; - } - if( j < 25 ) // was it not A# over Bb? - barkey[j] = ' '; - barkey[k] = n; - } - } - oct = 3; // ABC note pitch C is C4 and pitch c is C5 - if( k < 25 ) { - k += tp->transpose; - while( k > 12 ) { - oct++; - k -= 12; - } - while( k < 0 ) { - oct--; - k += 12; - } - d[note] = 23 + k; // C0 is midi notenumber 24 - } - else - d[note] = 0; // someone has doen ^X3 or something like it... - while( p[i] && strchr(",'",p[i]) ) { - if( p[i]==',' ) oct--; - else oct++; - i++; - tp->octave_shift = 0; // forget we ever had to look at it - } - if( tp->octave_shift ) - tp->transpose += 12 * tp->octave_shift; - oct += tp->octave_shift; - tp->octave_shift = 0; // after the first note we never have to look at it again - if( oct < 0 ) oct = 0; - if( oct > 9 ) oct = 9; - d[octave] = oct; - d[smpno] = pat_gmtosmp(tp->instr); - d[volume] = abc_dynamic_volume(tp, tracktime, vol); - d[effect] = fx; // effect - d[effoper] = fxop; - tp = abc_track_with_note_tied(h, tracktime, d[note], oct); - if( tp->tienote ) { - if( tp->tienote->par[note] != d[note] ) { - if( abs(tp->tienote->par[note] - d[note]) < 3 ) { - // may be tied over bar symbol, recover local accidental to barkey - k = tp->tienote->par[note] - 23 - tp->transpose; - while( k < 0 ) k += 12; - while( k > 12 ) k -= 12; - if( (isupper(n) && barkey[k+12] == ' ') || (islower(n) && barkey[k] == ' ') ) { - barkey[j] = ' '; - if( isupper(n) ) - barkey[k] = n; - else - barkey[k+12] = n; - d[note] = tp->tienote->par[note]; - d[octave] = tp->tienote->par[octave]; - } - } - } - } - if( tp->tienote - && tp->tienote->par[note] == d[note] - && tp->tienote->par[octave] == d[octave] ) { - for( e = tp->tienote; e; e = e->next ) { - if( e->par[note] == 0 && e->par[octave] == 0 ) { // undo noteoff - e->flg = 1; - e->cmd = cmdhide; - e->lpar = 0; - break; - } - } - tp->tienote->tiednote = 1; // mark him for the pattern writers - for( j=i; isdigit(p[j]) || p[j]=='/'; j++ ) ; // look ahead to see if this one is tied too - if( p[j] != '-' ) // is this note tied too? - tp->tienote = NULL; // if not the tie ends here... - return i; - } - tp->tienote = NULL; - if( tp->tail - && tp->tail->tracktick == tracktime - && tp->tail->par[note] == 0 - && tp->tail->par[octave] == 0 ) { - for( j=0; j<6; j++ ) - tp->tail->par[j] = d[j]; - } - else { - e = abc_new_event(h, tracktime, d); - abc_add_event(h, tp, e); - } - if( i > 0 && p[i-1] == '"' ) { - i--; // someone coded a weird note like ^"E" - abc_message("strange note encountered scanning %s", h->line); - } - return i; -} - -static void abc_add_dronenote(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int nnum, int vol) -{ - ABCEVENT *e; - int j,k; - int oct; - char d[6]; - oct = -1; // ABC note pitch C is C4 and pitch c is C5 - k = nnum + 1; - while( k > 12 ) { - oct++; - k -= 12; - } - while( k < 0 ) { - oct--; - k += 12; - } - if( oct < 0 ) oct = 0; - d[note] = 23 + k; // C0 is midi notenumber 24 - d[octave] = oct; - d[smpno] = pat_gmtosmp(tp->instr); - d[volume] = abc_dynamic_volume(tp, tracktime, vol); - d[effect] = 0; // effect - d[effoper] = 0; - if( tp->tail - && tp->tail->tracktick == tracktime - && tp->tail->par[note] == 0 - && tp->tail->par[octave] == 0 ) { - for( j=0; j<6; j++ ) - tp->tail->par[j] = d[j]; - } - else { - e = abc_new_event(h, tracktime, d); - abc_add_event(h, tp, e); - } -} - -static void abc_add_chordnote(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int nnum, int vol) -{ - abc_add_dronenote(h, tp, tracktime, nnum + 23, tp->mute? 0: vol); -} - -static void abc_add_drumnote(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int nnum, int vol) -{ - abc_add_dronenote(h, tp, tracktime, nnum, tp->mute? 0: vol); -} - -static void abc_add_variant_start(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, int n) -{ - ABCEVENT *e; - char d[6]; - d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; - d[cmdflag] = 1; - d[command] = cmdvariant; - e = abc_new_event(h, tracktime, d); - e->lpar = 1<tail->lpar |= 1<flg != 1 && e->par[note] != 0 ) - e->par[volume] = abc_dynamic_volume(tp, e->tracktick, e->par[volume]); - e = e->next; - } -} - -static void abc_add_setjumploop(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime, ABCEVENT_JUMPTYPE j) -{ - ABCEVENT *e; - char d[8]; - d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; - d[cmdflag] = 1; - d[command] = cmdjump; - d[jumptype] = j; - e = abc_new_event(h, tracktime, d); - abc_add_event(h, tp, e); -} - -static void abc_add_sync(ABCHANDLE *h, ABCTRACK *tp, ULONG tracktime) -{ - ABCEVENT *e; - char d[6]; - e = tp->tail; - if( e && e->tracktick == tracktime ) return; - if( e && e->flg == 1 && e->cmd == cmdsync ) { - e->tracktick = tracktime; - return; - } - d[0] = d[1] = d[2] = d[3] = d[4] = d[5] = 0; - d[cmdflag] = 1; - d[command] = cmdsync; - e = abc_new_event(h, tracktime, d); - abc_add_event(h, tp, e); -} - -static void abc_add_gchord_syncs(ABCHANDLE *h, ABCTRACK *tpc, ULONG tracktime) -{ - ABCTRACK *tp; - int i; - for( i = GCHORDBPOS; i < DRUMPOS; i++ ) { - tp = abc_locate_track(h, tpc->v, i); - abc_add_sync(h,tp,tracktime); - } -} - -static void abc_add_drum_sync(ABCHANDLE *h, ABCTRACK *tpr, ULONG tracktime) -{ - ABCTRACK *tp; - tp = abc_locate_track(h, tpr->v, DRUMPOS); - abc_add_sync(h,tp,tracktime); -} - -static int abc_getnumber(const char *p, int *number) -{ - int i,h; - i = 0; - h = 0; - while( isdigit(p[i]) ) { - h = 10 * h + p[i] - '0'; - i++; - } - if( i==0 ) - *number = 1; - else - *number = h; - return i; -} - -static int abc_getexpr(const char *p, int *number) -{ - int i, term, total; - i = 0; - while( isspace(p[i]) ) - i++; - if( p[i] == '(' ) { - i += abc_getexpr(p+i+1, number); - while( p[i] && (p[i] != ')') ) - i++; - return i; - } - i += abc_getnumber(p+i, &total); - while( isspace(p[i]) ) - i++; - while( p[i] == '+' ) { - i += abc_getexpr(p+i+1, &term); - total += term; - while( isspace(p[i]) ) - i++; - } - *number = total; - return i; -} - -static int abc_notelen(const char *p, int *len, int *div) -{ - int i,h,k; - i = abc_getnumber(p,len); - h = 1; - while( p[i] == '/' ) { - h *= 2; - i++; - } - if( isdigit(p[i]) ) { - h /= 2; - i += abc_getnumber(p+i,&k); - } - else k = 1; - *div = h * k; - return i; -} - -static int abc_brokenrithm(const char *p, int *nl, int *nd, int *b, int hornpipe) -{ - switch( *b ) { - case '<': - *nl *= 3; - *nd *= 2; - hornpipe = 0; - break; - case '>': - *nd *= 2; - hornpipe = 0; - break; - } - *b = *p; - switch( *b ) { - case '>': - *nl *= 3; - *nd *= 2; - return 1; - case '<': - *nd *= 2; - return 1; - default: - *b = 0; - break; - } - if( hornpipe ) { // still true then make 1/8 notes broken rithme - if( *nl == 1 && *nd == 1 ) { - *b = '>'; - *nl = 3; - *nd = 2; - } - } - return 0; -} - -// put p notes in the time q for the next r notes -static int abc_tuplet(int *nl, int *nd, int p, int q, int r) -{ - if( !r ) return 0; - *nl *= q; - *nd *= p; - return r - 1; -} - -// evaluate [Q:"string" n1/m1 n2/m2 n3/m3 n4/m4=bpm "string"] -// minimal form [Q:"string"] -// most used form [Q: 1/4=120] -static int abc_extract_tempo(const char *p, int invoice) -{ - int nl, nd, ns, in, tempo; - int nl1=0, nd1, notes, state; - const char *q; - in = 0; - nl = 0; - nd = 1; - ns = 120; - notes = 0; - state = 0; - for( q=p; *q; q++ ) { - if( in ) { - if( *q=='"' ) - in = 0; - } - else { - if( *q == ']' ) break; - switch( *q ) { - case '"': - in = 1; - break; - case '/': - notes++; - state = 1; - nl1 = ns; - break; - case '=': - break; - default: - if( isdigit(*q) ) { - if( state ) { - q+=abc_getnumber(q,&nd1)-1; - state = 0; - nl = nl * nd1 + nl1 * nd; - nd = nd * nd1; - } - else - q+=abc_getnumber(q,&ns)-1; - } - break; - } - } - } - if( !notes ) { - nl = 1; - nd = 4; - } - if( !nd ) tempo = 120; - else tempo = ns * nl * 4 / nd; // mod tempo is really BPM where one B is equal to a quartnote - if( invoice ) { - nl = global_tempo_factor; - nd = global_tempo_divider; - } - global_tempo_factor = 1; - global_tempo_divider = 1; - while( tempo/global_tempo_divider > 255 ) - global_tempo_divider++; - tempo /= global_tempo_divider; - while( tempo * global_tempo_factor < 256 ) - global_tempo_factor++; - global_tempo_factor--; - tempo *= global_tempo_factor; - if( tempo * 3 < 512 ) { - global_tempo_factor *= 3; - global_tempo_divider *= 2; - tempo = (tempo * 3) / 2; - } - if( invoice ) { - if( nl != global_tempo_factor || nd != global_tempo_divider ) { - ns = (tempo * nl * global_tempo_divider) / (nd * global_tempo_factor); - if( ns > 31 && ns < 256 ) { - tempo = ns; - global_tempo_factor = nl; - global_tempo_divider = nd; - } - else - abc_message("Failure: inconvenient tempo change in middle of voice (%s)", p); - } - } - return tempo; -} - -static void abc_set_parts(char **d, char *p) -{ - int i,j,k,m,n; - char *q; -#ifdef NEWMIKMOD - static MM_ALLOC *h; - if( *d ) _mmalloc_close(h); -#else - if( *d ) free(*d); -#endif - *d = 0; - if( !p ) return; - for( i=0; p[i] && p[i] != '%'; i++ ) { - if( !strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ().0123456789 ",p[i]) ) { - abc_message("invalid characters in part string scanning P:%s", p); - return; - } - } -#ifdef NEWMIKMOD - h = _mmalloc_create("Load_ABC_parts", NULL); -#endif - // decode constructs like "((AB)2.(CD)2)3.(AB)E2" to "ABABCDCDABABCDCDABABCDCDABEE" - // first compute needed storage... - j=0; - k=0; - for( i=0; p[i] && p[i] != '%'; i++ ) { - if( isupper(p[i]) ) { - j++; - } - if( isdigit(p[i]) ) { - n=abc_getnumber(p+i,&k); - if( p[i-1] == ')' ) - j *= k; // never mind multiple parens, just take the worst case - else - j += k-1; - i += n-1; - } - } - q = (char *)_mm_calloc(h, j+1, sizeof(char)); // enough storage for the worst case - // now copy bytes from p to *d, taking parens and digits in account - j = 0; - for( i=0; p[i] && p[i] != '%'; i++ ) { - if( isdigit(p[i]) || isupper(p[i]) || p[i] == '(' || p[i] == ')' ) { - if( p[i] == ')' ) { - for( n=j; n > 0 && q[n-1] != '('; n-- ) ; // find open paren in q - // q[n+1] to q[j] contains the substring that must be repeated - if( n > 0 ) { - for( k = n; k 1 ) { - for( m=0; m 1 ) { - q[j] = q[j-1]; - j++; - } - continue; - } - q[j] = p[i]; - j++; - } - } - q[j] = '\0'; - // remove any left over parens - for( i=0; itail->tracktick - pt1; - for( e=tp->head; e && e->tracktick <= pt2; e=e->next ) { - if( e->tracktick >= pt1 ) { - if( e->flg != 1 || e->cmd == cmdsync || e->cmd == cmdchord ) { - if( e != tp->tail ) { - // copy this event at tail - ec = abc_copy_event(h,e); - ec->tracktick += dt; - ec->part = '*'; - tp->tail->next = ec; - tp->tail = ec; - } - } - } - } - abc_add_sync(h, tp, pt2 + dt); // make sure there is progression... -} - -static ULONG abc_pattracktime(ABCHANDLE *h, ULONG tracktime) -{ - ABCEVENT *e; - ULONG dt,et,pt=abcticks(64 * h->speed); - if(!h || !h->track || !h->track->head ) return 0; - dt = 0; - for( e=h->track->head; e && e->tracktick <= tracktime; e=e->next ) { - if( e->flg == 1 && e->cmd == cmdpartbrk ) { - et = e->tracktick + dt; - if( et % pt ) { - et += pt; - et /= pt; - et *= pt; - dt = et - e->tracktick; - } - } - } - return (tracktime + dt); -} - -static int abc_patno(ABCHANDLE *h, ULONG tracktime) -{ - return modticks(abc_pattracktime(h, tracktime)) / 64 / h->speed; -} - -static void abc_stripoff(ABCHANDLE *h, ABCTRACK *tp, ULONG tt) -{ - ABCEVENT *e1, *e2; - e2 = NULL; - for( e1 = tp->head; e1 && e1->tracktick <= tt; e1=e1->next ) - e2 = e1; - if( e2 ) { - e1 = e2->next; - tp->tail = e2; - e2->next = NULL; - } - else { - e1 = tp->tail; - tp->head = NULL; - tp->tail = NULL; - } - while( e1 ) { - e2 = e1->next; - _mm_free(h->trackhandle,e1); - e1 = e2; - } -} - -static void abc_keeptiednotes(ABCHANDLE *h, ULONG fromtime, ULONG totime) { - ABCTRACK *tp; - ABCEVENT *e,*n,*f; - if( totime <= fromtime ) return; - for( tp=h->track; tp; tp=tp->next ) { - if( tp->vno ) { // if track is in use... - n = NULL; - for( e=tp->head; e && e->tracktick < fromtime; e = e->next ) - if( e->flg != 1 ) n = e; // remember it when it is a note event - if( n && n->tiednote ) { // we've a candidate to tie over the break - while( e && e->tracktick < totime ) e=e->next; // skip to other part - if( e && e->tracktick == totime ) { // if this is on begin row of this part - f = NULL; - while( !f && e && e->tracktick == totime ) { - if( e->flg != 1 ) f = e; - e = e->next; - } - if( f && f->par[note] ) { // pfoeie, we've found a candidate - if( abs(n->par[note] - f->par[note]) < 3 ) { // undo the note on - f->flg = 1; - f->cmd = cmdhide; - f->lpar = 0; - } - } - } - } - } - } -} - -static ULONG abc_fade_tracks(ABCHANDLE *h, char *abcparts, ULONG ptt[27]) -{ - ABCTRACK *tp; - ABCEVENT *e0; - char *p; - int vol; - ULONG pt1,pt2; - ULONG tt; - tt = h->track->tail->tracktick; - for( tp=h->track->next; tp; tp=tp->next ) { - if( !tp->tail ) abc_add_sync(h, tp, tt); // no empty tracks please... - if( tp->tail->tracktick > tt ) abc_stripoff(h, tp, tt); // should not happen.... - if( tp->tail->tracktick < tt ) abc_add_sync(h, tp, tt); - } - for( tp=h->track; tp; tp=tp->next ) { - vol = 127; - e0 = tp->tail; - if( tp->slidevol != -2 ) { - tp->slidevol = -2; - tp->slidevoltime = e0->tracktick; - } - tp->mute = 0; // unmute track for safety, notes in a muted track already have zero volume... - while( vol > 5 ) { - for( p=abcparts; *p && vol > 5; p++ ) { - pt1 = ptt[*p-'A']; - pt2 = ptt[*p-'A'+1]; - abc_appendpart(h, tp, pt1, pt2); - vol = abc_dynamic_volume(tp, tp->tail->tracktick, 127); - } - } - abc_fade_track(tp,e0); - } - return h->track->tail->tracktick; -} - -static void abc_song_to_parts(ABCHANDLE *h, char **abcparts, BYTE partp[27][2]) -{ - ULONG starttick; - ABCEVENT *e; - int i, fading, loop, normal, partno, partsegno, partloop, partcoda, parttocoda, partfine, skip, x, y; - int vmask[27],nextp[27]; - ULONG ptt[27]; - char buf[256]; // must be enough, mod's cannot handle more than 240 patterns - char *pfade; - if( !h || !h->track || !h->track->capostart ) return; - strcpy(buf,"A"); // initialize our temporary array - i = 1; - loop = 1; - partno = 0; - partsegno = 0; - partloop = 0; - partcoda = -1; - parttocoda = -1; - partfine = -1; - starttick = h->track->capostart->tracktick; - ptt[0] = starttick; - vmask[0] = -1; - nextp[0] = 1; - for( e=h->track->capostart; e; e=e->next ) { - if( e->flg == 1 ) { - switch( e->cmd ) { - case cmdpartbrk: - if( e->tracktick > starttick) { - starttick = e->tracktick; // do not make empty parts - if( partno < 26 ) { - partno++; - ptt[partno] = starttick; - } - if( i < 255 ) buf[i++] = partno+'A'; - vmask[partno] = -1; - nextp[partno] = partno+1; - } - break; - case cmdloop: - partloop = partno; - loop = 1; // start counting anew... - break; - case cmdvariant: - vmask[partno] = e->lpar; - break; - case cmdjump: - x = 0; - fading = 0; - normal = 0; - skip = 0; - pfade = &buf[i]; - switch( e->par[jumptype] ) { - case jumpfade: - fading = 1; - case jumpnormal: - normal = 1; - x = partloop; - loop++; - break; - case jumpdsfade: - fading = 1; - case jumpdasegno: - x = partsegno; - break; - case jumpdcfade: - fading = 1; - case jumpdacapo: - x = 0; - break; - default: - x = 0; - break; - } - if( vmask[partno] != -1 ) nextp[partno] = x; - if( partno < 26 ) ptt[partno+1] = e->tracktick; // for handling ties over breaks - while( x <= partno ) { - if( skip == 1 && x == partcoda ) skip = 0; - y = !skip; - if( y ) { - if( !normal ) { - if( x == partfine ) skip = 2; - if( x == parttocoda ) skip = 1; - y = !skip; - } - if( !(vmask[x] & (1<tracktick; - buf[i] = '\0'; // close up pfade with zero byte - starttick = abc_fade_tracks(h, pfade, ptt); - buf[i++] = partno+'A'; - partno++; - ptt[partno] = starttick; - buf[i++] = partno+'A'; // one extra to throw away... - e = h->track->tail; // this is the edge of the world captain... - } - break; - case cmdtocoda: - parttocoda = partno; - break; - case cmdcoda: - partcoda = partno; - break; - case cmdfine: - partfine = partno; - break; - case cmdsegno: - partsegno = partno; - break; - } - } - e->part = partno+'a'; // small caps for generated parts... - } - i--; // strip off last partno - if( partno > 0 ) partno--; - buf[i] = '\0'; - if( i > 1 ) { - for( i=1; buf[i]; i++ ) { - if( buf[i] != buf[i-1] + 1 ) { - x = buf[i-1] - 'A'; - y = buf[i] - 'A'; - abc_keeptiednotes(h, ptt[x+1], ptt[y]); - } - } - } - starttick = h->track->tail->tracktick; - ptt[partno+1] = starttick; - for( i=0; i<=partno; i++ ) { - partp[i][0] = abc_patno(h, ptt[i]); - partp[i][1] = abc_patno(h, ptt[i+1]); - } - // calculate end point of last part - starttick = abc_pattracktime(h, starttick); - if( starttick % abcticks(64 * h->speed) ) - partp[partno][1]++; - abc_set_parts(abcparts, buf); -} - -// ===================================================================================== -static char *abc_fgets(MMFILE *mmfile, char buf[], unsigned int bufsz) -// ===================================================================================== -{ - if( mmfeof(mmfile) ) return NULL; - mmfgets(buf,bufsz,mmfile); - return buf; -} - -// ===================================================================================== -static char *abc_fgetbytes(MMFILE *mmfile, char buf[], unsigned int bufsz) -// ===================================================================================== -{ - unsigned int i; - long pos; - if( mmfeof(mmfile) ) return NULL; - for( i=0; iline, target)) ) { - if( (i=strlen(h->line)) + n - l >= (int)h->len ) { - h->line = (char *)_mm_recalloc(h->allochandle, h->line, h->len<<1, sizeof(char)); - h->len <<= 1; - p=strstr(h->line, target); - } - if( n > l ) { - for( q=&h->line[i]; q>p; q-- ) q[n-l] = q[0]; - for( q=s; *q; q++ ) *p++ = *q; - } - else { - strcpy(p,s); - strcat(p,p+l); - } - } -} - -static void abc_preprocess(ABCHANDLE *h, ABCMACRO *m) -{ - int i, j, k, l, a, b; - char t[32]; - char s[200],*p; - if( m->n ) { - k = m->n - m->name; - for( i=0; i<14; i++ ) { - strncpy(t, m->name, 32); - t[k] = "CDEFGABcdefgab"[i]; - l = strlen(m->subst); - p = s; - for( j=0; jsubst[j]; - if( a > 'g' && islower(a) ) { - b = a - 'n'; - a = "CDEFGABCDEFGABcdefgabcdefgab"[i+b+7]; - *p++ = a; - if( i+b < 0 ) - *p++ = ','; - if( i+b > 13 ) - *p++ = '\''; - } - else *p++ = a; - } - *p = '\0'; - abc_substitute(h, t, s); - } - } - else - abc_substitute(h, m->name, m->subst); -} - -static char *abc_gets(ABCHANDLE *h, MMFILE *mmfile) -{ - int i; - ABCMACRO *mp; - if( !h->len ) { - h->len = 64; // initial line size, adequate for most abc's - h->line = (char *)_mm_calloc(h->allochandle, h->len, sizeof(char)); - } - if( abc_fgetbytes(mmfile, h->line, h->len) ) { - while( (i=strlen(h->line)) > (int)(h->len - 3) ) { - // line too short, double it - h->line = (char *)_mm_recalloc(h->allochandle, h->line, h->len<<1, sizeof(char)); - if( h->line[i-1] != '\n' ) - abc_fgetbytes(mmfile, &h->line[i], h->len); - h->len <<= 1; - } - h->line[i-1] = '\0'; // strip off newline - for( mp=h->macro; mp; mp=mp->next ) - abc_preprocess(h,mp); - return h->line; - } - return NULL; -} - -static int abc_parse_decorations(ABCHANDLE *h, ABCTRACK *tp, const char *p) -{ - int vol=0; - if( !strncmp(p,"mp",2) ) vol = 75; - if( !strncmp(p,"mf",2) ) vol = 90; - if( !strncmp(p,"sfz",3) ) vol = 100; - if( *p == 'p' ) { - vol = 60; - while( *p++ == 'p' ) vol -= 15; - if( vol < 1 ) vol = 1; - } - if( *p == 'f' ) { - vol = 105; - while( *p++ == 'f' ) vol += 15; - if( vol > 135 ) vol = 127; // ffff - if( vol > 127 ) vol = 125; // fff - } - if( vol ) { - tp->volume = vol; - if( tp == h->track ) { // copy volume over to all voice tracks - for( ; tp; tp=tp->next ) { - if( tp->vpos == 0 || tp->vpos > DRONEPOS2 ) tp->volume = vol; - } - tp = h->track; - } - } - return tp->volume; -} - -// ===================================================================================== -#ifdef NEWMIKMOD -BOOL ABC_Test(MMSTREAM *mmfile) -#else -BOOL CSoundFile::TestABC(const BYTE *lpStream, DWORD dwMemLength) -#endif -// ===================================================================================== -{ - char id[128]; - // scan file for first K: line (last in header) -#ifdef NEWMIKMOD - _mm_fseek(mmfile,0,SEEK_SET); - while(abc_fgets(mmfile,id,128)) { -#else - MMFILE mmfile; - mmfile.mm = (char *)lpStream; - mmfile.sz = dwMemLength; - mmfseek(&mmfile,0,SEEK_SET); - while(abc_fgets(&mmfile,id,128)) { -#endif - if(id[0]=='K' - && id[1]==':' - && (isalpha(id[2]) || isspace(id[2])) ) return 1; - } - return 0; -} - -// ===================================================================================== -static ABCHANDLE *ABC_Init(void) -{ - ABCHANDLE *retval; - char *p; - char buf[10]; -#ifdef NEWMIKMOD - MM_ALLOC *allochandle; - - allochandle = _mmalloc_create("Load_ABC", NULL); - retval = (ABCHANDLE *)_mm_calloc(allochandle, 1,sizeof(ABCHANDLE)); - if( !retval ) return NULL; - retval->allochandle = allochandle; - allochandle = _mmalloc_create("Load_ABC_macros", NULL); - retval->macrohandle = allochandle; - allochandle = _mmalloc_create("Load_ABC_tracks", NULL); - retval->trackhandle = allochandle; -#else - retval = (ABCHANDLE *)calloc(1,sizeof(ABCHANDLE)); - if( !retval ) return NULL; -#endif - retval->track = NULL; - retval->macro = NULL; - retval->umacro = NULL; - retval->beatstring = NULL; - retval->pickrandom = 0; - retval->len = 0; - retval->line = NULL; - strcpy(retval->gchord, ""); - retval->barticks = 0; - p = getenv(ABC_ENV_NORANDOMPICK); - if( p ) { - if( isdigit(*p) ) - retval->pickrandom = atoi(p); - if( *p == '-' ) { -#ifdef NEWMIKMOD - retval->pickrandom = atoi(p+1); - sprintf(buf,"-%ld",retval->pickrandom+1); -#else - retval->pickrandom = atoi(p+1)-1; // xmms preloads the file - sprintf(buf,"-%ld",retval->pickrandom+2); -#endif - setenv(ABC_ENV_NORANDOMPICK, buf, 1); - } - } - else { - srandom(time(0)); // initialize random generator with seed - retval->pickrandom = 1+(int)(10000.0*random()/(RAND_MAX+1.0)); - // can handle pickin' from songbooks with 10.000 songs -#ifdef NEWMIKMOD - sprintf(buf,"-%ld",retval->pickrandom+1); // next in sequence -#else - sprintf(buf,"-%ld",retval->pickrandom); // xmms preloads the file -#endif - setenv(ABC_ENV_NORANDOMPICK, buf, 1); - } - return retval; -} - -#ifndef NEWMIKMOD -static void ABC_CleanupTrack(ABCTRACK *tp) -{ - ABCEVENT *ep, *en; - if( tp ) { - for( ep=tp->head; ep; ep = en ) { - en=ep->next; - free(ep); - } - tp->head = NULL; - } -} - -static void ABC_CleanupMacro(ABCMACRO *m) -{ - if( m->name ) - free(m->name); - if( m->subst ) - free(m->subst); - free(m); -} -#endif - -// ===================================================================================== -static void ABC_CleanupTracks(ABCHANDLE *handle) -// ===================================================================================== -{ -#ifdef NEWMIKMOD - if(handle && handle->trackhandle) { - _mmalloc_close(handle->trackhandle); - handle->trackhandle = 0; - } -#else - ABCTRACK *tp, *tn; - if(handle) { - for( tp=handle->track; tp; tp = tn ) { - tn=tp->next; - ABC_CleanupTrack(tp); - } - handle->track = NULL; - } -#endif -} - -// ===================================================================================== -static void ABC_CleanupMacros(ABCHANDLE *handle) -// ===================================================================================== -{ -#ifdef NEWMIKMOD - if(handle && handle->macrohandle) { - _mmalloc_close(handle->macrohandle); - handle->macrohandle = 0; - } -#else - ABCMACRO *mp, *mn; - if(handle) { - for( mp=handle->macro; mp; mp = mn ) { - mn=mp->next; - ABC_CleanupMacro(mp); - } - for( mp=handle->umacro; mp; mp = mn ) { - mn=mp->next; - ABC_CleanupMacro(mp); - } - handle->macro = NULL; - handle->umacro = NULL; - } -#endif -} - -// ===================================================================================== -static void ABC_Cleanup(ABCHANDLE *handle) -// ===================================================================================== -{ -#ifdef NEWMIKMOD - if(handle && handle->allochandle) { -#else - if(handle) { -#endif - ABC_CleanupMacros(handle); - ABC_CleanupTracks(handle); -#ifdef NEWMIKMOD - _mmalloc_close(handle->allochandle); - handle->allochandle = 0; - handle->len = 0; -#else - if( handle->line ) - free(handle->line); - if( handle->beatstring ) - free(handle->beatstring); - free(handle); -#endif - } -} - -static int abc_is_global_event(ABCEVENT *e) -{ - return e->flg == 1 && (e->cmd == cmdtempo || e->cmd == cmdpartbrk); -} - -static ABCEVENT *abc_next_global(ABCEVENT *e) -{ - for( ; e && !abc_is_global_event(e); e=e->next ) ; - return e; -} - -static ABCEVENT *abc_next_note(ABCEVENT *e) -{ - for( ; e && e->flg == 1; e=e->next ) ; - return e; -} - -// ============================================================================= -#ifdef NEWMIKMOD -static void ABC_ReadPatterns(UNIMOD *of, ABCHANDLE *h, int numpat) -// ============================================================================= -{ - int pat,row,i,ch,trillbits; - BYTE n,ins,vol; - ABCTRACK *t; - ABCEVENT *e, *en, *ef, *el; - ULONG tt1, tt2; - UNITRK_EFFECT eff; - - // initialize start points of event list in tracks - for( t = h->track; t; t = t->next ) t->capostart = t->head; - trillbits = 0; // trill effect admininstration: one bit per channel, max 32 channnels - for( pat = 0; pat < numpat; pat++ ) { - utrk_reset(of->ut); - for( row = 0; row < 64; row++ ) { - tt1 = abcticks((pat * 64 + row ) * h->speed); - tt2 = tt1 + abcticks(h->speed); - ch = 0; - for( e=abc_next_global(h->track->capostart); e && e->tracktick < tt2; e=abc_next_global(e->next) ) { - if( e && e->tracktick >= tt1 ) { // we have a tempo event in this row - switch( e->cmd ) { - case cmdtempo: - eff.effect = UNI_GLOB_TEMPO; - eff.param.u = e->lpar; - eff.framedly = UFD_RUNONCE; - utrk_write_global(of->ut, &eff, PTMEM_TEMPO); - break; - case cmdpartbrk: - eff.effect = UNI_GLOB_PATBREAK; - eff.param.u = 0; - eff.framedly = UFD_RUNONCE; - utrk_write_global(of->ut, &eff, UNIMEM_NONE); - break; - } - } - } - for( t = h->track; t; t = t->next ) { - for( e=abc_next_note(t->capostart); e && e->tracktick < tt1; e=abc_next_note(e->next) ) - t->capostart = e; - i = 0; - ef = NULL; - en = e; - el = e; - for( ; e && e->tracktick < tt2; e=abc_next_note(e->next) ) { // we have a note event in this row - t->capostart = e; - i++; - if( e->par[volume] ) { - if( !ef ) ef = e; - el = e; - } - } - if( i ) { - trillbits &= ~(1<ut, ch); - if( i == 1 || ef == el || !ef ) { // only one event in this row - if( ef ) e = ef; - else e = en; - el = t->capostart; - i = e->par[note] + ((e->par[octave])*12); - if( t->chan == 10 ) { - n = pat_gm_drumnote(i) + 23; - ins = pat_gmtosmp(pat_gm_drumnr(i)); - } - else { - n = pat_modnote(i); - ins = e->par[smpno]; - } - eff.framedly = modticks(e->tracktick - tt1); - vol = e->par[volume]; - if( e->par[effect] == accent ) { - vol += vol / 10; - if( vol > 127 ) vol = 127; - } - if (vol <= 0) {} - else if( el->par[volume] == 0 ) { - eff.framedly = modticks(el->tracktick - tt1); - eff.param.u = 0; - eff.param.byte_a = n; - eff.param.byte_b = ins; - eff.effect = UNI_NOTEKILL; - utrk_write_local(of->ut, &eff, UNIMEM_NONE); - } - else { - switch( e->par[effect] ) { - case trill: - eff.effect = UNI_VIBRATO_DEPTH; - eff.param.u = 12; // depth 1.5 - utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_DEPTH); - eff.effect = UNI_VIBRATO_SPEED; - eff.param.u = 48; // speed 12 - utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_SPEED); - trillbits |= (1<speed/2)|UFD_RUNONCE; - eff.param.s = 2; - utrk_write_local(of->ut, &eff, (e->par[effoper])? PTMEM_PITCHSLIDEUP: PTMEM_PITCHSLIDEDN); - break; - default: - break; - } - if( eff.framedly ) { - eff.param.u = 0; - eff.param.byte_a = n; - eff.param.byte_b = ins; - eff.effect = UNI_NOTEDELAY; - utrk_write_local(of->ut, &eff, UNIMEM_NONE); - } - } - utrk_write_inst(of->ut, ins); - utrk_write_note(of->ut, n); // <- normal note - pt_write_effect(of->ut, 0xc, vol); - } - else { - // two notes in one row, use FINEPITCHSLIDE runonce effect - // start first note on first tick and framedly runonce on seconds note tick - // use volume and instrument of last note - if( t->chan == 10 ) { - i = el->par[note] + ((el->par[octave])*12); - n = pat_gm_drumnote(i) + 23; - ins = pat_gmtosmp(pat_gm_drumnr(i)); - i = n; // cannot change instrument here.. - } - else { - i = ef->par[note] + ((ef->par[octave])*12); - n = pat_modnote(i); - ins = el->par[smpno]; - i = pat_modnote(el->par[note] + ((el->par[octave])*12)); - } - vol = el->par[volume]; - eff.effect = UNI_PITCHSLIDE; - eff.framedly = modticks(el->tracktick - tt1)|UFD_RUNONCE; - eff.param.s = ((i > n)?i-n:n-i); - utrk_write_inst(of->ut, ins); - utrk_write_note(of->ut, n); // <- normal note - pt_write_effect(of->ut, 0xc, vol); - utrk_write_local(of->ut, &eff, (i > n)? PTMEM_PITCHSLIDEUP: PTMEM_PITCHSLIDEDN); - } - } - else { // no new notes, keep on trilling... - if( trillbits & (1<ut, ch); - eff.effect = UNI_VIBRATO_DEPTH; - eff.param.u = 12; // depth 1.5 - utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_DEPTH); - eff.effect = UNI_VIBRATO_SPEED; - eff.param.u = 60; // speed 15 - utrk_write_local(of->ut, &eff, PTMEM_VIBRATO_SPEED); - } - } - ch++; - } - utrk_newline(of->ut); - } - if(!utrk_dup_pattern(of->ut,of)) return; - } -} - -#else - -static int ABC_ReadPatterns(MODCOMMAND *pattern[], WORD psize[], ABCHANDLE *h, int numpat, int channels) -// ===================================================================================== -{ - int pat,row,i,ch,trillbits; - BYTE n,ins,vol; - ABCTRACK *t; - ABCEVENT *e, *en, *ef, *el; - ULONG tt1, tt2; - MODCOMMAND *m; - int patbrk, tempo; - if( numpat > MAX_PATTERNS ) numpat = MAX_PATTERNS; - - // initialize start points of event list in tracks - for( t = h->track; t; t = t->next ) t->capostart = t->head; - trillbits = 0; // trill effect admininstration: one bit per channel, max 32 channnels - for( pat = 0; pat < numpat; pat++ ) { - pattern[pat] = CSoundFile::AllocatePattern(64, channels); - if( !pattern[pat] ) return 0; - psize[pat] = 64; - for( row = 0; row < 64; row++ ) { - tt1 = abcticks((pat * 64 + row ) * h->speed); - tt2 = tt1 + abcticks(h->speed); - ch = 0; - tempo = 0; - patbrk = 0; - for( e=abc_next_global(h->track->capostart); e && e->tracktick < tt2; e=abc_next_global(e->next) ) { - if( e && e->tracktick >= tt1 ) { // we have a tempo event in this row - switch( e->cmd ) { - case cmdtempo: - tempo = e->lpar; - break; - case cmdpartbrk: - patbrk = 1; - break; - } - } - } - for( t = h->track; t; t = t->next ) { - for( e=abc_next_note(t->capostart); e && e->tracktick < tt1; e=abc_next_note(e->next) ) ; - i = 0; - ef = NULL; - en = e; - el = e; - for( ; e && e->tracktick < tt2; e=abc_next_note(e->next) ) { // we have a note event in this row - t->capostart = e; - i++; - if( e->par[volume] ) { - if( !ef ) ef = e; - el = e; - } - } - m = &pattern[pat][row * channels + ch]; - m->param = 0; - m->command = CMD_NONE; - if( i ) { - trillbits &= ~(1<capostart; - i = e->par[note] + ((e->par[octave])*12); - if( t->chan == 10 ) { - n = pat_gm_drumnote(i) + 23; - ins = pat_gmtosmp(pat_gm_drumnr(i)); - } - else { - n = pat_modnote(i); - ins = e->par[smpno]; - } - vol = e->par[volume]/2; - if( e->par[volume] > 0 ) { - if( e->par[effect] == accent ) vol += vol / 20; - if( vol > 64 ) vol = 64; - if( el->par[volume] == 0 ) { // note cut - m->param = el->tracktick - tt1; - m->command = CMD_S3MCMDEX; - m->param |= 0xC0; - } - else { - switch( e->par[effect] ) { - case trill: - m->command = CMD_VIBRATO; - m->param = 0xC2; // speed 12 depth 2 - trillbits |= (1<command = CMD_XFINEPORTAUPDOWN; - m->param |= (e->par[effoper])? 0x12: 0x22; - break; - default: - m->param = modticks(e->tracktick - tt1); - if( m->param ) { // note delay - m->command = CMD_S3MCMDEX; - m->param |= 0xD0; - } - break; - } - } - } - m->instr = ins; - m->note = n; // <- normal note - m->volcmd = VOLCMD_VOLUME; - m->vol = vol; - } - else { - // two notes in one row, use FINEPITCHSLIDE runonce effect - // start first note on first tick and framedly runonce on seconds note tick - // use volume and instrument of last note - if( t->chan == 10 ) { - i = el->par[note] + ((el->par[octave])*12); - n = pat_gm_drumnote(i) + 23; - ins = pat_gmtosmp(pat_gm_drumnr(i)); - i = n; // cannot change instrument here.. - } - else { - i = ef->par[note] + ((ef->par[octave])*12); - n = pat_modnote(i); - ins = el->par[smpno]; - i = pat_modnote(el->par[note] + ((el->par[octave])*12)); - } - vol = el->par[volume]/2; - if( vol > 64 ) vol = 64; - m->instr = ins; - m->note = n; // <- normal note - m->volcmd = VOLCMD_VOLUME; - m->vol = vol; - m->param = ((i > n)?i-n:n-i); - if( m->param < 16 ) { - if( m->param ) { - m->command = CMD_XFINEPORTAUPDOWN; - m->param |= (i > n)? 0x10: 0x20; - } - else { // retrigger same note... - m->command = CMD_RETRIG; - m->param = modticks(el->tracktick - tt1); - } - } - else - m->command = (i > n)? CMD_PORTAMENTOUP: CMD_PORTAMENTODOWN; - } - } - else { // no new notes, keep on trilling... - if( trillbits & (1<command = CMD_VIBRATO; - m->param = 0; // inherited from first effect - m->instr = 0; - m->note = 0; - m->volcmd = 0; - m->vol = 0; - } - } - if( m->param == 0 && m->command == CMD_NONE ) { - if( tempo ) { - m->command = CMD_TEMPO; - m->param = tempo; - tempo = 0; - } - else { - if( patbrk ) { - m->command = CMD_PATTERNBREAK; - patbrk = 0; - } - } - } - ch++; - } - if( tempo || patbrk ) return 1; - } - } - return 0; -} - -#endif - -static int ABC_Key(const char *p) -{ - int i,j; - char c[8]; - const char *q; - while( isspace(*p) ) p++; - i = 0; - q = p; - for( i=0; i<8 && *p && *p != ']'; p++ ) { - if( isspace(*p) ) { - while( isspace(*p) ) p++; - if( strnicmp(p, "min", 3) && strnicmp(p, "maj", 3) ) - break; - } - c[i] = *p; - i++; - } - c[i] = '\0'; - if( !strcmp(c,"Hp") || !strcmp(c,"HP") ) // highland pipes - strcpy(c,"Bm"); // two sharps at c and f - if( !strcasecmp(c+1, "minor") ) i=2; - if( !strcasecmp(c+2, "minor") ) i=3; - if( !strcasecmp(c+1, "major") ) i=1; - if( !strcasecmp(c+2, "major") ) i=2; - if( !strcasecmp(c+1, "min") ) i=2; - if( !strcasecmp(c+2, "min") ) i=3; - if( !strcasecmp(c+1, "maj") ) i=1; - if( !strcasecmp(c+2, "maj") ) i=2; - for( ; i<6; i++ ) - c[i] = ' '; - c[i] = '\0'; - for( i=0; keySigs[i]; i++ ) { - for( j=10; j<46; j+=6 ) - if( !strnicmp(keySigs[i]+j, c, 6) ) - return i; - } - abc_message("Failure: Unrecognised K: field %s", q); - return 7; -} - -static char *abc_skip_word(char *p) -{ - while( isspace(*p) ) p++; - while( *p && !isspace(*p) && *p != ']') p++; - while( isspace(*p) ) p++; - return p; -} - -static ULONG abc_tracktime(ABCTRACK *tp) -{ - ULONG tracktime; - if( tp->tail ) tracktime = tp->tail->tracktick; - else tracktime = 0; - if( tracktime < global_songstart ) - tracktime = global_songstart; - return tracktime; -} - -static void abc_addchordname(char *s, int len, int *notes) -// adds chord name and note set to list of known chords -{ - int i, j; - if(strlen(s) > 7) { - abc_message("Failure: Chord name cannot exceed 7 characters, %s", s); - return; - } - if(len > 6) { - abc_message("Failure: Named chord cannot have more than 6 notes, %s", s); - return; - } - for( i=0; i < chordsnamed; i++ ) { - if(strcmp(s, chordname[i]) == 0) { - /* change chord */ - chordlen[i] = len; - for(j = 0; j < len; j++) chordnotes[i][j] = notes[j]; - return; - } - } - if(chordsnamed > MAXCHORDNAMES - 1) - abc_message("Failure: Too many Guitar Chord Names used, %s", s); - else { - strcpy(chordname[chordsnamed], s); - chordlen[chordsnamed] = len; - for(j = 0; j < len; j++) chordnotes[chordsnamed][j] = notes[j]; - chordsnamed++; - } -} - -static void abc_setup_chordnames() -// set up named guitar chords -{ - static int list_Maj[3] = { 0, 4, 7 }; - static int list_m[3] = { 0, 3, 7 }; - static int list_7[4] = { 0, 4, 7, 10 }; - static int list_m7[4] = { 0, 3, 7, 10 }; - static int list_maj7[4] = { 0, 4, 7, 11 }; - static int list_M7[4] = { 0, 4, 7, 11 }; - static int list_6[4] = { 0, 4, 7, 9 }; - static int list_m6[4] = { 0, 3, 7, 9 }; - static int list_aug[3] = { 0, 4, 8 }; - static int list_plus[3] = { 0, 4, 8 }; - static int list_aug7[4] = { 0, 4, 8, 10 }; - static int list_dim[3] = { 0, 3, 6 }; - static int list_dim7[4] = { 0, 3, 6, 9 }; - static int list_9[5] = { 0, 4, 7, 10, 2 }; - static int list_m9[5] = { 0, 3, 7, 10, 2 }; - static int list_maj9[5] = { 0, 4, 7, 11, 2 }; - static int list_M9[5] = { 0, 4, 7, 11, 2 }; - static int list_11[6] = { 0, 4, 7, 10, 2, 5 }; - static int list_dim9[5] = { 0, 4, 7, 10, 13 }; - static int list_sus[3] = { 0, 5, 7 }; - static int list_sus9[3] = { 0, 2, 7 }; - static int list_7sus[4] = { 0, 5, 7, 10 }; - static int list_7sus4[4] = { 0, 5, 7, 10 }; - static int list_7sus9[4] = { 0, 2, 7, 10 }; - static int list_9sus4[5] = { 0, 5, 10, 14, 19 }; - static int list_5[2] = { 0, 7 }; - static int list_13[6] = { 0, 4, 7, 10, 16, 21 }; - - chordsnamed = 0; - abc_addchordname("", 3, list_Maj); - abc_addchordname("m", 3, list_m); - abc_addchordname("7", 4, list_7); - abc_addchordname("m7", 4, list_m7); - abc_addchordname("maj7", 4, list_maj7); - abc_addchordname("M7", 4, list_M7); - abc_addchordname("6", 4, list_6); - abc_addchordname("m6", 4, list_m6); - abc_addchordname("aug", 3, list_aug); - abc_addchordname("+", 3, list_plus); - abc_addchordname("aug7", 4, list_aug7); - abc_addchordname("7+", 4, list_aug7); - abc_addchordname("dim", 3, list_dim); - abc_addchordname("dim7", 4, list_dim7); - abc_addchordname("9", 5, list_9); - abc_addchordname("m9", 5, list_m9); - abc_addchordname("maj9", 5, list_maj9); - abc_addchordname("M9", 5, list_M9); - abc_addchordname("11", 6, list_11); - abc_addchordname("dim9", 5, list_dim9); - abc_addchordname("sus", 3, list_sus); - abc_addchordname("sus9", 3, list_sus9); - abc_addchordname("7sus", 4, list_7sus); - abc_addchordname("7sus4", 4, list_7sus4); - abc_addchordname("7sus9", 4, list_7sus9); - abc_addchordname("9sus4", 5, list_9sus4); - abc_addchordname("5", 2, list_5); - abc_addchordname("13", 6, list_13); -} - -static int abc_MIDI_getnumber(const char *p) -{ - int n; - while( isspace(*p) ) p++; - abc_getnumber(p, &n); - if( n < 0 ) n = 0; - if( n > 127 ) n = 127; - return n; -} - -static int abc_MIDI_getprog(const char *p) -{ - int n; - while( isspace(*p) ) p++; - abc_getnumber(p, &n); - if( n < 1 ) n = 1; - if( n > 128 ) n = 128; - return n; -} - -// MIDI drone -static void abc_MIDI_drone(const char *p, int *gm, int *ptch, int *vol) -{ - int i; - while( isspace(*p) ) p++; - p += abc_getnumber(p, &i); - i++; // adjust for 1..128 - if( i>0 && i < 129 ) - *gm = i; - else - *gm = 71; // bassoon - while( isspace(*p) ) p++; - p += abc_getnumber(p, &i); - if( i>0 && i < 127 ) - ptch[0] = i; - else - ptch[0] = 45; - while( isspace(*p) ) p++; - p += abc_getnumber(p, &i); - if( i>0 && i < 127 ) - ptch[1] = i; - else - ptch[1] = 33; - while( isspace(*p) ) p++; - p += abc_getnumber(p, &i); - if( i>0 && i < 127 ) - vol[0] = i; - else - vol[0] = 80; - while( isspace(*p) ) p++; - p += abc_getnumber(p, &i); - if( i>0 && i < 127 ) - vol[1] = i; - else - vol[1] = 80; -} - -static void abc_chan_to_tracks(ABCHANDLE *h, int tno, int ch) -{ - ABCTRACK *tp; - if( tno>0 && tno<33 ) { - for( tp=h->track; tp; tp=tp->next ) { - if( tp->vno == tno && (tp->vpos < GCHORDBPOS || tp->vpos > DRONEPOS2) ) - tp->chan = ch; - } - } -} - -// %%MIDI channel int1 -// channel numbers are 1-16 -static void abc_MIDI_channel(const char *p, ABCTRACK *tp, ABCHANDLE *h) -{ - int i1, i2; - i1 = tp? tp->vno: 1; - for( ; *p && isspace(*p); p++ ) ; - if( isdigit(*p) ) { - p += abc_getnumber(p, &i2); - if( i2 >= 1 && i2 <= 16 ) - abc_chan_to_tracks(h, i1, i2); // we start at 1 - } -} - -static void abc_instr_to_tracks(ABCHANDLE *h, int tno, int gm) -{ - ABCTRACK *tp; - if( tno>0 && tno<33 && gm>0 && gm<129 ) { - for( tp=h->track; tp; tp=tp->next ) { - if( tp->vno == tno && (tp->vpos < GCHORDBPOS || tp->vpos > DRONEPOS2) ) - tp->instr = gm; - } - } -} - -// %%MIDI program [int1] -// instrument numbers are 0-127 -static void abc_MIDI_program(const char *p, ABCTRACK *tp, ABCHANDLE *h) -{ - int i1, i2; - i1 = tp? tp->vno: 1; - for( ; *p && isspace(*p); p++ ) ; - if( isdigit(*p) ) { - p += abc_getnumber(p, &i2); - for( ; *p && isspace(*p); p++ ) ; - if( isdigit(*p) ) { - i1 = i2; - p += abc_getnumber(p, &i2); - } - abc_instr_to_tracks(h, i1, i2 + 1); // we start at 1 - } -} - -static void abc_mute_voice(ABCHANDLE *h, ABCTRACK *tp, int m) -{ - ABCTRACK *t; - for( t=h->track; t; t=t->next ) { - if( t->vno == tp->vno ) t->mute = m; - } -} - -// %%MIDI voice [] [instrument= [bank=]] [mute] -// instrument numbers are 1-128 -static void abc_MIDI_voice(const char *p, ABCTRACK *tp, ABCHANDLE *h) -{ - int i1, i2; - for( ; *p && isspace(*p); p++ ) ; - if( strncmp(p,"instrument=",11) && strncmp(p,"mute",4) ) { - tp = abc_locate_track(h, p, 0); - for( ; *p && !isspace(*p); p++ ) ; - for( ; *p && isspace(*p); p++ ) ; - } - i1 = tp? tp->vno: 1; - i2 = 0; - if( !strncmp(p,"instrument=",11) && isdigit(p[11]) ) { - p += 11; - p += abc_getnumber(p, &i2); - for( ; *p && isspace(*p); p++ ) ; - if( !strncmp(p,"bank=",5) && isdigit(p[5]) ) { - for( ; *p && !isspace(*p); p++ ) ; - for( ; *p && isspace(*p); p++ ) ; - } - } - if( tp ) abc_mute_voice(h,tp,0); - if( !strncmp(p,"mute",4) && (p[4]=='\0' || p[4]=='%' || isspace(p[4])) ) { - if( tp ) abc_mute_voice(h,tp,1); - } - abc_instr_to_tracks(h, i1, i2); // starts already at 1 (draft 4.0) -} - -// %%MIDI chordname ... -static void abc_MIDI_chordname(const char *p) -{ - char name[20]; - int i, notes[6]; - - for( ; *p && isspace(*p); p++ ) ; - i = 0; - while ((i < 19) && (*p != ' ') && (*p != '\0')) { - name[i] = *p; - p = p + 1; - i = i + 1; - } - name[i] = '\0'; - if(*p != ' ') { - abc_message("Failure: Bad format for chordname command, %s", p); - } - else { - i = 0; - while ((i <= 6) && isspace(*p)) { - for( ; *p && isspace(*p); p++ ) ; - p += abc_getnumber(p, ¬es[i]); - i = i + 1; - } - abc_addchordname(name, i, notes); - } -} - -// %%MIDI drum ... ... -// instrument numbers are 0-127 -static int abc_MIDI_drum(const char *p, ABCHANDLE *h) -{ - char *q; - int i,n,m; - while( isspace(*p) ) p++; - if( !strncmp(p,"on",2) && (isspace(p[2]) || p[2] == '\0') ) return 2; - if( !strncmp(p,"off",3) && (isspace(p[3]) || p[3] == '\0') ) return 1; - n = 0; - for( q = h->drum; *p && !isspace(*p); p++ ) { - if( !strchr("dz0123456789",*p) ) break; - *q++ = *p; - if( !isdigit(*p) ) { - if( !isdigit(p[1]) ) *q++ = '1'; - n++; // count the silences too.... - } - } - *q = '\0'; - q = h->drumins; - for( i = 0; idrum[i*2] == 'd' ) { - while( isspace(*p) ) p++; - if( !isdigit(*p) ) { - m = 0; - while( !isspace(*p) ) p++; - } - else - p += abc_getnumber(p,&m); - q[i] = m + 1; // we start at 1 - } - else q[i] = 0; - } - q = h->drumvol; - for( i = 0; idrum[i*2] == 'd' ) { - while( isspace(*p) ) p++; - if( !isdigit(*p) ) { - m = 0; - while( !isspace(*p) ) p++; - } - else - p += abc_getnumber(p,&m); - q[i] = m; - } - else q[i] = 0; - } - return 0; -} - -// %%MIDI gchord -static int abc_MIDI_gchord(const char *p, ABCHANDLE *h) -{ - char *q; - while( isspace(*p) ) p++; - if( !strncmp(p,"on",2) && (isspace(p[2]) || p[2] == '\0') ) return 2; - if( !strncmp(p,"off",3) && (isspace(p[3]) || p[3] == '\0') ) return 1; - for( q = h->gchord; *p && !isspace(*p); p++ ) { - if( !strchr("fbcz0123456789ghijGHIJ",*p) ) break; - *q++ = *p; - if( !isdigit(*p) && !isdigit(p[1]) ) *q++ = '1'; - } - *q = '\0'; - return 0; -} - -static void abc_metric_gchord(ABCHANDLE *h, int mlen, int mdiv) -{ - switch( 16 * mlen + mdiv ) { - case 0x24: - case 0x44: - case 0x22: - abc_MIDI_gchord("fzczfzcz", h); - break; - case 0x64: - case 0x32: - abc_MIDI_gchord("fzczczfzczcz", h); - break; - case 0x34: - case 0x38: - abc_MIDI_gchord("fzczcz", h); - break; - case 0x68: - abc_MIDI_gchord("fzcfzc", h); - break; - case 0x98: - abc_MIDI_gchord("fzcfzcfzc", h); - break; - case 0xc8: - abc_MIDI_gchord("fzcfzcfzcfzc", h); - break; - default: - if( mlen % 3 == 0 ) - abc_MIDI_gchord("fzcfzcfzcfzcfzcfzcfzcfzcfzc", h); - else - abc_MIDI_gchord("fzczfzczfzczfzczfzczfzczfzcz", h); - if( mdiv == 8 ) h->gchord[mlen*2] = '\0'; - else h->gchord[mlen*4] = '\0'; - break; - } -} - -static void abc_MIDI_legato(const char *p, ABCTRACK *tp) -{ - for( ; *p && isspace(*p); p++ ) ; - if( !strncmp(p,"off",3) ) tp->legato = 0; - else tp->legato = 1; -} - -static void abc_M_field(const char *p, int *mlen, int *mdiv) -{ - if( !strncmp(p,"none",4) ) { - *mlen = 1; - *mdiv = 1; - return; - } - if( !strncmp(p,"C|",2) ) { - *mlen = 2; - *mdiv = 2; - return; - } - if( *p == 'C' ) { - *mlen = 4; - *mdiv = 4; - return; - } - p += abc_getexpr(p,mlen); - sscanf(p," / %d", mdiv); -} - -static int abc_drum_steps(const char *dch) -{ - const char *p; - int i=0; - for( p=dch; *p; p++ ) { - if( isdigit(*p) ) i += *p - '0';; - } - return i; -} - -static void abc_add_drum(ABCHANDLE *h, ULONG tracktime, ULONG bartime) -{ - ABCEVENT *e; - ABCTRACK *tp; - ULONG etime, ctime , rtime, stime; - int i, g, steps, gnote, gsteps, nnum; - steps = abc_drum_steps(h->drum); - ctime = h->barticks; - // look up the last event in tpr drumtrack - tp = abc_locate_track(h, h->tpr->v, DRUMPOS); - e = tp->tail; - etime = e? e->tracktick: bartime; - if( etime > tracktime ) return; - if( etime < bartime ) rtime = h->barticks - ((bartime - etime) % h->barticks); - else rtime = (etime - bartime) % h->barticks; - stime = ctime*steps; - rtime *= steps; - rtime += stime; - gsteps = strlen(h->drum)/2; - g = 0; - while( rtime > stime ) { - rtime -= ctime*(h->drum[g*2+1] - '0'); - if( ++g == gsteps ) g = 0; - } - stime = (tracktime - etime) * steps; - rtime = 0; - while( rtime < stime ) { - gnote = h->drum[g*2]; - i = h->drum[g*2+1] - '0'; - if(gnote=='d') { - tp->instr = pat_gm_drumnr(h->drumins[g]-1); - nnum = pat_gm_drumnote(h->drumins[g]); - abc_add_drumnote(h, tp, etime + rtime/steps, nnum, h->drumvol[g]); - abc_add_noteoff(h,tp,etime + ( rtime + ctime * i )/steps); - } - if( ++g == gsteps ) g = 0; - rtime += ctime * i; - } -} - -static int abc_gchord_steps(const char *gch) -{ - const char *p; - int i=0; - for( p=gch; *p; p++ ) - if( isdigit(*p) ) i += *p - '0'; - return i; -} - -static void abc_add_gchord(ABCHANDLE *h, ULONG tracktime, ULONG bartime) -{ - ABCEVENT *e, *c; - ABCTRACK *tp; - ULONG etime, ctime , rtime, stime; - int i, g, steps, gnote, gcnum, gsteps, nnum, glen; - // look up the last chord event in tpc - c = 0; - for( e = h->tpc->head; e; e = e->next ) - if( e->flg == 1 && e->cmd == cmdchord ) - c = e; - if( !c ) return; - gcnum = c->par[chordnum]; - steps = abc_gchord_steps(h->gchord); - ctime = h->barticks; - etime = 0; - for( i = GCHORDBPOS; i < DRUMPOS; i++ ) { - tp = abc_locate_track(h, h->tpc->v, i); - e = tp->tail; - if( !e ) e = c; - stime = e->tracktick; - if( stime > etime ) etime = stime; - } - if( etime > tracktime ) return; - if( etime < bartime ) rtime = h->barticks - ((bartime - etime) % h->barticks); - else rtime = (etime - bartime) % h->barticks; - stime = ctime * steps; - rtime *= steps; - rtime += stime; - gsteps = strlen(h->gchord); - g = 0; - while( rtime > stime ) { - glen = h->gchord[2*g+1] - '0'; - rtime -= ctime * glen; - if( ++g == gsteps ) g = 0; - } - stime = (tracktime - etime) * steps; - rtime = 0; - while( rtime < stime ) { - gnote = h->gchord[2*g]; - glen = h->gchord[2*g+1] - '0'; - if( ++g == gsteps ) g = 0; - nnum = 0; - switch(gnote) { - case 'b': - tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS); - tp->instr = h->abcbassprog; - nnum = c->par[chordnote]+chordnotes[gcnum][0]+24; - abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcbassvol); - abc_add_noteoff(h,tp,etime + ( rtime + ctime * glen )/steps); - case 'c': - for( i = 1; i < chordlen[gcnum]; i++ ) { - tp = abc_locate_track(h, h->tpc->v, i+GCHORDFPOS); - tp->instr = h->abcchordprog; - nnum = c->par[chordnote]+chordnotes[gcnum][i]+24; - abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcchordvol); - abc_add_noteoff(h,tp,etime + ( rtime + ctime * glen )/steps); - } - rtime += ctime * glen; - break; - case 'f': - tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS); - tp->instr = h->abcbassprog; - nnum = c->par[chordbase]+12; - abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcbassvol); - rtime += ctime * glen; - abc_add_noteoff(h,tp,etime + rtime/steps); - break; - case 'g': - case 'h': - case 'i': - case 'j': - case 'G': - case 'H': - case 'I': - case 'J': - i = toupper(gnote) - 'G'; - nnum = 0; - if( i < chordlen[gcnum] ) { - tp = abc_locate_track(h, h->tpc->v, GCHORDFPOS+i+1); - tp->instr = h->abcchordprog; - nnum = c->par[chordnote]+chordnotes[gcnum][i]+24; - if( isupper(gnote) ) nnum -= 12; - abc_add_chordnote(h, tp, etime + rtime/steps, nnum, h->abcchordvol); - } - rtime += ctime * glen; - if( nnum ) abc_add_noteoff(h,tp,etime + rtime/steps); - break; - case 'z': - rtime += ctime * glen; - break; - } - } -} - -// %%MIDI beat a b c n -// -// controls the way note velocities are selected. The first note in a bar has -// velocity a. Other "strong" notes have velocity b and all the rest have velocity -// c. a, b and c must be in the range 0-128. The parameter n determines which -// notes are "strong". If the time signature is x/y, then each note is given -// a position number k = 0, 1, 2 .. x-1 within each bar. Note that the units for -// n are not the unit note length. If k is a multiple of n, then the note is -// "strong". The volume specifiers !ppp! to !fff! are equivalent to the -// following : -// -// !ppp! = %%MIDI beat 30 20 10 1 -// !pp! = %%MIDI beat 45 35 20 1 -// !p! = %%MIDI beat 60 50 35 1 -// !mp! = %%MIDI beat 75 65 50 1 -// !mf! = %%MIDI beat 90 80 65 1 -// !f! = %%MIDI beat 105 95 80 1 -// !ff! = %%MIDI beat 120 110 95 1 -// !fff! = %%MIDI beat 127 125 110 1 -static void abc_MIDI_beat(ABCHANDLE *h, const char *p) -{ - int i,j; - h->beat[0] = 127; - h->beat[1] = 125; - h->beat[2] = 110; - h->beat[3] = 1; - for( j=0; j<4; j++ ) { - while( isspace(*p) ) p++; - if( *p ) { - p += abc_getnumber(p, &i); - if( i < 0 ) i = 0; - if( i > 127 ) i = 127; - h->beat[j] = i; - } - } - if( h->beat[3] == 0 ) h->beat[3] = 1; // BB Ruud says: do not let you make mad -} - -// -// %%MIDI beatstring -// -// This provides an alternative way of specifying where the strong and weak -// stresses fall within a bar. 'f' means velocity a (normally strong), 'm' -// means velocity b (medium velocity) and 'p' means velocity c (soft velocity). -// For example, if the time signature is 7/8 with stresses on the first, fourth -// and sixth notes in the bar, we could use the following -// -// %%MIDI beatstring fppmpmp -static void abc_MIDI_beatstring(ABCHANDLE *h, char *p) -{ - while( isspace(*p) ) p++; - if( h->beatstring ) _mm_free(h->allochandle, h->beatstring); - if( strlen(p) ) - h->beatstring = DupStr(h->allochandle,p,strlen(p)+1); - else - h->beatstring = NULL; -} - -static int abc_beat_vol(ABCHANDLE *h, int abcvol, int barpos) -{ - int vol; - if( h->beatstring ) { - vol = (h->beat[2] * 9) / 10; - if( barpos < (int)strlen(h->beatstring) ) { - switch(h->beatstring[barpos]) { - case 'f': - vol = h->beat[0]; - break; - case 'm': - vol = h->beat[1]; - break; - case 'p': - vol = h->beat[2]; - break; - default: - break; - } - } - } - else { - if( (barpos % h->beat[3]) == 0 ) { - if( barpos ) - vol = h->beat[1]; - else - vol = h->beat[0]; - } - else - vol = h->beat[2]; - } - vol *= abcvol; - vol /= 128; - return vol; -} - -static void abc_init_partpat(BYTE partp[27][2]) -{ - int i; - for( i=0; i<27; i++ ) { - partp[i][0] = 0xff; - partp[i][1] = 0; - } -} - -static int abc_partpat_to_orderlist(BYTE partp[27][2], const char *abcparts, ABCHANDLE *h, BYTE **list, int orderlen) -{ - int t, partsused; - const char *p; - BYTE *orderlist = *list; - static int ordersize = 0; - if( *list == NULL ) { - ordersize = 128; - orderlist = (BYTE *)_mm_calloc(h->ho, ordersize, sizeof(BYTE)); - *list = orderlist; - } - if( abcparts ) { - partsused = 0; - for( p = abcparts; *p; p++ ) { - for( t = partp[*p - 'A'][0]; t < partp[*p - 'A'][1]; t++ ) { - if( orderlen == ordersize ) { - ordersize <<= 1; - orderlist = (BYTE *)_mm_recalloc(h->ho, orderlist, ordersize, sizeof(BYTE)); - *list = orderlist; - } - orderlist[orderlen] = t; - orderlen++; - partsused++; - } - } - if( partsused ) return orderlen; - } - // some fool wrote a P: string in the header but didn't use P: in the body - for( t = partp[26][0]; t < partp[26][1]; t++ ) { - if( orderlen == ordersize ) { - ordersize <<= 1; - orderlist = (BYTE *)_mm_recalloc(h->ho, orderlist, ordersize, sizeof(BYTE)); - *list = orderlist; - } - orderlist[orderlen] = t; - orderlen++; - } - return orderlen; -} - -static void abc_globalslide(ABCHANDLE *h, ULONG tracktime, int slide) -{ - ABCTRACK *tp; - ABCEVENT *e; - int hslide; - hslide = h->track? h->track->slidevol: slide; - for( tp=h->track; tp; tp = tp->next ) { - if( slide ) { - tp->slidevoltime = tracktime; - if( slide == 2 ) - tp->slidevol = 0; - } - if( tp->slidevol > -2 && slide < 2 ) - tp->slidevol = slide; - } - if( h->track && h->track->tail - && hslide != slide && slide == -2 - && h->track->tail->tracktick >= tracktime ) { - // need to update jumptypes in mastertrack from tracktime on... - for( e=h->track->head; e; e=e->next ) { - if( e->flg == 1 && e->cmd == cmdjump && e->tracktick >= tracktime ) { - switch( e->par[jumptype] ) { - case jumpnormal: - case jumpfade: - e->par[jumptype] = jumpfade; - break; - case jumpdacapo: - case jumpdcfade: - e->par[jumptype] = jumpdcfade; - break; - case jumpdasegno: - case jumpdsfade: - e->par[jumptype] = jumpdsfade; - break; - } - } - } - } -} - -static void abc_recalculate_tracktime(ABCHANDLE *h) { - ABCTRACK *ttp; - h->tracktime = 0; - for( ttp=h->track; ttp; ttp=ttp->next ) - if( ttp->tail && ttp->tail->tracktick > h->tracktime ) - h->tracktime = ttp->tail->tracktick; -} - -static void abc_MIDI_command(ABCHANDLE *h, char *p, char delim) { - int t; - // interpret some of the possibilitys - if( !strncmp(p,"bassprog",8) && isspace(p[8]) ) h->abcbassprog = abc_MIDI_getprog(p+8)+1; - if( !strncmp(p,"bassvol",7) && isspace(p[7]) ) h->abcbassvol = abc_MIDI_getnumber(p+7); - if( !strncmp(p,"beat",4) && isspace(p[4]) ) abc_MIDI_beat(h, p+4); - if( !strncmp(p,"beatstring",10) && isspace(p[10]) ) abc_MIDI_beatstring(h, p+4); - if( !strncmp(p,"chordname",9) && isspace(p[9]) ) abc_MIDI_chordname(p+9); - if( !strncmp(p,"chordprog",9) && isspace(p[9]) ) h->abcchordprog = abc_MIDI_getprog(p+9)+1; - if( !strncmp(p,"chordvol",8) && isspace(p[8]) ) h->abcchordvol = abc_MIDI_getnumber(p+8); - if( !strncmp(p,"drone",5) && isspace(p[5]) ) abc_MIDI_drone(p+5, &h->dronegm, h->dronepitch, h->dronevol); - if( !strncmp(p,"droneoff",8) && (p[8]=='\0' || p[8]==delim || isspace(p[8])) ) h->droneon = 0; - if( !strncmp(p,"droneon",7) && (p[7]=='\0' || p[7]==delim || isspace(p[7])) ) h->droneon = 1; - t = h->drumon; - if( !strncmp(p,"drum",4) && isspace(p[4]) ) { - h->drumon = abc_MIDI_drum(p+4, h); - if( h->drumon ) --h->drumon; - else h->drumon = t; - } - if( !strncmp(p,"drumoff",7) && (p[7]=='\0' || p[7]==delim || isspace(p[7])) ) h->drumon = 0; - if( !strncmp(p,"drumon",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) h->drumon = 1; - if( t != h->drumon ) { - if( h->drumon && !h->tpr ) h->tpr = h->track; - if( h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); // don't start drumming from the beginning of time! - if( h->tpr && !h->drumon ) h->tpr = NULL; - } - t = h->gchordon; - if( !strncmp(p,"gchord",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) { - h->gchordon = abc_MIDI_gchord(p+6, h); - if( h->gchordon ) --h->gchordon; - else h->gchordon = t; - } - if( !strncmp(p,"gchordoff",9) && (p[9]=='\0' || p[9]==delim || isspace(p[9])) ) h->gchordon = 0; - if( !strncmp(p,"gchordon",8) && (p[8]=='\0' || p[8]==delim || isspace(p[8])) ) h->gchordon = 1; - if( t != h->gchordon ) { - if( h->tpc ) abc_add_gchord_syncs(h, h->tpc, h->tracktime); - } - if( !strncmp(p,"channel",7) && isspace(p[7]) ) - abc_MIDI_channel(p+8, h->tp = abc_check_track(h, h->tp), h); - if( !strncmp(p,"program",7) && isspace(p[7]) ) - abc_MIDI_program(p+8, h->tp = abc_check_track(h, h->tp), h); - if( !strncmp(p,"voice",5) && isspace(p[5]) ) - abc_MIDI_voice(p+6, h->tp = abc_check_track(h, h->tp), h); - if( !strncmp(p,"legato",6) && (p[6]=='\0' || p[6]==delim || isspace(p[6])) ) - abc_MIDI_legato(p+6, h->tp = abc_check_track(h, h->tp)); -} - -// continuate line that ends with a backslash, can't do this in abc_gets because voice lines -// can have comment lines in between that must be parsed properly, for example: -// [V:1] cdef gabc' |\ << continuation backslash -// %%MIDI program 25 -// c'bag fedc | -// informational lines can have this too, so it is rather convoluted code... -static char *abc_continuated(ABCHANDLE *h, MMFILE *mmf, char *p) { - char *pm, *p1, *p2 = 0; - int continued; - pm = p; - while( pm[strlen(pm)-1]=='\\' ) { - p1 = strdup(pm); - if( p2 ) free(p2); - continued = 1; - while( continued ) { - continued = 0; - pm = abc_gets(h, mmf); - if( !pm ) { - abc_message("line not properly continued\n%s", p1); - return p1; - } - while( *pm && isspace(*pm) ) ++pm; - if( !strncmp(pm,"%%",2) ) { - for( p2 = pm+2; *p2 && isspace(*p2); p2++ ) ; - if( !strncmp(p2,"MIDI",4) && (p2[4]=='=' || isspace(p2[4])) ) { - for( p2+=5; *p2 && isspace(*p2); p2++ ) ; - if( *p2 == '=' ) - for( p2+=1; *p2 && isspace(*p2); p2++ ) ; - abc_MIDI_command(h,p2,'%'); - } - continued = 1; - } - } - p2 = (char *)malloc(strlen(p1)+strlen(pm)); - if( !p2 ) { - abc_message("macro line too long\n%s", p1); - return p1; - } - p1[strlen(p1)-1] = '\0'; // strip off the backslash - strcpy(p2,p1); - strcat(p2,pm); - pm = p2; - free(p1); - } - return pm; -} - -// ===================================================================================== -#ifdef NEWMIKMOD -BOOL ABC_Load(ABCHANDLE *h, UNIMOD *of, MMSTREAM *mmfile) -#else -BOOL CSoundFile::ReadABC(const BYTE *lpStream, DWORD dwMemLength) -#endif -{ - static int avoid_reentry = 0; -#ifdef NEWMIKMOD -#define m_nDefaultTempo of->inittempo -#else - ABCHANDLE *h; - uint numpat; - MMFILE mm, *mmfile; -#endif - uint t; - char *line, *p, *pp, ch, ch0=0; - char barsig[52]; // for propagated accidental key signature within bar - char *abcparts; - BYTE partpat[27][2], *orderlist; - int orderlen; - enum { NOWHERE, INBETWEEN, INHEAD, INBODY, INSKIPFORX, INSKIPFORQUOTE } abcstate; - ABCEVENT_JUMPTYPE j; - ABCEVENT_X_EFFECT abceffect; - int abceffoper; - int abcxcount=0, abcxwanted=0, abcxnumber=1; - int abckey, abcrate, abcchord, abcvol, abcbeatvol, abcnoslurs, abcnolegato, abcfermata, abcarpeggio, abcto; - int abctempo; - int cnotelen=0, cnotediv=0, snotelen, snotediv, mnotelen, mnotediv, notelen, notediv; - // c for chords, s for standard L: setting, m for M: barlength - int abchornpipe, brokenrithm, tupletp, tupletq, tupletr; - int ktempo; - ULONG abcgrace=0, bartime, thistime=0; - ABCTRACK *tpd, *ttp; - ABCMACRO *mp; - int mmsp; -#ifdef NEWMIKMOD - MMSTREAM *mmstack[MAXABCINCLUDES]; - h->ho = _mmalloc_create("Load_ABC_ORDERLIST", NULL); -#else - MMFILE *mmstack[MAXABCINCLUDES]; - if( !TestABC(lpStream, dwMemLength) ) return FALSE; - h = ABC_Init(); - if( !h ) return FALSE; - mmfile = &mm; - mm.mm = (char *)lpStream; - mm.sz = dwMemLength; - mm.pos = 0; -#endif - while( avoid_reentry ) sleep(1); - avoid_reentry = 1; - pat_resetsmp(); - pat_init_patnames(); - m_nDefaultTempo = 0; - global_voiceno = 0; - abckey = 0; - h->tracktime = 0; - global_songstart = 0; - h->speed = 6; - abcrate = 240; - global_tempo_factor = 2; - global_tempo_divider = 1; - abctempo = 0; - ktempo = 0; - abceffect = none; - abceffoper = 0; - abcvol = 120; - h->abcchordvol = abcvol; - h->abcbassvol = abcvol; - h->abcchordprog = 25; // acoustic guitar - h->abcbassprog = 33; // acoustic bass - abcparts = 0; - abcnoslurs = 1; - abcnolegato = 1; - abcfermata = 0; - abcarpeggio = 0; - abcto = 0; - snotelen = 0; - snotediv = 0; - mnotelen = 1; - mnotediv = 1; - abchornpipe = 0; - brokenrithm = 0; - tupletp = 0; - tupletq = 0; - tupletr = 0; - h->ktrans = 0; - h->drumon = 0; - h->gchordon = 1; - h->droneon = 0; - h->tracktime = 0; - bartime = 0; - h->tp = NULL; - h->tpc = NULL; - h->tpr = NULL; - tpd = NULL; - h->dronegm = 71; - h->dronepitch[0] = 45; - h->dronepitch[1] = 33; - h->dronevol[0] = 80; - h->dronevol[1] = 80; - abc_new_umacro(h, "v = +downbow+"); - abc_new_umacro(h, "u = +upbow+"); - abc_new_umacro(h, "O = +coda+"); - abc_new_umacro(h, "S = +segno+"); - abc_new_umacro(h, "P = +uppermordent+"); - abc_new_umacro(h, "M = +lowermordent+"); - abc_new_umacro(h, "L = +emphasis+"); - abc_new_umacro(h, "H = +fermata+"); - abc_new_umacro(h, "T = +trill+"); - abc_new_umacro(h, "~ = +roll+"); - abc_setup_chordnames(); - abc_init_partpat(partpat); - abc_MIDI_beat(h, ""); // reset beat array - abc_MIDI_beatstring(h, ""); // reset beatstring - orderlist = NULL; - orderlen = 0; - mmsp = 1; - mmstack[0] = mmfile; - mmfseek(mmfile,0,SEEK_SET); - abcstate = NOWHERE; - if( h->pickrandom ) { - abcstate = INSKIPFORX; - abcxcount = 0; - mmfseek(mmfile,0,SEEK_SET); - while( (line=abc_gets(h, mmfile)) ) { - for( p=line; isspace(*p); p++ ) ; - if( !strncmp(p,"X:",2) ) abcxcount++; - } - if( abcxcount == 0 ) - abcstate = NOWHERE; - else - abcxwanted = (h->pickrandom - 1) % abcxcount; - abcxcount = 0; - mmfseek(mmfile,0,SEEK_SET); - } - while( mmsp > 0 ) { - mmsp--; - while((line=abc_gets(h, mmstack[mmsp]))) { - for( p=line; isspace(*p); p++ ) ; - switch(abcstate) { - case INSKIPFORX: - if( !strncmp(p,"X:",2) ) { - if( abcxcount++ != abcxwanted ) - break; - } - // fall through - case INBETWEEN: - if( !strncmp(p,"X:",2) ) { - abcstate = INHEAD; -#ifdef NEWMIKMOD - of->songname = NULL; -#else - memset(m_szNames[0], 0, 32); -#endif - for( p+=2; isspace(*p); p++ ) ; - abcxnumber = atoi(p); - abchornpipe = 0; - h->droneon = 0; - h->dronegm = 71; - h->dronepitch[0] = 45; - h->dronepitch[1] = 33; - h->dronevol[0] = 80; - h->dronevol[1] = 80; - for( ttp = h->track; ttp; ttp=ttp->next ) { - ttp->vno = 0; // mark track unused - ttp->capostart = NULL; - } - h->tp = NULL; // forget old voices - h->tpc = NULL; - h->tpr = NULL; - global_voiceno = 0; - abc_set_parts(&abcparts, 0); - abcgrace = 0; - h->ktrans = 0; - ktempo = 0; - h->gchordon = 1; - h->drumon = 0; - global_songstart = h->tracktime; - abc_MIDI_beat(h, ""); // reset beat array - abc_MIDI_beatstring(h, ""); // reset beatstring - strcpy(h->gchord, ""); // reset gchord string - abcnolegato = 1; // reset legato switch - } - break; - case NOWHERE: - if( p[0] != '\0' && p[1] == ':' ) { - abcstate = INHEAD; - abc_set_parts(&abcparts, 0); - strcpy(h->gchord, ""); - if( h->drumon && h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); - if( h->tpc && !h->gchordon ) abc_add_gchord_syncs(h, h->tpc, h->tracktime); - h->gchordon = 1; - h->drumon = 0; - } - else - break; - case INHEAD: - if( !strncmp(p,"L:",2) ) { - sscanf(p+2," %d / %d", &snotelen, &snotediv); - break; - } - if( !strncmp(p,"M:",2) ) { - abc_M_field(p+2, &mnotelen, &mnotediv); - break; - } - if( !strncmp(p,"P:",2) ) { - abc_set_parts(&abcparts, p+2); - break; - } - if( !strncmp(p,"Q:",2) ) { - abctempo = abc_extract_tempo(p+2,0); - ktempo = 1; - if( h->track ) { - // make h->tracktime start of a new age... - abc_add_partbreak(h, h->track, h->tracktime); - abc_add_tempo_event(h, h->track, h->tracktime, abctempo); - } - if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abctempo; - break; - } - if( !strncmp(p,"T:",2) ) { - char buf[200]; - if( strchr(p,'%') ) *strchr(p,'%') = '\0'; - for( t=strlen(p)-1; isspace(p[t]); t-- ) - p[t]='\0'; - for( t=2; isspace(p[t]); t++ ) ; -#ifdef NEWMIKMOD - if( of->songname ) - strcpy(buf,of->songname); - else - strcpy(buf,""); -#else - strcpy(buf,m_szNames[0]); -#endif - if( strlen(buf) + strlen(p+t) > 199 ) p[t+199-strlen(buf)] = '\0'; // chop it of - if( strlen(buf) ) strcat(buf," "); // add a space - strcat(buf, p+t); -#ifdef NEWMIKMOD - of->songname = DupStr(of->allochandle, buf, strlen(buf)); -#else - if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of - strcpy(m_szNames[0], buf); -#endif - break; - } - if( !strncmp(p,"R:",2) ) { - for( p+=2; isspace(*p); p++ ) ; - if( !strncmp(p,"hornpipe",8) && (isspace(p[8]) || p[8]=='\0') ) abchornpipe = 1; - else abchornpipe = 0; - break; - } - if( !strncmp(p,"V:",2) ) { - for( t=2; p[t]==' '; t++ ) ; - h->tp = abc_locate_track(h, p+t, 0); - abcvol = h->tp->volume; - abcnolegato = !h->tp->legato; - if( !abcnolegato ) abcnoslurs = 0; - break; - } - if( !strncmp(p,"K:",2) ) { - abcstate = INBODY; - abckey = ABC_Key(p+2); - sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature - p = abc_skip_word(p+2); - h->ktrans = abc_transpose(p); - *p = '%'; // force skip rest of line - if( snotelen == 0 ) { // calculate default notelen from meter M: - if( mnotediv == 0 ) mnotediv = mnotelen = 1; // do'nt get nuked - snotelen = 100 * mnotelen / mnotediv; - if( snotelen > 74 ) - snotediv = 8; - else - snotediv = 16; - snotelen = 1; - } - abceffect = none; - abceffoper = 0; - if( !(snotelen == 1 && snotediv == 8) ) abchornpipe = 0; // no matter what they said at R: - brokenrithm = 0; - global_part = ' '; - abcgrace = 0; - abcnoslurs = abcnolegato; - abcto = 0; - h->tpc = NULL; // reset chord track - tpd = NULL; // reset drone track - h->tpr = NULL; // reset drum track - if( !strlen(h->gchord) ) abc_metric_gchord(h, mnotelen, mnotediv); - h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv); - if( abctempo && !ktempo ) { // did not set tempo in this songpiece so reset to abcrate - abctempo = 0; - global_tempo_factor = 2; - global_tempo_divider = 1; - if( h->track ) { - // make h->tracktime start of a new age... - abc_add_partbreak(h, h->track, h->tracktime); - abc_add_tempo_event(h, h->track, h->tracktime, abcrate); - } - if( m_nDefaultTempo == 0 ) m_nDefaultTempo = abcrate; - } - abc_init_partpat(partpat); - partpat[26][0] = abc_patno(h, h->tracktime); - partpat[26][1] = 0; - abc_globalslide(h, h->tracktime, 2); // reset all volumeslides - break; - } - if( !strlen(p) ) - abcstate = INBETWEEN; - break; - case INSKIPFORQUOTE: - while( (ch=*p++) && (ch != '"') ) - ; - if( !ch ) break; - abcstate = INBODY; - // fall through - case INBODY: - if( !strlen(p) && h->track ) { // end of this song - abcstate = h->pickrandom? INSKIPFORX: INBETWEEN; - // last but not least shut off all pending events - abc_recalculate_tracktime(h); - for( ttp=h->track; ttp; ttp=ttp->next ) - abc_add_noteoff(h,ttp,h->tracktime); - abc_add_partbreak(h, h->track, h->tracktime); - t = abc_patno(h, h->tracktime); - if( abc_pattracktime(h, h->tracktime) % abcticks(64 * h->speed) ) t++; - if( global_part == ' ' ) { - partpat[26][1] = t; - if( abcparts ) { - for( t=0; t<26; t++ ) - if( partpat[t][0] < partpat[t][1] ) break; - if( t == 26 ) { - abc_message("parts (%s) set but not used", abcparts); - abc_set_parts(&abcparts, 0); // forget the parts array - } - } - } - else - partpat[global_part - 'A'][1] = t; - if( !abcparts ) abc_song_to_parts(h, &abcparts, partpat); - orderlen = abc_partpat_to_orderlist(partpat, abcparts, h, &orderlist, orderlen); - } - if( !strncmp(p,"V:",2) ) { - for( t=2; p[t]==' '; t++ ) ; - h->tp = abc_locate_track(h, p+t, 0); - sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature - abcgrace = 0; - brokenrithm = 0; - h->tracktime = abc_tracktime(h->tp); - bartime = h->tracktime; // it is not friendly to break voices in the middle of a track... - abcnolegato = !h->tp->legato; - if( !abcnolegato ) abcnoslurs = 0; - *p = '%'; // make me skip the rest of the line.... - } - if( !strncmp(p,"K:",2) ) { - abckey = ABC_Key(p+2); - sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature - p = abc_skip_word(p+2); - h->ktrans = abc_transpose(p); - *p = '%'; // make me skip the rest of the line.... - } - if( !strncmp(p,"L:",2) ) { - sscanf(p+2," %d / %d", &snotelen, &snotediv); - *p = '%'; // make me skip the rest of the line.... - } - if( !strncmp(p,"M:",2) ) { - abc_M_field(p+2, &mnotelen, &mnotediv); - h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv); - *p = '%'; // make me skip the rest of the line.... - } - if( !strncmp(p,"Q:",2) ) { - abctempo = abc_extract_tempo(p+2,ch0=='\\'); - if( !h->track ) { - h->tp = abc_check_track(h, h->track); - h->tp->vno = 0; // mark reuseable (temporarely, until first notes come up) - } - abc_add_tempo_event(h, h->track, h->tracktime, abctempo); - *p = '%'; // make me skip the rest of the line.... - } - if( !strncmp(p,"T:",2) ) { - char buf[200]; - if( strchr(p,'%') ) *strchr(p,'%') = '\0'; - for( t=strlen(p)-1; isspace(p[t]); t-- ) - p[t]='\0'; - for( t=2; isspace(p[t]); t++ ) ; -#ifdef NEWMIKMOD - if( of->songname ) - strcpy(buf,of->songname); - else - strcpy(buf,""); -#else - strcpy(buf,m_szNames[0]); -#endif - if( strlen(buf) + strlen(p+t) > 198 ) p[t+198-strlen(buf)] = '\0'; // chop it of - if( strlen(buf) ) strcat(buf," "); // add a space - strcat(buf, p+t); -#ifdef NEWMIKMOD - of->songname = DupStr(of->allochandle, buf, strlen(buf)); -#else - if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of - strcpy(m_szNames[0], buf); -#endif - *p = '%'; // make me skip the rest of the line.... - } - break; - } - if( !strncmp(p,"m:",2) ) { - if( abcstate != INSKIPFORX ) { - char *pm; - pm = abc_continuated(h, mmstack[mmsp], p); - abc_new_macro(h, pm+2); - if( pm != p ) { - free(pm); - if( h->tp ) abcnolegato = !h->tp->legato; - if( !abcnolegato ) abcnoslurs = 0; - } - } - *p = '%'; // skip rest of line - } - if( !strncmp(p,"U:",2) ) { - abc_new_umacro(h, p+2); - *p = '%'; // skip rest of line - } - if( !strncmp(p,"w:",2) ) { // inline lyrics - *p = '%'; // skip rest of line - } - if( !strncmp(p,"W:",2) ) { // lyrics at end of song body - *p = '%'; // skip rest of line - } - if( !strncmp(p,"d:",2) ) { // oldstyle decorations - abc_message("warning: old style decorations not handled\n%s", p); - *p = '%'; // skip rest of line - } - if( !strncmp(p,"s:",2) ) { // newstyle decorations (symbols) - abc_message("warning: new style decorations not handled\n%s", p); - *p = '%'; // skip rest of line - } - if( !strncmp(p,"I:",2) && abcstate != INSKIPFORX ) { // handle like oldstyle '%%command' lines - p[0]= '%'; - p[1]= '%'; - } - if( !strncmp(p,"%%",2) ) { - for( p+=2; *p && isspace(*p); p++ ) ; - if( !strncmp(p,"abc-include",11) && isspace(p[11]) ) { - for( t=12; isspace(p[t]); t++ ) ; - if( p[t] ) { - mmsp++; - if( mmsp == MAXABCINCLUDES ) { - mmsp--; - abc_message("failure: too many abc-include's, %s", &p[t]); - } else { - mmstack[mmsp] = mmfopen(&p[t], "r"); - if( !mmstack[mmsp] ) { - mmsp--; - abc_message("failure: abc-include file %s not found", &p[t]); - } - } - } - else abc_message("failure: abc-include missing file name, %s", p); - } - if( !strncmp(p,"MIDI",4) && (p[4]=='=' || isspace(p[4])) && abcstate != INSKIPFORX ) { - for( p+=5; *p && isspace(*p); p++ ) ; - if( *p == '=' ) - for( p+=1; *p && isspace(*p); p++ ) ; - abc_MIDI_command(h,p,'%'); - if( h->tp ) abcnolegato = !h->tp->legato; - if( !abcnolegato ) abcnoslurs = 0; - } - if(*p) *p = '%'; // skip rest of line - } - if( abcstate == INBODY ) { - if( *p == 'P' && p[1] == ':' ) { // a line with a part indication - if( abcparts != NULL ) { - // make h->tracktime start of a new age... - if( !h->track ) { - h->tp = abc_check_track(h, h->track); - h->tp->vno = 0; // mark reuseable (temporarely, until first notes come up) - } - h->tracktime = h->track? abc_tracktime(h->track): 0; // global parts are voice independent - abc_add_partbreak(h, h->track, h->tracktime); - t = abc_patno(h, h->tracktime); - if( global_part == ' ' ) { - partpat[26][1] = t; - if( abcparts ) { - for( t=0; t<26; t++ ) - if( partpat[t][0] < partpat[t][1] ) break; - if( t == 26 ) { - abc_message("parts (%s) set but not used", abcparts); - abc_set_parts(&abcparts, 0); // forget the parts array - } - } - } - else - partpat[global_part - 'A'][1] = t; - // give every new coming abcevent the desired part indication - while( p[2]==' ' || p[2]=='.' ) p++; // skip blancs and dots - if( isupper(p[2]) ) - global_part = p[2]; - else - global_part = ' '; - if( global_part == ' ' ) - partpat[26][0] = t; - else - partpat[global_part - 'A'][0] = t; - } - *p = '%'; // make me skip the rest of the line.... - } - if( h->droneon && !tpd ) { - tpd = h->track; - if( tpd ) { - tpd = abc_locate_track(h, tpd->v, DRONEPOS1); - tpd->instr = h->dronegm; - abc_add_dronenote(h, tpd, h->tracktime, h->dronepitch[0], h->dronevol[0]); - tpd = abc_locate_track(h, tpd->v, DRONEPOS2); - tpd->instr = h->dronegm; - abc_add_dronenote(h, tpd, h->tracktime, h->dronepitch[1], h->dronevol[1]); - } - } - if( tpd && !h->droneon ) { - tpd = abc_locate_track(h, tpd->v, DRONEPOS1); - abc_add_noteoff(h, tpd, h->tracktime); - tpd = abc_locate_track(h, tpd->v, DRONEPOS2); - abc_add_noteoff(h, tpd, h->tracktime); - tpd = NULL; - } - if( h->drumon && !h->tpr ) { - h->tpr = h->track; - if( h->tpr ) abc_add_drum_sync(h, h->tpr, h->tracktime); // don't start drumming from the beginning of time! - } - if( h->tpr && !h->drumon ) h->tpr = NULL; - if( *p != '%' ) { // skip uninteresting lines - // plough thru the songline gathering mos.... - ch0 = ' '; - pp = 0; - while( (ch = *p++) ) { - if( isalpha(ch) && *p != ':' ) { // maybe a macro - for( mp=h->umacro; mp; mp=mp->next ) { - if( ch == mp->name[0] ) { - pp = p; - p = mp->subst; - ch = *p++; - break; - } - } - } - switch(ch) { - case '%': - abcto = 0; - while( *p ) p++; - break; - case '[': // chord follows or some inline field - abcto = 0; - if( *p=='|' ) break; // [| a thick-thin bar line, loop around and let case '|' handle it - if( !strncmp(p,"V:",2) ) { // inline voice change - for( t=2; isspace(p[t]); t++ ) ; - h->tp = abc_locate_track(h, p+t, 0); - for( ; *p && *p != ']'; p++ ) ; - abcgrace = 0; - brokenrithm = 0; - sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature - h->tracktime = abc_tracktime(h->tp); - bartime = h->tracktime; // it is not wise to break voices in the middle of a track... - abcvol = h->tp->volume; - abcnolegato = !h->tp->legato; - if( !abcnolegato ) abcnoslurs = 0; - break; - } - if( !strncmp(p,"K:",2) ) { - abckey = ABC_Key(p+2); - sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature - p = abc_skip_word(p+2); - h->ktrans = abc_transpose(p); - for( ; *p && *p != ']'; p++ ) ; - break; - } - if( !strncmp(p,"M:",2) ) { - abc_M_field(p+2, &mnotelen, &mnotediv); - for( ; *p && *p != ']'; p++ ) ; - h->barticks = notelen_notediv_to_ticks(h->speed, mnotelen, mnotediv); - break; - } - if( !strncmp(p,"P:",2) ) { // a [P:X] field inline - if( abcparts != NULL ) { - // make h->tracktime start of a new age... - abc_add_partbreak(h, h->track, h->tracktime); - t = abc_patno(h, h->tracktime); - if( global_part == ' ' ) - partpat[26][1] = t; - else - partpat[global_part - 'A'][1] = t; - // give every new coming abcevent the desired part indication - while( isspace(p[2]) || p[2]=='.' ) p++; // skip blancs and dots - if( isupper(p[2]) ) - global_part = p[2]; - else - global_part = ' '; - if( global_part == ' ' ) - partpat[26][0] = t; - else - partpat[global_part - 'A'][0] = t; - } - for( ; *p && *p != ']'; p++ ) ; - break; - } - if( !strncmp(p,"Q:",2) ) { - abctempo = abc_extract_tempo(p+2,1); - for( ; *p && *p != ']'; p++ ) ; - abc_add_tempo_event(h, h->track, h->tracktime, abctempo); - break; - } - if( !strncmp(p,"I:",2) ) { // interpret some of the possibilitys - for( p += 2; isspace(*p); p++ ) ; - if( !strncmp(p,"MIDI",4) && (p[4]=='=' || isspace(p[4])) ) { // interpret some of the possibilitys - for( p += 4; isspace(*p); p++ ) ; - if( *p == '=' ) - for( p += 1; isspace(*p); p++ ) ; - abc_MIDI_command(h, p, ']'); - if( h->tp ) abcnolegato = !h->tp->legato; - if( !abcnolegato ) abcnoslurs = 0; - } - for( ; *p && *p != ']'; p++ ) ; // skip rest of inline field - } - if( *p && p[1] == ':' ) { // some other kind of inline field - for( ; *p && *p != ']'; p++ ) ; - break; - } - if( *p && strchr("abcdefgABCDEFG^_=",*p) ) { - int cnl[8],cnd[8],vnl,nl0=0,nd0=0; // for chords with notes of varying length - abcchord = 0; - vnl = 0; - h->tp = abc_check_track(h, h->tp); - abc_track_clear_tiedvpos(h); - abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv)); - while( (ch=*p++) && (ch != ']') ) { - h->tp = abc_locate_track(h, h->tp->v, abcchord? abcchord+DRONEPOS2: 0); - p += abc_add_noteon(h, ch, p, h->tracktime, barsig, abcbeatvol, abceffect, abceffoper); - p += abc_notelen(p, ¬elen, ¬ediv); - if( *p == '-' ) { - p++; - if( h->tp->tail->flg != 1 ) - h->tp->tienote = h->tp->tail; - } - if( abcchord<8 ) { - cnl[abcchord] = notelen; - cnd[abcchord] = notediv; - } - if( abcchord==0 ) { - cnotelen = notelen; - cnotediv = notediv; - nl0 = notelen; - nd0 = notediv; - } - else { - if( cnotelen != notelen || cnotediv != notediv ) { - vnl = 1; - // update to longest duration - if( cnotelen * notediv < notelen * cnotediv ) { - cnotelen = notelen; - cnotediv = notediv; - abc_track_untie_short_chordnotes(h); - } - if( cnotelen * notediv > notelen * cnotediv ) { - if( h->tp->tienote ) { - abc_message("short notes in chord can not be tied:\n%s", h->line); - h->tp->tienote = 0; // short chord notes cannot be tied... - } - } - // update to shortest duration - if( nl0 * notediv > notelen * nd0 ) { - nl0 = notelen; - nd0 = notediv; - } - } - } - abcchord++; - } - p += abc_notelen(p, ¬elen, ¬ediv); - if( (ch = *p) == '-' ) p++; // tied chord... - if( abcarpeggio ) { // update starttime in the noteon events... - thistime = notelen_notediv_to_ticks(h->speed, nl0*notelen*snotelen, nd0*notediv*snotediv)/abcchord; - if( thistime > abcticks(h->speed) ) thistime = abcticks(h->speed); - for( nl0=1; nl0tp = abc_locate_track(h, h->tp->v, nl0+DRONEPOS2); - h->tp->tail->tracktick = h->tracktime + thistime * nl0; - } - } - notelen *= cnotelen; - notediv *= cnotediv; - tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr); - while( isspace(*p) ) p++; // allow spacing in broken rithm notation - p += abc_brokenrithm(p, ¬elen, ¬ediv, &brokenrithm, abchornpipe); - thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv); - if( abcfermata ) { - thistime <<= 1; - abcfermata = 0; - } - if( thistime > abcgrace ) { - thistime -= abcgrace; - abcgrace = 0; - } - else { - abcgrace -= thistime; - thistime = abcticks(h->speed); - abcgrace += abcticks(h->speed); - } - h->tracktime += thistime; - while( abcchord>0 ) { - abcchord--; - h->tp = abc_locate_track(h, h->tp->v, abcchord? abcchord+DRONEPOS2: 0); - if( vnl && (abcchord < 8) && (cnl[abcchord] != cnotelen || cnd[abcchord] != cnotediv) ) { - abc_add_noteoff(h, h->tp, - h->tracktime - thistime - + (thistime * cnl[abcchord] * cnotediv)/(cnd[abcchord] * cnotelen) ); - } - else { - if( ch=='-' && h->tp->tail->flg != 1 ) - h->tp->tienote = h->tp->tail; // copy noteon event to tienote in track - if( thistime > abcticks(h->speed) ) - abc_add_noteoff(h, h->tp, h->tracktime - abcnoslurs); - else - abc_add_noteoff(h, h->tp, h->tracktime); - } - } - if( h->gchordon && (h->tp == h->tpc) ) - abc_add_gchord(h, h->tracktime, bartime); - if( h->drumon && (h->tp == h->tpr) ) - abc_add_drum(h, h->tracktime, bartime); - abcarpeggio = 0; - if( abceffoper != 255 ) abceffect = none; - break; - } - if( isdigit(*p) ) { // different endings in repeats [i,j,n-r,s,... - h->tp = abc_check_track(h, h->tp); - abc_add_partbreak(h, h->tp, h->tracktime); - p += abc_getnumber(p, ¬elen); - abc_add_variant_start(h, h->tp, h->tracktime, notelen); - while( *p==',' || *p=='-' ) { - if( *p==',' ) { - p++; - p += abc_getnumber(p, ¬elen); - abc_add_variant_choise(h->tp, notelen); - } - else { - p++; - p += abc_getnumber(p, ¬ediv); - while( notelen < notediv ) { - notelen++; - abc_add_variant_choise(h->tp, notelen); - } - } - } - break; - } - // collect the notes in the chord - break; - case '(': // slurs follow or some tuplet (duplet, triplet etc.) - abcto = 0; - if( isdigit(*p) ) { - p += abc_getnumber(p,&tupletp); - tupletr = tupletp; // ABC draft 2.0 (4.13): if r is not given it defaults to p - switch( tupletp ) { // ABC draft 2.0 (4.13): q defaults depending on p and time signature - case 2: case 4: case 8: - tupletq = 3; - break; - case 3: case 6: - tupletq = 2; - break; - default: - if( snotediv == 8 ) - tupletq = 3; - else - tupletq = 2; - break; - } - if( *p==':' ) { - p++; - if( isdigit(*p) ) p += abc_getnumber(p,&tupletq); - if( *p==':' ) { - p++; - if( isdigit(*p) ) p += abc_getnumber(p,&tupletr); - } - } - } - else - abcnoslurs=0; - break; - case ')': // end of slurs - abcto = 0; - abcnoslurs = abcnolegato; - break; - case '{': // grace notes follow - abcto = 0; - h->tp = abc_check_track(h, h->tp); - abc_track_clear_tiedvpos(h); - abcgrace = 0; - abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv)); - while( (ch=*p++) && (ch != '}') ) { - p += abc_add_noteon(h, ch, p, h->tracktime+abcgrace, barsig, abcbeatvol, none, 0); - p += abc_notelen(p, ¬elen, ¬ediv); - if( *p=='-' ) { - p++; - if( h->tp->tail->flg != 1 ) - h->tp->tienote = h->tp->tail; - } - notediv *= 4; // grace notes factor 4 shorter (1/8 => 1/32) - abcgrace += notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv); - abc_add_noteoff(h, h->tp, h->tracktime + abcgrace); - } - h->tracktime += abcgrace; - abc_add_sync(h, h->tp, h->tracktime); - if( h->gchordon && (h->tp == h->tpc) ) - abc_add_gchord(h, h->tracktime, bartime); - if( h->drumon && (h->tp == h->tpr) ) - abc_add_drum(h, h->tracktime, bartime); - break; - case '|': // bar symbols - abcto = 0; - if( h->gchordon && h->tp && (h->tp == h->tpc) ) - abc_add_gchord(h, h->tracktime, bartime); - if( h->drumon && (h->tp == h->tpr) ) - abc_add_drum(h, h->tracktime, bartime); - sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature - bartime = h->tracktime; - if( h->tp && h->tp->vpos ) h->tp = abc_locate_track(h, h->tp->v, 0); // reset from voice overlay - if( isdigit(*p) ) { // different endings in repeats |i,j,n-r,s,... - h->tp = abc_check_track(h, h->tp); - abc_add_partbreak(h, h->tp, h->tracktime); - p += abc_getnumber(p, ¬elen); - abc_add_variant_start(h, h->tp, h->tracktime, notelen); - while( *p==',' || *p=='-' ) { - if( *p==',' ) { - p++; - p += abc_getnumber(p, ¬elen); - abc_add_variant_choise(h->tp, notelen); - } - else { - p++; - p += abc_getnumber(p, ¬ediv); - while( notelen < notediv ) { - notelen++; - abc_add_variant_choise(h->tp, notelen); - } - } - } - break; - } - if( *p==':' ) { // repeat start - p++; - h->tp = abc_check_track(h, h->tp); - abc_add_partbreak(h, h->tp, h->tracktime); - abc_add_setloop(h, h->tp, h->tracktime); - } - break; - case '&': // voice overlay - abcto = 0; - h->tracktime = bartime; - h->tp = abc_check_track(h, h->tp); - t = h->tp->vpos; - h->tp = abc_locate_track(h, h->tp->v, t? t+1: DRONEPOS2+1); - break; - case ']': // staff break, end of song - abcto = 0; - break; - case ':': // repeat jump - abcto = 0; - h->tp = abc_check_track(h, h->tp); - j = (h->tp->slidevol == -2)? jumpfade: jumpnormal; - abc_add_setjumploop(h, h->tp, h->tracktime, j); - abc_add_partbreak(h, h->tp, h->tracktime); - if( *p==':' ) { // repeat start without intermediate bar symbol - p++; - abc_add_setloop(h, h->tp, h->tracktime); - } - break; - case '"': // chord notation - if( !strchr("_^<>@", *p) && !isdigit(*p) ) { // if it's not a annotation string - h->tp = abc_check_track(h, h->tp); - if( !h->tpc ) h->tpc = abc_locate_track(h, h->tp->v, 0); - if( h->tp == h->tpc ) abc_add_chord(p, h, h->tpc, h->tracktime); // only do chords for one voice - } - abcto = 0; - while( (ch=*p++) && (ch != '"') ) { - if( !strnicmp(p,"fade",4) && h->track && h->track->slidevol > -2 ) - abc_globalslide(h, h->tracktime, -2); // set volumeslide to fade away... - if( !strnicmp(p,"to coda",7) ) { - h->tp = abc_check_track(h, h->tp); - abc_add_partbreak(h, h->tp, h->tracktime); - abc_add_tocoda(h, h->tp, h->tracktime); - p+=7; - abcto = -1; - } - else - if( !isspace(*p) ) abcto = 0; - if( !strnicmp(p,"to",2) && (isspace(p[2]) || p[2] == '"') ) abcto = 1; - } - if( !ch ) abcstate = INSKIPFORQUOTE; - break; - case '\\': // skip the rest of this line, should be the end of the line anyway - while( (ch=*p++) ) - ; - ch = '\\'; // remember for invoice tempo changes.... - break; - case '!': // line break, or deprecated old style decoration - case '+': // decorations new style - if( !strncmp(p,"coda",4) && p[4] == ch ) { - h->tp = abc_check_track(h, h->tp); - if( abcto ) { - if( abcto > 0 ) { - abc_add_partbreak(h, h->tp, h->tracktime); - abc_add_tocoda(h, h->tp, h->tracktime); - } - } - else { - abc_add_partbreak(h, h->tp, h->tracktime); - abc_add_coda(h, h->tp, h->tracktime); - } - p += 5; - abcto = 0; - break; - } - abcto = 0; - if( !strncmp(p,"arpeggio",8) && p[8] == ch ) { - abcarpeggio = 1; - p += 9; - break; - } - if( !strncmp(p,"crescendo(",10) && p[10] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_globalslide(h, h->tracktime, 1); - p += 11; - break; - } - if( !strncmp(p,"crescendo)",10) && p[10] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_globalslide(h, h->tracktime, 0); - p += 11; - break; - } - if( !strncmp(p,"<(",2) && p[2] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_globalslide(h, h->tracktime, 1); - p += 3; - break; - } - if( !strncmp(p,"<)",2) && p[2] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_globalslide(h, h->tracktime, 0); - p += 3; - break; - } - if( !strncmp(p,"dimimuendo(",11) && p[11] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_globalslide(h, h->tracktime, -1); - p += 12; - break; - } - if( !strncmp(p,"diminuendo)",11) && p[11] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_globalslide(h, h->tracktime, 0); - p += 12; - break; - } - if( !strncmp(p,">(",2) && p[2] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_globalslide(h, h->tracktime, -1); - p += 3; - break; - } - if( !strncmp(p,">)",2) && p[2] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_globalslide(h, h->tracktime, 0); - p += 3; - break; - } - if( !strncmp(p,"upbow",5) && p[5] == ch ) { - abceffect = bow; - abceffoper = 1; - p += 6; - break; - } - if( !strncmp(p,"downbow",7) && p[7] == ch ) { - abceffect = bow; - abceffoper = 0; - p += 8; - break; - } - if( !strncmp(p,"trill",5) && p[5] == ch ) { - abceffect = trill; - abceffoper = 0; - p += 6; - break; - } - if( !strncmp(p,"trill(",6) && p[6] == ch ) { - abceffect = trill; - abceffoper = 255; - p += 7; - break; - } - if( !strncmp(p,"trill)",6) && p[6] == ch ) { - abceffect = none; - abceffoper = 0; - p += 7; - break; - } - if( !strncmp(p,"accent",6) && p[6] == ch ) { - abceffect = accent; - abceffoper = 0; - p += 7; - break; - } - if( !strncmp(p,"emphasis",8) && p[8] == ch ) { - abceffect = accent; - abceffoper = 0; - p += 9; - break; - } - if( !strncmp(p,">",1) && p[1] == ch ) { - abceffect = accent; - abceffoper = 0; - p += 2; - break; - } - if( !strncmp(p,"fermata",7) && p[7] == ch ) { - abcfermata = 1; - p += 8; - break; - } - if( !strncmp(p,"fine",4) && p[4] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_add_partbreak(h, h->tp, h->tracktime); - abc_add_fine(h, h->tp, h->tracktime); - p += 5; - break; - } - if( !strncmp(p,"segno",5) && p[5] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_add_partbreak(h, h->tp, h->tracktime); - abc_add_segno(h, h->tp, h->tracktime); - p += 6; - break; - } - if( !strncmp(p,"tocoda",6) && p[6] == ch ) { - h->tp = abc_check_track(h, h->tp); - abc_add_partbreak(h, h->tp, h->tracktime); - abc_add_tocoda(h, h->tp, h->tracktime); - p += 7; - break; - } - if( !strncmp(p,"D.C.",4) && p[4] == ch ) { - h->tp = abc_check_track(h, h->tp); - j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo; - abc_add_setjumploop(h, h->tp, h->tracktime, j); - abc_add_partbreak(h, h->tp, h->tracktime); - p += 5; - break; - } - if( !strncmp(p,"D.S.",4) && p[4] == ch ) { - h->tp = abc_check_track(h, h->tp); - j = (h->tp->slidevol == -2)? jumpdsfade: jumpdasegno; - abc_add_setjumploop(h, h->tp, h->tracktime, j); - abc_add_partbreak(h, h->tp, h->tracktime); - p += 5; - break; - } - if( !strncmp(p,"dacapo",6) && p[6] == ch ) { - h->tp = abc_check_track(h, h->tp); - j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo; - abc_add_setjumploop(h, h->tp, h->tracktime, j); - abc_add_partbreak(h, h->tp, h->tracktime); - p += 7; - break; - } - if( !strncmp(p,"dacoda",6) && p[6] == ch ) { - h->tp = abc_check_track(h, h->tp); - j = (h->tp->slidevol == -2)? jumpdcfade: jumpdacapo; - abc_add_setjumploop(h, h->tp, h->tracktime, j); - abc_add_partbreak(h, h->tp, h->tracktime); - p += 7; - break; - } - if( ch == '!' ) { - for( t=0; p[t] && strchr("|[:]!",p[t])==0 && !isspace(p[t]); t++ ) ; - if( p[t] == '!' ) { // volume and other decorations, deprecated - h->tp = abc_check_track(h, h->tp); - abcvol = abc_parse_decorations(h, h->tp, p); - p = &p[t+1]; - } - } - else { - h->tp = abc_check_track(h, h->tp); - abcvol = abc_parse_decorations(h, h->tp, p); - while( (ch=*p++) && (ch != '+') ) - ; - } - break; - case '`': // back quotes are for readability - break; - case '.': // staccato marks - break; - default: // some kinda note must follow - if( strchr("abcdefgABCDEFG^_=X",ch) ) { - h->tp = abc_check_track(h, h->tp); - abc_track_clear_tiedvpos(h); - abcbeatvol = abc_beat_vol(h, abcvol, (h->tracktime - bartime)/notelen_notediv_to_ticks(h->speed,1,mnotediv)); - p += abc_add_noteon(h, ch, p, h->tracktime, barsig, abcbeatvol, abceffect, abceffoper); - if( abceffoper != 255 ) abceffect = none; - p += abc_notelen(p, ¬elen, ¬ediv); - if( *p=='-' ) { - p++; - if( h->tp->tail->flg != 1 ) - h->tp->tienote = h->tp->tail; - } - tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr); - while( isspace(*p) ) p++; // allow spacing in broken rithm notation - p += abc_brokenrithm(p, ¬elen, ¬ediv, &brokenrithm, abchornpipe); - thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv); - if( abcfermata ) { - thistime <<= 1; - abcfermata = 0; - } - if( thistime > abcgrace ) { - thistime -= abcgrace; - abcgrace = 0; - } - else { - abcgrace -= thistime; - thistime = abcticks(h->speed); - abcgrace += abcticks(h->speed); - } - h->tracktime += thistime; - if( thistime > abcticks(h->speed) ) - abc_add_noteoff(h, h->tp, h->tracktime - abcnoslurs - (( ch0 == '.')? thistime / 2: 0)); - else - abc_add_noteoff(h, h->tp, h->tracktime); - abc_add_sync(h, h->tp, h->tracktime); - if( h->gchordon && (h->tp == h->tpc) ) - abc_add_gchord(h, h->tracktime, bartime); - if( h->drumon && (h->tp == h->tpr) ) - abc_add_drum(h, h->tracktime, bartime); - abcarpeggio = 0; - break; - } - if( strchr("zx",ch) ) { - h->tp = abc_check_track(h, h->tp); - abc_track_clear_tiednote(h); - p += abc_notelen(p, ¬elen, ¬ediv); - tupletr = abc_tuplet(¬elen, ¬ediv, tupletp, tupletq, tupletr); - while( isspace(*p) ) p++; // allow spacing in broken rithm notation - p += abc_brokenrithm(p, ¬elen, ¬ediv, &brokenrithm, abchornpipe); - thistime = notelen_notediv_to_ticks(h->speed, notelen*snotelen, notediv*snotediv); - if( abcfermata ) { - thistime <<= 1; - abcfermata = 0; - } - if( thistime > abcgrace ) { - thistime -= abcgrace; - abcgrace = 0; - } - else { - abcgrace -= thistime; - thistime = abcticks(h->speed); - abcgrace += abcticks(h->speed); - } - h->tracktime += thistime; - abc_add_sync(h, h->tp, h->tracktime); - if( h->gchordon && (h->tp == h->tpc) ) - abc_add_gchord(h, h->tracktime, bartime); - if( h->drumon && (h->tp == h->tpr) ) - abc_add_drum(h, h->tracktime, bartime); - abcarpeggio = 0; - break; - } - if( strchr("Z",ch) ) { - h->tp = abc_check_track(h, h->tp); - abc_track_clear_tiednote(h); - p += abc_notelen(p, ¬elen, ¬ediv); - thistime = notelen_notediv_to_ticks(h->speed, notelen*mnotelen, notediv*mnotediv); - if( abcfermata ) { - thistime <<= 1; - abcfermata = 0; - } - if( thistime > abcgrace ) { - thistime -= abcgrace; - abcgrace = 0; - } - else { - abcgrace -= thistime; - thistime = abcticks(h->speed); - abcgrace += abcticks(h->speed); - } - h->tracktime += thistime; - sprintf(barsig, "%s%s", sig[abckey], sig[abckey]); // reset the key signature - abc_add_sync(h, h->tp, h->tracktime); - if( h->gchordon && (h->tp == h->tpc) ) - abc_add_gchord(h, h->tracktime, bartime); - if( h->drumon && (h->tp == h->tpr) ) - abc_add_drum(h, h->tracktime, bartime); - abcarpeggio = 0; - break; - } - if( isalpha(ch) && *p==':' ) { - // some unprocessed field line? - while( *p ) p++; // skip it - break; - } - break; - } - ch0 = ch; // remember previous char, can be staccato dot... - if( pp ) { // did we have a U: macro substitution? - if( !*p ) { - p = pp; - pp = 0; - } - } - } - } - } - } - if( mmsp ) mmfclose(mmstack[mmsp]); - } - ABC_CleanupMacros(h); // we dont need them anymore - if( !h->track ) { - char buf[10]; - sprintf(buf,"%d",abcxnumber); - abc_message("abc X:%s has no body", buf); - h->track = abc_check_track(h, h->track); // for sanity... - } - if( abcstate == INBODY ) { - // last but not least shut off all pending events - abc_recalculate_tracktime(h); - for( ttp=h->track; ttp; ttp=ttp->next ) - abc_add_noteoff(h,ttp,h->tracktime); - abc_add_partbreak(h, h->track, h->tracktime); - t = abc_patno(h, h->tracktime); - if( abc_pattracktime(h, h->tracktime) % abcticks(64 * h->speed) ) t++; - if( global_part == ' ' ) { - partpat[26][1] = t; - if( abcparts ) { - for( t=0; t<26; t++ ) - if( partpat[t][0] < partpat[t][1] ) break; - if( t == 26 ) { - abc_message("parts (%s) set but not used", abcparts); - abc_set_parts(&abcparts, 0); // forget the parts array - } - } - } - else - partpat[global_part - 'A'][1] = t; - if( !abcparts ) abc_song_to_parts(h, &abcparts, partpat); - orderlen = abc_partpat_to_orderlist(partpat, abcparts, h, &orderlist, orderlen); - } - abc_synchronise_tracks(h); // distribute all control events - abc_recalculate_tracktime(h); -/* - - abctrack: - tracktick long - note byte - octave byte - instrument byte - effects byte - - tick = tracktick modulo speed - row = (tracktick div speed) modulo 64 - pat = (tracktick div speed) div 64 - ord = calculated - -*/ - if( (p=getenv(ABC_ENV_DUMPTRACKS)) ) { - printf("P:%s\n",abcparts); - for( t=0; t<26; t++ ) - if( partpat[t][1] >= partpat[t][0] ) - printf(" %c ",t+'A'); - if( partpat[26][1] >= partpat[26][0] ) - printf("All"); - printf("\n"); - for( t=0; t<27; t++ ) - if( partpat[t][1] >= partpat[t][0] ) - printf("%3d ",partpat[t][0]); - printf("\n"); - for( t=0; t<27; t++ ) - if( partpat[t][1] >= partpat[t][0] ) - printf("%3d ",partpat[t][1]); - printf("\n"); - for( t=0; (int)tmemsize = PTMEM_LAST; // Number of memory slots to reserve! - of->modtype = _mm_strdup(of->allochandle, ABC_Version); - of->numpat = 1+(modticks(h->tracktime) / h->speed / 64); - of->numpos = orderlen; - of->reppos = 0; - of->initspeed = h->speed; - of->numchn = abc_numtracks(h); - of->numtrk = of->numpat * of->numchn; - of->initvolume = 64; - of->pansep = 128; - // orderlist - if(!AllocPositions(of, orderlen)) { - avoid_reentry = 0; - return FALSE; - } - for(t=0; tpositions[t] = orderlist[t]; - _mmalloc_close(h->ho); // get rid of orderlist memory -#else - m_nType = MOD_TYPE_ABC; - numpat = 1+(modticks(h->tracktime) / h->speed / 64); - m_nDefaultSpeed = h->speed; - m_nChannels = abc_numtracks(h); - m_dwSongFlags = SONG_LINEARSLIDES; - m_nMinPeriod = 28 << 2; - m_nMaxPeriod = 1712 << 3; - // orderlist - for(t=0; t < (uint)orderlen; t++) - Order[t] = orderlist[t]; - free(orderlist); // get rid of orderlist memory -#endif -#ifdef NEWMIKMOD - // ============================== - // Load the pattern info now! - if(!AllocTracks(of)) return 0; - if(!AllocPatterns(of)) return 0; - of->ut = utrk_init(of->numchn, h->allochandle); - utrk_memory_reset(of->ut); - utrk_local_memflag(of->ut, PTMEM_PORTAMENTO, TRUE, FALSE); - ABC_ReadPatterns(of, h, of->numpat); - // load instruments after building the patterns (chan == 10 track handling) - if( !PAT_Load_Instruments(of) ) { - avoid_reentry = 0; - return FALSE; - } - // ============================================================ - // set panning positions - for(t=0; tnumchn; t++) { - of->panning[t] = PAN_LEFT+((t+2)%5)*((PAN_RIGHT - PAN_LEFT)/5); // 0x30 = std s3m val - } -#else - // ============================== - // Load the pattern info now! - if( ABC_ReadPatterns(Patterns, PatternSize, h, numpat, m_nChannels) ) { - // :^( need one more channel to handle the global events ;^b - m_nChannels++; - h->tp = abc_locate_track(h, "", 99); - abc_add_sync(h, h->tp, h->tracktime); - for( t=0; t -*/ - -/////////////////////////////////////////////////// -// -// AMF module loader -// -// There is 2 types of AMF files: -// - ASYLUM Music Format -// - Advanced Music Format(DSM) -// -/////////////////////////////////////////////////// -#include "stdafx.h" -#include "sndfile.h" - -//#define AMFLOG - -//#pragma warning(disable:4244) - -#pragma pack(1) - -typedef struct _AMFFILEHEADER -{ - UCHAR szAMF[3]; - UCHAR version; - CHAR title[32]; - UCHAR numsamples; - UCHAR numorders; - USHORT numtracks; - UCHAR numchannels; -} AMFFILEHEADER; - -typedef struct _AMFSAMPLE -{ - UCHAR type; - CHAR samplename[32]; - CHAR filename[13]; - ULONG offset; - ULONG length; - USHORT c2spd; - UCHAR volume; -} AMFSAMPLE; - - -#pragma pack() - - -#ifdef AMFLOG -extern void Log(LPCSTR, ...); -#endif - -VOID AMF_Unpack(MODCOMMAND *pPat, const BYTE *pTrack, UINT nRows, UINT nChannels) -//------------------------------------------------------------------------------- -{ - UINT lastinstr = 0; - UINT nTrkSize = bswapLE16(*(USHORT *)pTrack); - nTrkSize += (UINT)pTrack[2] <<16; - pTrack += 3; - while (nTrkSize--) - { - UINT row = pTrack[0]; - UINT cmd = pTrack[1]; - UINT arg = pTrack[2]; - if (row >= nRows) break; - MODCOMMAND *m = pPat + row * nChannels; - if (cmd < 0x7F) // note+vol - { - m->note = cmd+1; - if (!m->instr) m->instr = lastinstr; - m->volcmd = VOLCMD_VOLUME; - m->vol = arg; - } else - if (cmd == 0x7F) // duplicate row - { - signed char rdelta = (signed char)arg; - int rowsrc = (int)row + (int)rdelta; - if ((rowsrc >= 0) && (rowsrc < (int)nRows)) memcpy(m, &pPat[rowsrc*nChannels],sizeof(pPat[rowsrc*nChannels])); - } else - if (cmd == 0x80) // instrument - { - m->instr = arg+1; - lastinstr = m->instr; - } else - if (cmd == 0x83) // volume - { - m->volcmd = VOLCMD_VOLUME; - m->vol = arg; - } else - // effect - { - UINT command = cmd & 0x7F; - UINT param = arg; - switch(command) - { - // 0x01: Set Speed - case 0x01: command = CMD_SPEED; break; - // 0x02: Volume Slide - // 0x0A: Tone Porta + Vol Slide - // 0x0B: Vibrato + Vol Slide - case 0x02: command = CMD_VOLUMESLIDE; - case 0x0A: if (command == 0x0A) command = CMD_TONEPORTAVOL; - case 0x0B: if (command == 0x0B) command = CMD_VIBRATOVOL; - if (param & 0x80) param = (-(signed char)param)&0x0F; - else param = (param&0x0F)<<4; - break; - // 0x04: Porta Up/Down - case 0x04: if (param & 0x80) { command = CMD_PORTAMENTOUP; param = -(signed char)param; } - else { command = CMD_PORTAMENTODOWN; } break; - // 0x06: Tone Portamento - case 0x06: command = CMD_TONEPORTAMENTO; break; - // 0x07: Tremor - case 0x07: command = CMD_TREMOR; break; - // 0x08: Arpeggio - case 0x08: command = CMD_ARPEGGIO; break; - // 0x09: Vibrato - case 0x09: command = CMD_VIBRATO; break; - // 0x0C: Pattern Break - case 0x0C: command = CMD_PATTERNBREAK; break; - // 0x0D: Position Jump - case 0x0D: command = CMD_POSITIONJUMP; break; - // 0x0F: Retrig - case 0x0F: command = CMD_RETRIG; break; - // 0x10: Offset - case 0x10: command = CMD_OFFSET; break; - // 0x11: Fine Volume Slide - case 0x11: if (param) { command = CMD_VOLUMESLIDE; - if (param & 0x80) param = 0xF0|((-(signed char)param)&0x0F); - else param = 0x0F|((param&0x0F)<<4); - } else command = 0; break; - // 0x12: Fine Portamento - // 0x16: Extra Fine Portamento - case 0x12: - case 0x16: if (param) { int mask = (command == 0x16) ? 0xE0 : 0xF0; - command = (param & 0x80) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN; - if (param & 0x80) param = mask|((-(signed char)param)&0x0F); - else param |= mask; - } else command = 0; break; - // 0x13: Note Delay - case 0x13: command = CMD_S3MCMDEX; param = 0xD0|(param & 0x0F); break; - // 0x14: Note Cut - case 0x14: command = CMD_S3MCMDEX; param = 0xC0|(param & 0x0F); break; - // 0x15: Set Tempo - case 0x15: command = CMD_TEMPO; break; - // 0x17: Panning - case 0x17: param = (param+64)&0x7F; - if (m->command) { if (!m->volcmd) { m->volcmd = VOLCMD_PANNING; m->vol = param/2; } command = 0; } - else { command = CMD_PANNING8; } - // Unknown effects - default: command = param = 0; - } - if (command) - { - m->command = command; - m->param = param; - } - } - pTrack += 3; - } -} - - - -BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, DWORD dwMemLength) -//----------------------------------------------------------- -{ - AMFFILEHEADER *pfh = (AMFFILEHEADER *)lpStream; - DWORD dwMemPos; - - if ((!lpStream) || (dwMemLength < 2048)) return FALSE; - if ((!strncmp((LPCTSTR)lpStream, "ASYLUM Music Format V1.0", 25)) && (dwMemLength > 4096)) - { - UINT numorders, numpats, numsamples; - - dwMemPos = 32; - numpats = lpStream[dwMemPos+3]; - numorders = lpStream[dwMemPos+4]; - numsamples = 64; - dwMemPos += 6; - if ((!numpats) || (numpats > MAX_PATTERNS) || (!numorders) - || (numpats*64*32 + 294 + 37*64 >= dwMemLength)) return FALSE; - m_nType = MOD_TYPE_AMF0; - m_nChannels = 8; - m_nInstruments = 0; - m_nSamples = 31; - m_nDefaultTempo = 125; - m_nDefaultSpeed = 6; - for (UINT iOrd=0; iOrdnFineTune = MOD2XMFineTune(lpStream[dwMemPos+22]); - psmp->nVolume = lpStream[dwMemPos+23]; - psmp->nGlobalVol = 64; - if (psmp->nVolume > 0x40) psmp->nVolume = 0x40; - psmp->nVolume <<= 2; - psmp->nLength = bswapLE32(*((LPDWORD)(lpStream+dwMemPos+25))); - psmp->nLoopStart = bswapLE32(*((LPDWORD)(lpStream+dwMemPos+29))); - psmp->nLoopEnd = psmp->nLoopStart + bswapLE32(*((LPDWORD)(lpStream+dwMemPos+33))); - if ((psmp->nLoopEnd > psmp->nLoopStart) && (psmp->nLoopEnd <= psmp->nLength)) - { - psmp->uFlags = CHN_LOOP; - } else - { - psmp->nLoopStart = psmp->nLoopEnd = 0; - } - if ((psmp->nLength) && (iSmp>31)) m_nSamples = iSmp+1; - dwMemPos += 37; - } - for (UINT iPat=0; iPatnote = 0; - - if (pin[0]) - { - p->note = pin[0] + 13; - } - p->instr = pin[1]; - p->command = pin[2]; - p->param = pin[3]; - if (p->command > 0x0F) - { - #ifdef AMFLOG - Log("0x%02X.0x%02X ?", p->command, p->param); - #endif - p->command = 0; - } - ConvertModCommand(p); - pin += 4; - p++; - } - dwMemPos += 64*32; - } - // Read samples - for (UINT iData=0; iDatanLength) - { - dwMemPos += ReadSample(psmp, RS_PCM8S, (LPCSTR)(lpStream+dwMemPos), dwMemLength); - } - } - return TRUE; - } - //////////////////////////// - // DSM/AMF - USHORT *ptracks[MAX_PATTERNS]; - DWORD sampleseekpos[MAX_SAMPLES]; - - if ((pfh->szAMF[0] != 'A') || (pfh->szAMF[1] != 'M') || (pfh->szAMF[2] != 'F') - || (pfh->version < 10) || (pfh->version > 14) || (!bswapLE16(pfh->numtracks)) - || (!pfh->numorders) || (pfh->numorders > MAX_PATTERNS) - || (!pfh->numsamples) || (pfh->numsamples > MAX_SAMPLES) - || (pfh->numchannels < 4) || (pfh->numchannels > 32)) - return FALSE; - memcpy(m_szNames[0], pfh->title, 32); - dwMemPos = sizeof(AMFFILEHEADER); - m_nType = MOD_TYPE_AMF; - m_nChannels = pfh->numchannels; - m_nSamples = pfh->numsamples; - m_nInstruments = 0; - // Setup Channel Pan Positions - if (pfh->version >= 11) - { - signed char *panpos = (signed char *)(lpStream + dwMemPos); - UINT nchannels = (pfh->version >= 13) ? 32 : 16; - for (UINT i=0; i 256) { pan = 128; ChnSettings[i].dwFlags |= CHN_SURROUND; } - ChnSettings[i].nPan = pan; - } - dwMemPos += nchannels; - } else - { - for (UINT i=0; i<16; i++) - { - ChnSettings[i].nPan = (lpStream[dwMemPos+i] & 1) ? 0x30 : 0xD0; - } - dwMemPos += 16; - } - // Get Tempo/Speed - m_nDefaultTempo = 125; - m_nDefaultSpeed = 6; - if (pfh->version >= 13) - { - if (lpStream[dwMemPos] >= 32) m_nDefaultTempo = lpStream[dwMemPos]; - if (lpStream[dwMemPos+1] <= 32) m_nDefaultSpeed = lpStream[dwMemPos+1]; - dwMemPos += 2; - } - // Setup sequence list - for (UINT iOrd=0; iOrdnumorders) - { - Order[iOrd] = iOrd; - PatternSize[iOrd] = 64; - if (pfh->version >= 14) - { - PatternSize[iOrd] = bswapLE16(*(USHORT *)(lpStream+dwMemPos)); - dwMemPos += 2; - } - ptracks[iOrd] = (USHORT *)(lpStream+dwMemPos); - dwMemPos += m_nChannels * sizeof(USHORT); - } - } - if (dwMemPos + m_nSamples * (sizeof(AMFSAMPLE)+8) > dwMemLength) return TRUE; - // Read Samples - UINT maxsampleseekpos = 0; - for (UINT iIns=0; iInssamplename, 32); - memcpy(pins->name, psh->filename, 13); - pins->nLength = bswapLE32(psh->length); - pins->nC4Speed = bswapLE16(psh->c2spd); - pins->nGlobalVol = 64; - pins->nVolume = psh->volume * 4; - if (pfh->version >= 11) - { - pins->nLoopStart = bswapLE32(*(DWORD *)(lpStream+dwMemPos)); - pins->nLoopEnd = bswapLE32(*(DWORD *)(lpStream+dwMemPos+4)); - dwMemPos += 8; - } else - { - pins->nLoopStart = bswapLE16(*(WORD *)(lpStream+dwMemPos)); - pins->nLoopEnd = pins->nLength; - dwMemPos += 2; - } - sampleseekpos[iIns] = 0; - if ((psh->type) && (bswapLE32(psh->offset) < dwMemLength-1)) - { - sampleseekpos[iIns] = bswapLE32(psh->offset); - if (bswapLE32(psh->offset) > maxsampleseekpos) - maxsampleseekpos = bswapLE32(psh->offset); - if ((pins->nLoopEnd > pins->nLoopStart + 2) - && (pins->nLoopEnd <= pins->nLength)) pins->uFlags |= CHN_LOOP; - } - } - // Read Track Mapping Table - USHORT *pTrackMap = (USHORT *)(lpStream+dwMemPos); - UINT realtrackcnt = 0; - dwMemPos += pfh->numtracks * sizeof(USHORT); - for (UINT iTrkMap=0; iTrkMapnumtracks; iTrkMap++) - { - if (realtrackcnt < pTrackMap[iTrkMap]) realtrackcnt = pTrackMap[iTrkMap]; - } - // Store tracks positions - BYTE **pTrackData = new BYTE *[realtrackcnt]; - memset(pTrackData, 0, sizeof(pTrackData)); - for (UINT iTrack=0; iTracknumorders; iPat++) - { - MODCOMMAND *p = AllocatePattern(PatternSize[iPat], m_nChannels); - if (!p) break; - Patterns[iPat] = p; - for (UINT iChn=0; iChnnumtracks)) - { - UINT realtrk = bswapLE16(pTrackMap[nTrack-1]); - if (realtrk) - { - realtrk--; - if ((realtrk < realtrackcnt) && (pTrackData[realtrk])) - { - AMF_Unpack(p+iChn, pTrackData[realtrk], PatternSize[iPat], m_nChannels); - } - } - } - } - } - delete pTrackData; - // Read Sample Data - for (UINT iSeek=1; iSeek<=maxsampleseekpos; iSeek++) - { - if (dwMemPos >= dwMemLength) break; - for (UINT iSmp=0; iSmp -*/ - -////////////////////////////////////////////// -// AMS module loader // -////////////////////////////////////////////// -#include "stdafx.h" -#include "sndfile.h" - -//#pragma warning(disable:4244) - -#pragma pack(1) - -typedef struct AMSFILEHEADER -{ - char szHeader[7]; // "Extreme" // changed from CHAR - BYTE verlo, verhi; // 0x??,0x01 - BYTE chncfg; - BYTE samples; - WORD patterns; - WORD orders; - BYTE vmidi; - WORD extra; -} AMSFILEHEADER; - -typedef struct AMSSAMPLEHEADER -{ - DWORD length; - DWORD loopstart; - DWORD loopend; - BYTE finetune_and_pan; - WORD samplerate; // C-2 = 8363 - BYTE volume; // 0-127 - BYTE infobyte; -} AMSSAMPLEHEADER; - - -#pragma pack() - - - -BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength) -//----------------------------------------------------------- -{ - BYTE pkinf[MAX_SAMPLES]; - AMSFILEHEADER *pfh = (AMSFILEHEADER *)lpStream; - DWORD dwMemPos; - UINT tmp, tmp2; - - if ((!lpStream) || (dwMemLength < 1024)) return FALSE; - if ((pfh->verhi != 0x01) || (strncmp(pfh->szHeader, "Extreme", 7)) - || (!pfh->patterns) || (!pfh->orders) || (!pfh->samples) || (pfh->samples > MAX_SAMPLES) - || (pfh->patterns > MAX_PATTERNS) || (pfh->orders > MAX_ORDERS)) - { - return ReadAMS2(lpStream, dwMemLength); - } - dwMemPos = sizeof(AMSFILEHEADER) + pfh->extra; - if (dwMemPos + pfh->samples * sizeof(AMSSAMPLEHEADER) + 256 >= dwMemLength) return FALSE; - m_nType = MOD_TYPE_AMS; - m_nInstruments = 0; - m_nChannels = (pfh->chncfg & 0x1F) + 1; - m_nSamples = pfh->samples; - for (UINT nSmp=1; nSmp<=m_nSamples; nSmp++, dwMemPos += sizeof(AMSSAMPLEHEADER)) - { - AMSSAMPLEHEADER *psh = (AMSSAMPLEHEADER *)(lpStream + dwMemPos); - MODINSTRUMENT *pins = &Ins[nSmp]; - pins->nLength = psh->length; - pins->nLoopStart = psh->loopstart; - pins->nLoopEnd = psh->loopend; - pins->nGlobalVol = 64; - pins->nVolume = psh->volume << 1; - pins->nC4Speed = psh->samplerate; - pins->nPan = (psh->finetune_and_pan & 0xF0); - if (pins->nPan < 0x80) pins->nPan += 0x10; - pins->nFineTune = MOD2XMFineTune(psh->finetune_and_pan & 0x0F); - pins->uFlags = (psh->infobyte & 0x80) ? CHN_16BIT : 0; - if ((pins->nLoopEnd <= pins->nLength) && (pins->nLoopStart+4 <= pins->nLoopEnd)) pins->uFlags |= CHN_LOOP; - pkinf[nSmp] = psh->infobyte; - } - // Read Song Name - tmp = lpStream[dwMemPos++]; - if (dwMemPos + tmp + 1 >= dwMemLength) return TRUE; - tmp2 = (tmp < 32) ? tmp : 31; - if (tmp2) memcpy(m_szNames[0], lpStream+dwMemPos, tmp2); - m_szNames[0][tmp2] = 0; - dwMemPos += tmp; - // Read sample names - for (UINT sNam=1; sNam<=m_nSamples; sNam++) - { - if (dwMemPos + 32 >= dwMemLength) return TRUE; - tmp = lpStream[dwMemPos++]; - tmp2 = (tmp < 32) ? tmp : 31; - if (tmp2) memcpy(m_szNames[sNam], lpStream+dwMemPos, tmp2); - dwMemPos += tmp; - } - // Skip Channel names - for (UINT cNam=0; cNam= dwMemLength) return TRUE; - tmp = lpStream[dwMemPos++]; - dwMemPos += tmp; - } - // Read Pattern Names - m_lpszPatternNames = new char[pfh->patterns * 32]; // changed from CHAR - if (!m_lpszPatternNames) return TRUE; - m_nPatternNames = pfh->patterns; - memset(m_lpszPatternNames, 0, m_nPatternNames * 32); - for (UINT pNam=0; pNam < m_nPatternNames; pNam++) - { - if (dwMemPos + 32 >= dwMemLength) return TRUE; - tmp = lpStream[dwMemPos++]; - tmp2 = (tmp < 32) ? tmp : 31; - if (tmp2) memcpy(m_lpszPatternNames+pNam*32, lpStream+dwMemPos, tmp2); - dwMemPos += tmp; - } - // Read Song Comments - tmp = *((WORD *)(lpStream+dwMemPos)); - dwMemPos += 2; - if (dwMemPos + tmp >= dwMemLength) return TRUE; - if (tmp) - { - m_lpszSongComments = new char[tmp+1]; // changed from CHAR - if (!m_lpszSongComments) return TRUE; - memset(m_lpszSongComments, 0, tmp+1); - memcpy(m_lpszSongComments, lpStream + dwMemPos, tmp); - dwMemPos += tmp; - } - // Read Order List - for (UINT iOrd=0; iOrdorders; iOrd++, dwMemPos += 2) - { - UINT n = *((WORD *)(lpStream+dwMemPos)); - Order[iOrd] = (BYTE)n; - } - // Read Patterns - for (UINT iPat=0; iPatpatterns; iPat++) - { - if (dwMemPos + 4 >= dwMemLength) return TRUE; - UINT len = *((DWORD *)(lpStream + dwMemPos)); - dwMemPos += 4; - if ((len >= dwMemLength) || (dwMemPos + len > dwMemLength)) return TRUE; - PatternSize[iPat] = 64; - MODCOMMAND *m = AllocatePattern(PatternSize[iPat], m_nChannels); - if (!m) return TRUE; - Patterns[iPat] = m; - const BYTE *p = lpStream + dwMemPos; - UINT row = 0, i = 0; - while ((row < PatternSize[iPat]) && (i+2 < len)) - { - BYTE b0 = p[i++]; - BYTE b1 = p[i++]; - BYTE b2 = 0; - UINT ch = b0 & 0x3F; - // Note+Instr - if (!(b0 & 0x40)) - { - b2 = p[i++]; - if (ch < m_nChannels) - { - if (b1 & 0x7F) m[ch].note = (b1 & 0x7F) + 25; - m[ch].instr = b2; - } - if (b1 & 0x80) - { - b0 |= 0x40; - b1 = p[i++]; - } - } - // Effect - if (b0 & 0x40) - { - anothercommand: - if (b1 & 0x40) - { - if (ch < m_nChannels) - { - m[ch].volcmd = VOLCMD_VOLUME; - m[ch].vol = b1 & 0x3F; - } - } else - { - b2 = p[i++]; - if (ch < m_nChannels) - { - UINT cmd = b1 & 0x3F; - if (cmd == 0x0C) - { - m[ch].volcmd = VOLCMD_VOLUME; - m[ch].vol = b2 >> 1; - } else - if (cmd == 0x0E) - { - if (!m[ch].command) - { - UINT command = CMD_S3MCMDEX; - UINT param = b2; - switch(param & 0xF0) - { - case 0x00: if (param & 0x08) { param &= 0x07; param |= 0x90; } else {command=param=0;} break; - case 0x10: command = CMD_PORTAMENTOUP; param |= 0xF0; break; - case 0x20: command = CMD_PORTAMENTODOWN; param |= 0xF0; break; - case 0x30: param = (param & 0x0F) | 0x10; break; - case 0x40: param = (param & 0x0F) | 0x30; break; - case 0x50: param = (param & 0x0F) | 0x20; break; - case 0x60: param = (param & 0x0F) | 0xB0; break; - case 0x70: param = (param & 0x0F) | 0x40; break; - case 0x90: command = CMD_RETRIG; param &= 0x0F; break; - case 0xA0: if (param & 0x0F) { command = CMD_VOLUMESLIDE; param = (param << 4) | 0x0F; } else command=param=0; break; - case 0xB0: if (param & 0x0F) { command = CMD_VOLUMESLIDE; param |= 0xF0; } else command=param=0; break; - } - m[ch].command = command; - m[ch].param = param; - } - } else - { - m[ch].command = cmd; - m[ch].param = b2; - ConvertModCommand(&m[ch]); - } - } - } - if (b1 & 0x80) - { - b1 = p[i++]; - if (i <= len) goto anothercommand; - } - } - if (b0 & 0x80) - { - row++; - m += m_nChannels; - } - } - dwMemPos += len; - } - // Read Samples - for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) if (Ins[iSmp].nLength) - { - if (dwMemPos >= dwMemLength - 9) return TRUE; - UINT flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_AMS16 : RS_AMS8; - dwMemPos += ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); - } - return TRUE; -} - - -///////////////////////////////////////////////////////////////////// -// AMS 2.2 loader - -#pragma pack(1) - -typedef struct AMS2FILEHEADER -{ - DWORD dwHdr1; // AMShdr - WORD wHdr2; - BYTE b1A; // 0x1A - BYTE titlelen; // 30-bytes max - CHAR szTitle[30]; // [titlelen] -} AMS2FILEHEADER; - -typedef struct AMS2SONGHEADER -{ - WORD version; - BYTE instruments; - WORD patterns; - WORD orders; - WORD bpm; - BYTE speed; - BYTE channels; - BYTE commands; - BYTE rows; - WORD flags; -} AMS2SONGHEADER; - -typedef struct AMS2INSTRUMENT -{ - BYTE samples; - BYTE notemap[120]; -} AMS2INSTRUMENT; - -typedef struct AMS2ENVELOPE -{ - BYTE speed; - BYTE sustain; - BYTE loopbegin; - BYTE loopend; - BYTE points; - BYTE info[3]; -} AMS2ENVELOPE; - -typedef struct AMS2SAMPLE -{ - DWORD length; - DWORD loopstart; - DWORD loopend; - WORD frequency; - BYTE finetune; - WORD c4speed; - CHAR transpose; - BYTE volume; - BYTE flags; -} AMS2SAMPLE; - - -#pragma pack() - - -BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength) -//------------------------------------------------------------ -{ - AMS2FILEHEADER *pfh = (AMS2FILEHEADER *)lpStream; - AMS2SONGHEADER *psh; - DWORD dwMemPos; - BYTE smpmap[16]; - BYTE packedsamples[MAX_SAMPLES]; - - if ((pfh->dwHdr1 != 0x68534D41) || (pfh->wHdr2 != 0x7264) - || (pfh->b1A != 0x1A) || (pfh->titlelen > 30)) return FALSE; - dwMemPos = pfh->titlelen + 8; - psh = (AMS2SONGHEADER *)(lpStream + dwMemPos); - if (((psh->version & 0xFF00) != 0x0200) || (!psh->instruments) - || (psh->instruments > MAX_INSTRUMENTS) || (!psh->patterns) || (!psh->orders)) return FALSE; - dwMemPos += sizeof(AMS2SONGHEADER); - if (pfh->titlelen) - { - memcpy(m_szNames, pfh->szTitle, pfh->titlelen); - m_szNames[0][pfh->titlelen] = 0; - } - m_nType = MOD_TYPE_AMS; - m_nChannels = 32; - m_nDefaultTempo = psh->bpm >> 8; - m_nDefaultSpeed = psh->speed; - m_nInstruments = psh->instruments; - m_nSamples = 0; - if (psh->flags & 0x40) m_dwSongFlags |= SONG_LINEARSLIDES; - for (UINT nIns=1; nIns<=m_nInstruments; nIns++) - { - UINT insnamelen = lpStream[dwMemPos]; - CHAR *pinsname = (CHAR *)(lpStream+dwMemPos+1); - dwMemPos += insnamelen + 1; - AMS2INSTRUMENT *pins = (AMS2INSTRUMENT *)(lpStream + dwMemPos); - dwMemPos += sizeof(AMS2INSTRUMENT); - if (dwMemPos + 1024 >= dwMemLength) return TRUE; - AMS2ENVELOPE *volenv, *panenv, *pitchenv; - volenv = (AMS2ENVELOPE *)(lpStream+dwMemPos); - dwMemPos += 5 + volenv->points*3; - panenv = (AMS2ENVELOPE *)(lpStream+dwMemPos); - dwMemPos += 5 + panenv->points*3; - pitchenv = (AMS2ENVELOPE *)(lpStream+dwMemPos); - dwMemPos += 5 + pitchenv->points*3; - INSTRUMENTHEADER *penv = new INSTRUMENTHEADER; - if (!penv) return TRUE; - memset(smpmap, 0, sizeof(smpmap)); - memset(penv, 0, sizeof(INSTRUMENTHEADER)); - for (UINT ismpmap=0; ismpmapsamples; ismpmap++) - { - if ((ismpmap >= 16) || (m_nSamples+1 >= MAX_SAMPLES)) break; - m_nSamples++; - smpmap[ismpmap] = m_nSamples; - } - penv->nGlobalVol = 64; - penv->nPan = 128; - penv->nPPC = 60; - Headers[nIns] = penv; - if (insnamelen) - { - if (insnamelen > 31) insnamelen = 31; - memcpy(penv->name, pinsname, insnamelen); - penv->name[insnamelen] = 0; - } - for (UINT inotemap=0; inotemap<120; inotemap++) - { - penv->NoteMap[inotemap] = inotemap+1; - penv->Keyboard[inotemap] = smpmap[pins->notemap[inotemap] & 0x0F]; - } - // Volume Envelope - { - UINT pos = 0; - penv->nVolEnv = (volenv->points > 16) ? 16 : volenv->points; - penv->nVolSustainBegin = penv->nVolSustainEnd = volenv->sustain; - penv->nVolLoopStart = volenv->loopbegin; - penv->nVolLoopEnd = volenv->loopend; - for (UINT i=0; inVolEnv; i++) - { - penv->VolEnv[i] = (BYTE)((volenv->info[i*3+2] & 0x7F) >> 1); - pos += volenv->info[i*3] + ((volenv->info[i*3+1] & 1) << 8); - penv->VolPoints[i] = (WORD)pos; - } - } - penv->nFadeOut = (((lpStream[dwMemPos+2] & 0x0F) << 8) | (lpStream[dwMemPos+1])) << 3; - UINT envflags = lpStream[dwMemPos+3]; - if (envflags & 0x01) penv->dwFlags |= ENV_VOLLOOP; - if (envflags & 0x02) penv->dwFlags |= ENV_VOLSUSTAIN; - if (envflags & 0x04) penv->dwFlags |= ENV_VOLUME; - dwMemPos += 5; - // Read Samples - for (UINT ismp=0; ismpsamples; ismp++) - { - MODINSTRUMENT *psmp = ((ismp < 16) && (smpmap[ismp])) ? &Ins[smpmap[ismp]] : NULL; - UINT smpnamelen = lpStream[dwMemPos]; - if ((psmp) && (smpnamelen) && (smpnamelen <= 22)) - { - memcpy(m_szNames[smpmap[ismp]], lpStream+dwMemPos+1, smpnamelen); - } - dwMemPos += smpnamelen + 1; - if (psmp) - { - AMS2SAMPLE *pams = (AMS2SAMPLE *)(lpStream+dwMemPos); - psmp->nGlobalVol = 64; - psmp->nPan = 128; - psmp->nLength = pams->length; - psmp->nLoopStart = pams->loopstart; - psmp->nLoopEnd = pams->loopend; - psmp->nC4Speed = pams->c4speed; - psmp->RelativeTone = pams->transpose; - psmp->nVolume = pams->volume / 2; - packedsamples[smpmap[ismp]] = pams->flags; - if (pams->flags & 0x04) psmp->uFlags |= CHN_16BIT; - if (pams->flags & 0x08) psmp->uFlags |= CHN_LOOP; - if (pams->flags & 0x10) psmp->uFlags |= CHN_PINGPONGLOOP; - } - dwMemPos += sizeof(AMS2SAMPLE); - } - } - if (dwMemPos + 256 >= dwMemLength) return TRUE; - // Comments - { - UINT composernamelen = lpStream[dwMemPos]; - if (composernamelen) - { - m_lpszSongComments = new char[composernamelen+1]; // changed from CHAR - if (m_lpszSongComments) - { - memcpy(m_lpszSongComments, lpStream+dwMemPos+1, composernamelen); - m_lpszSongComments[composernamelen] = 0; - } - } - dwMemPos += composernamelen + 1; - // channel names - for (UINT i=0; i<32; i++) - { - UINT chnnamlen = lpStream[dwMemPos]; - if ((chnnamlen) && (chnnamlen < MAX_CHANNELNAME)) - { - memcpy(ChnSettings[i].szName, lpStream+dwMemPos+1, chnnamlen); - } - dwMemPos += chnnamlen + 1; - if (dwMemPos + chnnamlen + 256 >= dwMemLength) return TRUE; - } - // packed comments (ignored) - UINT songtextlen = *((LPDWORD)(lpStream+dwMemPos)); - dwMemPos += songtextlen; - if (dwMemPos + 256 >= dwMemLength) return TRUE; - } - // Order List - { - for (UINT i=0; i= dwMemLength) return TRUE; - if (i < psh->orders) - { - Order[i] = lpStream[dwMemPos]; - dwMemPos += 2; - } - } - } - // Pattern Data - for (UINT ipat=0; ipatpatterns; ipat++) - { - if (dwMemPos+8 >= dwMemLength) return TRUE; - UINT packedlen = *((LPDWORD)(lpStream+dwMemPos)); - UINT numrows = 1 + (UINT)(lpStream[dwMemPos+4]); - //UINT patchn = 1 + (UINT)(lpStream[dwMemPos+5] & 0x1F); - //UINT patcmds = 1 + (UINT)(lpStream[dwMemPos+5] >> 5); - UINT patnamlen = lpStream[dwMemPos+6]; - dwMemPos += 4; - if ((ipat < MAX_PATTERNS) && (packedlen < dwMemLength-dwMemPos) && (numrows >= 8)) - { - if ((patnamlen) && (patnamlen < MAX_PATTERNNAME)) - { - char s[MAX_PATTERNNAME]; // changed from CHAR - memcpy(s, lpStream+dwMemPos+3, patnamlen); - s[patnamlen] = 0; - SetPatternName(ipat, s); - } - PatternSize[ipat] = numrows; - Patterns[ipat] = AllocatePattern(numrows, m_nChannels); - if (!Patterns[ipat]) return TRUE; - // Unpack Pattern Data - LPCBYTE psrc = lpStream + dwMemPos; - UINT pos = 3 + patnamlen; - UINT row = 0; - while ((pos < packedlen) && (row < numrows)) - { - MODCOMMAND *m = Patterns[ipat] + row * m_nChannels; - UINT byte1 = psrc[pos++]; - UINT ch = byte1 & 0x1F; - // Read Note + Instr - if (!(byte1 & 0x40)) - { - UINT byte2 = psrc[pos++]; - UINT note = byte2 & 0x7F; - if (note) m[ch].note = (note > 1) ? (note-1) : 0xFF; - m[ch].instr = psrc[pos++]; - // Read Effect - while (byte2 & 0x80) - { - byte2 = psrc[pos++]; - if (byte2 & 0x40) - { - m[ch].volcmd = VOLCMD_VOLUME; - m[ch].vol = byte2 & 0x3F; - } else - { - UINT command = byte2 & 0x3F; - UINT param = psrc[pos++]; - if (command == 0x0C) - { - m[ch].volcmd = VOLCMD_VOLUME; - m[ch].vol = param / 2; - } else - if (command < 0x10) - { - m[ch].command = command; - m[ch].param = param; - ConvertModCommand(&m[ch]); - } else - { - // TODO: AMS effects - } - } - } - } - if (byte1 & 0x80) row++; - } - } - dwMemPos += packedlen; - } - // Read Samples - for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) if (Ins[iSmp].nLength) - { - if (dwMemPos >= dwMemLength - 9) return TRUE; - UINT flags; - if (packedsamples[iSmp] & 0x03) - { - flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_AMS16 : RS_AMS8; - } else - { - flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S; - } - dwMemPos += ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); - } - return TRUE; -} - - -///////////////////////////////////////////////////////////////////// -// AMS Sample unpacking - -void AMSUnpack(const char *psrc, UINT inputlen, char *pdest, UINT dmax, char packcharacter) -{ - UINT tmplen = dmax; - signed char *amstmp = new signed char[tmplen]; - - if (!amstmp) return; - // Unpack Loop - { - signed char *p = amstmp; - UINT i=0, j=0; - while ((i < inputlen) && (j < tmplen)) - { - signed char ch = psrc[i++]; - if (ch == packcharacter) - { - BYTE ch2 = psrc[i++]; - if (ch2) - { - ch = psrc[i++]; - while (ch2--) - { - p[j++] = ch; - if (j >= tmplen) break; - } - } else p[j++] = packcharacter; - } else p[j++] = ch; - } - } - // Bit Unpack Loop - { - signed char *p = amstmp; - UINT bitcount = 0x80, dh; - UINT k=0; - for (UINT i=0; i> ((dh+8-count) & 7)) & 0xFF; - bitcount = ((bitcount|(bitcount<<8)) >> 1) & 0xFF; - pdest[k++] |= bl; - if (k >= dmax) - { - k = 0; - dh++; - } - } - bitcount = ((bitcount|(bitcount<<8)) >> dh) & 0xFF; - } - } - // Delta Unpack - { - signed char old = 0; - for (UINT i=0; i, - * Adam Goode (endian and char fixes for PPC) -*/ - -/////////////////////////////////////////////////////////////// -// -// DigiBooster Pro Module Loader (*.dbm) -// -// Note: this loader doesn't handle multiple songs -// -/////////////////////////////////////////////////////////////// - -#include "stdafx.h" -#include "sndfile.h" - -//#pragma warning(disable:4244) - -#define DBM_FILE_MAGIC 0x304d4244 -#define DBM_ID_NAME 0x454d414e -#define DBM_NAMELEN 0x2c000000 -#define DBM_ID_INFO 0x4f464e49 -#define DBM_INFOLEN 0x0a000000 -#define DBM_ID_SONG 0x474e4f53 -#define DBM_ID_INST 0x54534e49 -#define DBM_ID_VENV 0x564e4556 -#define DBM_ID_PATT 0x54544150 -#define DBM_ID_SMPL 0x4c504d53 - -#pragma pack(1) - -typedef struct DBMFILEHEADER -{ - DWORD dbm_id; // "DBM0" = 0x304d4244 - WORD trkver; // Tracker version: 02.15 - WORD reserved; - DWORD name_id; // "NAME" = 0x454d414e - DWORD name_len; // name length: always 44 - CHAR songname[44]; - DWORD info_id; // "INFO" = 0x4f464e49 - DWORD info_len; // 0x0a000000 - WORD instruments; - WORD samples; - WORD songs; - WORD patterns; - WORD channels; - DWORD song_id; // "SONG" = 0x474e4f53 - DWORD song_len; - CHAR songname2[44]; - WORD orders; -// WORD orderlist[0]; // orderlist[orders] in words -} DBMFILEHEADER; - -typedef struct DBMINSTRUMENT -{ - CHAR name[30]; - WORD sampleno; - WORD volume; - DWORD finetune; - DWORD loopstart; - DWORD looplen; - WORD panning; - WORD flags; -} DBMINSTRUMENT; - -typedef struct DBMENVELOPE -{ - WORD instrument; - BYTE flags; - BYTE numpoints; - BYTE sustain1; - BYTE loopbegin; - BYTE loopend; - BYTE sustain2; - WORD volenv[2*32]; -} DBMENVELOPE; - -typedef struct DBMPATTERN -{ - WORD rows; - DWORD packedsize; - BYTE patterndata[2]; // [packedsize] -} DBMPATTERN; - -typedef struct DBMSAMPLE -{ - DWORD flags; - DWORD samplesize; - BYTE sampledata[2]; // [samplesize] -} DBMSAMPLE; - -#pragma pack() - - -BOOL CSoundFile::ReadDBM(const BYTE *lpStream, DWORD dwMemLength) -//--------------------------------------------------------------- -{ - DBMFILEHEADER *pfh = (DBMFILEHEADER *)lpStream; - DWORD dwMemPos; - UINT nOrders, nSamples, nInstruments, nPatterns; - - if ((!lpStream) || (dwMemLength <= sizeof(DBMFILEHEADER)) || (!pfh->channels) - || (pfh->dbm_id != DBM_FILE_MAGIC) || (!pfh->songs) || (pfh->song_id != DBM_ID_SONG) - || (pfh->name_id != DBM_ID_NAME) || (pfh->name_len != DBM_NAMELEN) - || (pfh->info_id != DBM_ID_INFO) || (pfh->info_len != DBM_INFOLEN)) return FALSE; - dwMemPos = sizeof(DBMFILEHEADER); - nOrders = bswapBE16(pfh->orders); - if (dwMemPos + 2 * nOrders + 8*3 >= dwMemLength) return FALSE; - nInstruments = bswapBE16(pfh->instruments); - nSamples = bswapBE16(pfh->samples); - nPatterns = bswapBE16(pfh->patterns); - m_nType = MOD_TYPE_DBM; - m_nChannels = bswapBE16(pfh->channels); - if (m_nChannels < 4) m_nChannels = 4; - if (m_nChannels > 64) m_nChannels = 64; - memcpy(m_szNames[0], (pfh->songname[0]) ? pfh->songname : pfh->songname2, 32); - m_szNames[0][31] = 0; - for (UINT iOrd=0; iOrd < nOrders; iOrd++) - { - Order[iOrd] = lpStream[dwMemPos+iOrd*2+1]; - if (iOrd >= MAX_ORDERS-2) break; - } - dwMemPos += 2*nOrders; - while (dwMemPos + 10 < dwMemLength) - { - DWORD chunk_id = ((LPDWORD)(lpStream+dwMemPos))[0]; - DWORD chunk_size = bswapBE32(((LPDWORD)(lpStream+dwMemPos))[1]); - DWORD chunk_pos; - - dwMemPos += 8; - chunk_pos = dwMemPos; - if ((dwMemPos + chunk_size > dwMemLength) || (chunk_size > dwMemLength)) break; - dwMemPos += chunk_size; - // Instruments - if (chunk_id == DBM_ID_INST) - { - if (nInstruments >= MAX_INSTRUMENTS) nInstruments = MAX_INSTRUMENTS-1; - for (UINT iIns=0; iIns dwMemPos) break; - if ((penv = new INSTRUMENTHEADER) == NULL) break; - pih = (DBMINSTRUMENT *)(lpStream+chunk_pos); - nsmp = bswapBE16(pih->sampleno); - psmp = ((nsmp) && (nsmp < MAX_SAMPLES)) ? &Ins[nsmp] : NULL; - memset(penv, 0, sizeof(INSTRUMENTHEADER)); - memcpy(penv->name, pih->name, 30); - if (psmp) - { - memcpy(m_szNames[nsmp], pih->name, 30); - m_szNames[nsmp][30] = 0; - } - Headers[iIns+1] = penv; - penv->nFadeOut = 1024; // ??? - penv->nGlobalVol = 64; - penv->nPan = bswapBE16(pih->panning); - if ((penv->nPan) && (penv->nPan < 256)) - penv->dwFlags = ENV_SETPANNING; - else - penv->nPan = 128; - penv->nPPC = 5*12; - for (UINT i=0; i<120; i++) - { - penv->Keyboard[i] = nsmp; - penv->NoteMap[i] = i+1; - } - // Sample Info - if (psmp) - { - DWORD sflags = bswapBE16(pih->flags); - psmp->nVolume = bswapBE16(pih->volume) * 4; - if ((!psmp->nVolume) || (psmp->nVolume > 256)) psmp->nVolume = 256; - psmp->nGlobalVol = 64; - psmp->nC4Speed = bswapBE32(pih->finetune); - int f2t = FrequencyToTranspose(psmp->nC4Speed); - psmp->RelativeTone = f2t >> 7; - psmp->nFineTune = f2t & 0x7F; - if ((pih->looplen) && (sflags & 3)) - { - psmp->nLoopStart = bswapBE32(pih->loopstart); - psmp->nLoopEnd = psmp->nLoopStart + bswapBE32(pih->looplen); - psmp->uFlags |= CHN_LOOP; - psmp->uFlags &= ~CHN_PINGPONGLOOP; - if (sflags & 2) psmp->uFlags |= CHN_PINGPONGLOOP; - } - } - chunk_pos += sizeof(DBMINSTRUMENT); - m_nInstruments = iIns+1; - } - } else - // Volume Envelopes - if (chunk_id == DBM_ID_VENV) - { - UINT nEnvelopes = lpStream[chunk_pos+1]; - - chunk_pos += 2; - for (UINT iEnv=0; iEnv dwMemPos) break; - peh = (DBMENVELOPE *)(lpStream+chunk_pos); - nins = bswapBE16(peh->instrument); - if ((nins) && (nins < MAX_INSTRUMENTS) && (Headers[nins]) && (peh->numpoints)) - { - INSTRUMENTHEADER *penv = Headers[nins]; - - if (peh->flags & 1) penv->dwFlags |= ENV_VOLUME; - if (peh->flags & 2) penv->dwFlags |= ENV_VOLSUSTAIN; - if (peh->flags & 4) penv->dwFlags |= ENV_VOLLOOP; - penv->nVolEnv = peh->numpoints + 1; - if (penv->nVolEnv > MAX_ENVPOINTS) penv->nVolEnv = MAX_ENVPOINTS; - penv->nVolLoopStart = peh->loopbegin; - penv->nVolLoopEnd = peh->loopend; - penv->nVolSustainBegin = penv->nVolSustainEnd = peh->sustain1; - for (UINT i=0; inVolEnv; i++) - { - penv->VolPoints[i] = bswapBE16(peh->volenv[i*2]); - penv->VolEnv[i] = (BYTE)bswapBE16(peh->volenv[i*2+1]); - } - } - chunk_pos += sizeof(DBMENVELOPE); - } - } else - // Packed Pattern Data - if (chunk_id == DBM_ID_PATT) - { - if (nPatterns > MAX_PATTERNS) nPatterns = MAX_PATTERNS; - for (UINT iPat=0; iPat dwMemPos) break; - pph = (DBMPATTERN *)(lpStream+chunk_pos); - pksize = bswapBE32(pph->packedsize); - if ((chunk_pos + pksize + 6 > dwMemPos) || (pksize > dwMemPos)) break; - nRows = bswapBE16(pph->rows); - if ((nRows >= 4) && (nRows <= 256)) - { - MODCOMMAND *m = AllocatePattern(nRows, m_nChannels); - if (m) - { - LPBYTE pkdata = (LPBYTE)&pph->patterndata; - UINT row = 0; - UINT i = 0; - - PatternSize[iPat] = nRows; - Patterns[iPat] = m; - while ((i+3> 4)*12) + (note & 0x0F) + 13; - } - m[ch].note = note; - } - if (b & 0x02) m[ch].instr = pkdata[i++]; - if (b & 0x3C) - { - UINT cmd1 = 0xFF, param1 = 0, cmd2 = 0xFF, param2 = 0; - if (b & 0x04) cmd1 = (UINT)pkdata[i++]; - if (b & 0x08) param1 = pkdata[i++]; - if (b & 0x10) cmd2 = (UINT)pkdata[i++]; - if (b & 0x20) param2 = pkdata[i++]; - if (cmd1 == 0x0C) - { - m[ch].volcmd = VOLCMD_VOLUME; - m[ch].vol = param1; - cmd1 = 0xFF; - } else - if (cmd2 == 0x0C) - { - m[ch].volcmd = VOLCMD_VOLUME; - m[ch].vol = param2; - cmd2 = 0xFF; - } - if ((cmd1 > 0x13) || ((cmd1 >= 0x10) && (cmd2 < 0x10))) - { - cmd1 = cmd2; - param1 = param2; - cmd2 = 0xFF; - } - if (cmd1 <= 0x13) - { - m[ch].command = cmd1; - m[ch].param = param1; - ConvertModCommand(&m[ch]); - } - } - } else - { - if (b & 0x01) i++; - if (b & 0x02) i++; - if (b & 0x04) i++; - if (b & 0x08) i++; - if (b & 0x10) i++; - if (b & 0x20) i++; - } - } else - { - row++; - m += m_nChannels; - } - } - } - } - chunk_pos += 6 + pksize; - } - } else - // Reading Sample Data - if (chunk_id == DBM_ID_SMPL) - { - if (nSamples >= MAX_SAMPLES) nSamples = MAX_SAMPLES-1; - m_nSamples = nSamples; - for (UINT iSmp=1; iSmp<=nSamples; iSmp++) - { - MODINSTRUMENT *pins; - DBMSAMPLE *psh; - DWORD samplesize; - DWORD sampleflags; - - if (chunk_pos + sizeof(DBMSAMPLE) >= dwMemPos) break; - psh = (DBMSAMPLE *)(lpStream+chunk_pos); - chunk_pos += 8; - samplesize = bswapBE32(psh->samplesize); - sampleflags = bswapBE32(psh->flags); - pins = &Ins[iSmp]; - pins->nLength = samplesize; - if (sampleflags & 2) - { - pins->uFlags |= CHN_16BIT; - samplesize <<= 1; - } - if ((chunk_pos+samplesize > dwMemPos) || (samplesize > dwMemLength)) break; - if (sampleflags & 3) - { - ReadSample(pins, (pins->uFlags & CHN_16BIT) ? RS_PCM16M : RS_PCM8S, - (LPSTR)(psh->sampledata), samplesize); - } - chunk_pos += samplesize; - } - } - } - return TRUE; -} - diff --git a/src/modplug/load_dmf.cpp b/src/modplug/load_dmf.cpp deleted file mode 100644 index 606dd6a9..00000000 --- a/src/modplug/load_dmf.cpp +++ /dev/null @@ -1,606 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque -*/ - -/////////////////////////////////////////////////////// -// DMF DELUSION DIGITAL MUSIC FILEFORMAT (X-Tracker) // -/////////////////////////////////////////////////////// -#include "stdafx.h" -#include "sndfile.h" - -//#define DMFLOG - -//#pragma warning(disable:4244) - -#pragma pack(1) - -typedef struct DMFHEADER -{ - DWORD id; // "DDMF" = 0x464d4444 - BYTE version; // 4 - CHAR trackername[8]; // "XTRACKER" - CHAR songname[30]; - CHAR composer[20]; - BYTE date[3]; -} DMFHEADER; - -typedef struct DMFINFO -{ - DWORD id; // "INFO" - DWORD infosize; -} DMFINFO; - -typedef struct DMFSEQU -{ - DWORD id; // "SEQU" - DWORD seqsize; - WORD loopstart; - WORD loopend; - WORD sequ[2]; -} DMFSEQU; - -typedef struct DMFPATT -{ - DWORD id; // "PATT" - DWORD patsize; - WORD numpat; // 1-1024 - BYTE tracks; - BYTE firstpatinfo; -} DMFPATT; - -typedef struct DMFTRACK -{ - BYTE tracks; - BYTE beat; // [hi|lo] -> hi=ticks per beat, lo=beats per measure - WORD ticks; // max 512 - DWORD jmpsize; -} DMFTRACK; - -typedef struct DMFSMPI -{ - DWORD id; - DWORD size; - BYTE samples; -} DMFSMPI; - -typedef struct DMFSAMPLE -{ - DWORD len; - DWORD loopstart; - DWORD loopend; - WORD c3speed; - BYTE volume; - BYTE flags; -} DMFSAMPLE; - -#pragma pack() - - -#ifdef DMFLOG -extern void Log(LPCSTR s, ...); -#endif - - -BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength) -//--------------------------------------------------------------- -{ - DMFHEADER *pfh = (DMFHEADER *)lpStream; - DMFINFO *psi; - DMFSEQU *sequ; - DWORD dwMemPos; - BYTE infobyte[32]; - BYTE smplflags[MAX_SAMPLES]; - - if ((!lpStream) || (dwMemLength < 1024)) return FALSE; - if ((pfh->id != 0x464d4444) || (!pfh->version) || (pfh->version & 0xF0)) return FALSE; - dwMemPos = 66; - memcpy(m_szNames[0], pfh->songname, 30); - m_szNames[0][30] = 0; - m_nType = MOD_TYPE_DMF; - m_nChannels = 0; -#ifdef DMFLOG - Log("DMF version %d: \"%s\": %d bytes (0x%04X)\n", pfh->version, m_szNames[0], dwMemLength, dwMemLength); -#endif - while (dwMemPos + 7 < dwMemLength) - { - DWORD id = *((LPDWORD)(lpStream+dwMemPos)); - - switch(id) - { - // "INFO" - case 0x4f464e49: - // "CMSG" - case 0x47534d43: - psi = (DMFINFO *)(lpStream+dwMemPos); - if (id == 0x47534d43) dwMemPos++; - if ((psi->infosize > dwMemLength) || (psi->infosize + dwMemPos + 8 > dwMemLength)) goto dmfexit; - if ((psi->infosize >= 8) && (!m_lpszSongComments)) - { - m_lpszSongComments = new char[psi->infosize]; // changed from CHAR - if (m_lpszSongComments) - { - for (UINT i=0; iinfosize-1; i++) - { - CHAR c = lpStream[dwMemPos+8+i]; - if ((i % 40) == 39) - m_lpszSongComments[i] = 0x0d; - else - m_lpszSongComments[i] = (c < ' ') ? ' ' : c; - } - m_lpszSongComments[psi->infosize-1] = 0; - } - } - dwMemPos += psi->infosize + 8 - 1; - break; - - // "SEQU" - case 0x55514553: - sequ = (DMFSEQU *)(lpStream+dwMemPos); - if ((sequ->seqsize >= dwMemLength) || (dwMemPos + sequ->seqsize + 12 > dwMemLength)) goto dmfexit; - { - UINT nseq = sequ->seqsize >> 1; - if (nseq >= MAX_ORDERS-1) nseq = MAX_ORDERS-1; - if (sequ->loopstart < nseq) m_nRestartPos = sequ->loopstart; - for (UINT i=0; isequ[i]; - } - dwMemPos += sequ->seqsize + 8; - break; - - // "PATT" - case 0x54544150: - if (!m_nChannels) - { - DMFPATT *patt = (DMFPATT *)(lpStream+dwMemPos); - UINT numpat; - DWORD dwPos = dwMemPos + 11; - if ((patt->patsize >= dwMemLength) || (dwMemPos + patt->patsize + 8 > dwMemLength)) goto dmfexit; - numpat = patt->numpat; - if (numpat > MAX_PATTERNS) numpat = MAX_PATTERNS; - m_nChannels = patt->tracks; - if (m_nChannels < patt->firstpatinfo) m_nChannels = patt->firstpatinfo; - if (m_nChannels > 32) m_nChannels = 32; - if (m_nChannels < 4) m_nChannels = 4; - for (UINT npat=0; npattracks, pt->ticks); - #endif - UINT tracks = pt->tracks; - if (tracks > 32) tracks = 32; - UINT ticks = pt->ticks; - if (ticks > 256) ticks = 256; - if (ticks < 16) ticks = 16; - dwPos += 8; - if ((pt->jmpsize >= dwMemLength) || (dwPos + pt->jmpsize + 4 >= dwMemLength)) break; - PatternSize[npat] = (WORD)ticks; - MODCOMMAND *m = AllocatePattern(PatternSize[npat], m_nChannels); - if (!m) goto dmfexit; - Patterns[npat] = m; - DWORD d = dwPos; - dwPos += pt->jmpsize; - UINT ttype = 1; - UINT tempo = 125; - UINT glbinfobyte = 0; - UINT pbeat = (pt->beat & 0xf0) ? pt->beat>>4 : 8; - BOOL tempochange = (pt->beat & 0xf0) ? TRUE : FALSE; - memset(infobyte, 0, sizeof(infobyte)); - for (UINT row=0; row>4; tempochange = ttype; break; - #ifdef DMFLOG - default: if (info) Log("GLB: %02X.%02X\n", info, infoval); - #endif - } - } else - { - glbinfobyte--; - } - // Parse channels - for (UINT i=0; i>2; - } - // Effect 1 - if (info & 0x08) - { - BYTE efx = lpStream[d++]; - BYTE eval = lpStream[d++]; - switch(efx) - { - // 1: Key Off - case 1: if (!cmd.note) cmd.note = 0xFE; break; - // 2: Set Loop - // 4: Sample Delay - case 4: if (eval&0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xD0; } break; - // 5: Retrig - case 5: if (eval&0xe0) { cmd.command = CMD_RETRIG; cmd.param = (eval>>5); } break; - // 6: Offset - case 6: cmd.command = CMD_OFFSET; cmd.param = eval; break; - #ifdef DMFLOG - default: Log("FX1: %02X.%02X\n", efx, eval); - #endif - } - } - // Effect 2 - if (info & 0x04) - { - BYTE efx = lpStream[d++]; - BYTE eval = lpStream[d++]; - switch(efx) - { - // 1: Finetune - case 1: if (eval&0xf0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>4)|0x20; } break; - // 2: Note Delay - case 2: if (eval&0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xD0; } break; - // 3: Arpeggio - case 3: if (eval) { cmd.command = CMD_ARPEGGIO; cmd.param = eval; } break; - // 4: Portamento Up - case 4: cmd.command = CMD_PORTAMENTOUP; cmd.param = (eval >= 0xe0) ? 0xdf : eval; break; - // 5: Portamento Down - case 5: cmd.command = CMD_PORTAMENTODOWN; cmd.param = (eval >= 0xe0) ? 0xdf : eval; break; - // 6: Tone Portamento - case 6: cmd.command = CMD_TONEPORTAMENTO; cmd.param = eval; break; - // 8: Vibrato - case 8: cmd.command = CMD_VIBRATO; cmd.param = eval; break; - // 12: Note cut - case 12: if (eval & 0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xc0; } - else if (!cmd.note) { cmd.note = 0xfe; } break; - #ifdef DMFLOG - default: Log("FX2: %02X.%02X\n", efx, eval); - #endif - } - } - // Effect 3 - if (info & 0x02) - { - BYTE efx = lpStream[d++]; - BYTE eval = lpStream[d++]; - switch(efx) - { - // 1: Vol Slide Up - case 1: if (eval == 0xff) break; - eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; - cmd.command = CMD_VOLUMESLIDE; cmd.param = eval<<4; break; - // 2: Vol Slide Down - case 2: if (eval == 0xff) break; - eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; - cmd.command = CMD_VOLUMESLIDE; cmd.param = eval; break; - // 7: Set Pan - case 7: if (!cmd.volcmd) { cmd.volcmd = VOLCMD_PANNING; cmd.vol = (eval+3)>>2; } - else { cmd.command = CMD_PANNING8; cmd.param = eval; } break; - // 8: Pan Slide Left - case 8: eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; - cmd.command = CMD_PANNINGSLIDE; cmd.param = eval<<4; break; - // 9: Pan Slide Right - case 9: eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; - cmd.command = CMD_PANNINGSLIDE; cmd.param = eval; break; - #ifdef DMFLOG - default: Log("FX3: %02X.%02X\n", efx, eval); - #endif - - } - } - // Store effect - if (i < m_nChannels) p[i] = cmd; - if (d > dwPos) - { - #ifdef DMFLOG - Log("Unexpected EOP: row=%d\n", row); - #endif - break; - } - } else - { - infobyte[i]--; - } - - // Find free channel for tempo change - if (tempochange) - { - tempochange = FALSE; - UINT speed=6, modtempo=tempo; - UINT rpm = ((ttype) && (pbeat)) ? tempo*pbeat : (tempo+1)*15; - for (speed=30; speed>1; speed--) - { - modtempo = rpm*speed/24; - if (modtempo <= 200) break; - if ((speed < 6) && (modtempo < 256)) break; - } - #ifdef DMFLOG - Log("Tempo change: ttype=%d pbeat=%d tempo=%3d -> speed=%d tempo=%d\n", - ttype, pbeat, tempo, speed, modtempo); - #endif - for (UINT ich=0; ich= 32) && (modtempo < 256)) - { - p[ich].command = CMD_TEMPO; - p[ich].param = (BYTE)modtempo; - modtempo = 0; - } else - { - break; - } - } - } - if (d >= dwPos) break; - } - #ifdef DMFLOG - Log(" %d/%d bytes remaining\n", dwPos-d, pt->jmpsize); - #endif - if (dwPos + 8 >= dwMemLength) break; - } - dwMemPos += patt->patsize + 8; - } - break; - - // "SMPI": Sample Info - case 0x49504d53: - { - DMFSMPI *pds = (DMFSMPI *)(lpStream+dwMemPos); - if (pds->size <= dwMemLength - dwMemPos) - { - DWORD dwPos = dwMemPos + 9; - m_nSamples = pds->samples; - if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1; - for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) - { - UINT namelen = lpStream[dwPos]; - smplflags[iSmp] = 0; - if (dwPos+namelen+1+sizeof(DMFSAMPLE) > dwMemPos+pds->size+8) break; - if (namelen) - { - UINT rlen = (namelen < 32) ? namelen : 31; - memcpy(m_szNames[iSmp], lpStream+dwPos+1, rlen); - m_szNames[iSmp][rlen] = 0; - } - dwPos += namelen + 1; - DMFSAMPLE *psh = (DMFSAMPLE *)(lpStream+dwPos); - MODINSTRUMENT *psmp = &Ins[iSmp]; - psmp->nLength = psh->len; - psmp->nLoopStart = psh->loopstart; - psmp->nLoopEnd = psh->loopend; - psmp->nC4Speed = psh->c3speed; - psmp->nGlobalVol = 64; - psmp->nVolume = (psh->volume) ? ((WORD)psh->volume)+1 : (WORD)256; - psmp->uFlags = (psh->flags & 2) ? CHN_16BIT : 0; - if (psmp->uFlags & CHN_16BIT) psmp->nLength >>= 1; - if (psh->flags & 1) psmp->uFlags |= CHN_LOOP; - smplflags[iSmp] = psh->flags; - dwPos += (pfh->version < 8) ? 22 : 30; - #ifdef DMFLOG - Log("SMPI %d/%d: len=%d flags=0x%02X\n", iSmp, m_nSamples, psmp->nLength, psh->flags); - #endif - } - } - dwMemPos += pds->size + 8; - } - break; - - // "SMPD": Sample Data - case 0x44504d53: - { - DWORD dwPos = dwMemPos + 8; - UINT ismpd = 0; - for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) - { - ismpd++; - DWORD pksize; - if (dwPos + 4 >= dwMemLength) - { - #ifdef DMFLOG - Log("Unexpected EOF at sample %d/%d! (pos=%d)\n", iSmp, m_nSamples, dwPos); - #endif - break; - } - pksize = *((LPDWORD)(lpStream+dwPos)); - #ifdef DMFLOG - Log("sample %d: pos=0x%X pksize=%d ", iSmp, dwPos, pksize); - Log("len=%d flags=0x%X [%08X]\n", Ins[iSmp].nLength, smplflags[ismpd], *((LPDWORD)(lpStream+dwPos+4))); - #endif - dwPos += 4; - if (pksize > dwMemLength - dwPos) - { - #ifdef DMFLOG - Log("WARNING: pksize=%d, but only %d bytes left\n", pksize, dwMemLength-dwPos); - #endif - pksize = dwMemLength - dwPos; - } - if ((pksize) && (iSmp <= m_nSamples)) - { - UINT flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S; - if (smplflags[ismpd] & 4) flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_DMF16 : RS_DMF8; - ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwPos), pksize); - } - dwPos += pksize; - } - dwMemPos = dwPos; - } - break; - - // "ENDE": end of file - case 0x45444e45: - goto dmfexit; - - // Unrecognized id, or "ENDE" field - default: - dwMemPos += 4; - break; - } - } -dmfexit: - if (!m_nChannels) - { - if (!m_nSamples) - { - m_nType = MOD_TYPE_NONE; - return FALSE; - } - m_nChannels = 4; - } - return TRUE; -} - - -/////////////////////////////////////////////////////////////////////// -// DMF Compression - -#pragma pack(1) - -typedef struct DMF_HNODE -{ - short int left, right; - BYTE value; -} DMF_HNODE; - -typedef struct DMF_HTREE -{ - LPBYTE ibuf, ibufmax; - DWORD bitbuf; - UINT bitnum; - UINT lastnode, nodecount; - DMF_HNODE nodes[256]; -} DMF_HTREE; - -#pragma pack() - - -// DMF Huffman ReadBits -BYTE DMFReadBits(DMF_HTREE *tree, UINT nbits) -//------------------------------------------- -{ - BYTE x = 0, bitv = 1; - while (nbits--) - { - if (tree->bitnum) - { - tree->bitnum--; - } else - { - tree->bitbuf = (tree->ibuf < tree->ibufmax) ? *(tree->ibuf++) : 0; - tree->bitnum = 7; - } - if (tree->bitbuf & 1) x |= bitv; - bitv <<= 1; - tree->bitbuf >>= 1; - } - return x; -} - -// -// tree: [8-bit value][12-bit index][12-bit index] = 32-bit -// - -void DMFNewNode(DMF_HTREE *tree) -//------------------------------ -{ - BYTE isleft, isright; - UINT actnode; - - actnode = tree->nodecount; - if (actnode > 255) return; - tree->nodes[actnode].value = DMFReadBits(tree, 7); - isleft = DMFReadBits(tree, 1); - isright = DMFReadBits(tree, 1); - actnode = tree->lastnode; - if (actnode > 255) return; - tree->nodecount++; - tree->lastnode = tree->nodecount; - if (isleft) - { - tree->nodes[actnode].left = tree->lastnode; - DMFNewNode(tree); - } else - { - tree->nodes[actnode].left = -1; - } - tree->lastnode = tree->nodecount; - if (isright) - { - tree->nodes[actnode].right = tree->lastnode; - DMFNewNode(tree); - } else - { - tree->nodes[actnode].right = -1; - } -} - - -int DMFUnpack(LPBYTE psample, LPBYTE ibuf, LPBYTE ibufmax, UINT maxlen) -//---------------------------------------------------------------------- -{ - DMF_HTREE tree; - UINT actnode; - BYTE value, sign, delta = 0; - - memset(&tree, 0, sizeof(tree)); - tree.ibuf = ibuf; - tree.ibufmax = ibufmax; - DMFNewNode(&tree); - value = 0; - for (UINT i=0; i 255) break; - delta = tree.nodes[actnode].value; - if ((tree.ibuf >= tree.ibufmax) && (!tree.bitnum)) break; - } while ((tree.nodes[actnode].left >= 0) && (tree.nodes[actnode].right >= 0)); - if (sign) delta ^= 0xFF; - value += delta; - psample[i] = (i) ? value : 0; - } -#ifdef DMFLOG -// Log("DMFUnpack: %d remaining bytes\n", tree.ibufmax-tree.ibuf); -#endif - return int(tree.ibuf - ibuf); -} - - diff --git a/src/modplug/load_dsm.cpp b/src/modplug/load_dsm.cpp deleted file mode 100644 index 4f51469f..00000000 --- a/src/modplug/load_dsm.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque -*/ - -////////////////////////////////////////////// -// DSIK Internal Format (DSM) module loader // -////////////////////////////////////////////// -#include "stdafx.h" -#include "sndfile.h" - -#pragma pack(1) - -#define DSMID_RIFF 0x46464952 // "RIFF" -#define DSMID_DSMF 0x464d5344 // "DSMF" -#define DSMID_SONG 0x474e4f53 // "SONG" -#define DSMID_INST 0x54534e49 // "INST" -#define DSMID_PATT 0x54544150 // "PATT" - - -typedef struct DSMNOTE -{ - BYTE note,ins,vol,cmd,inf; -} DSMNOTE; - - -typedef struct DSMINST -{ - DWORD id_INST; - DWORD inst_len; - CHAR filename[13]; - BYTE flags; - BYTE flags2; - BYTE volume; - DWORD length; - DWORD loopstart; - DWORD loopend; - DWORD reserved1; - WORD c2spd; - WORD reserved2; - CHAR samplename[28]; -} DSMINST; - - -typedef struct DSMFILEHEADER -{ - DWORD id_RIFF; // "RIFF" - DWORD riff_len; - DWORD id_DSMF; // "DSMF" - DWORD id_SONG; // "SONG" - DWORD song_len; -} DSMFILEHEADER; - - -typedef struct DSMSONG -{ - CHAR songname[28]; - WORD reserved1; - WORD flags; - DWORD reserved2; - WORD numord; - WORD numsmp; - WORD numpat; - WORD numtrk; - BYTE globalvol; - BYTE mastervol; - BYTE speed; - BYTE bpm; - BYTE panpos[16]; - BYTE orders[128]; -} DSMSONG; - -typedef struct DSMPATT -{ - DWORD id_PATT; - DWORD patt_len; - BYTE dummy1; - BYTE dummy2; -} DSMPATT; - -#pragma pack() - - -BOOL CSoundFile::ReadDSM(LPCBYTE lpStream, DWORD dwMemLength) -//----------------------------------------------------------- -{ - DSMFILEHEADER *pfh = (DSMFILEHEADER *)lpStream; - DSMSONG *psong; - DWORD dwMemPos; - UINT nPat, nSmp; - - if ((!lpStream) || (dwMemLength < 1024) || (pfh->id_RIFF != DSMID_RIFF) - || (pfh->riff_len + 8 > dwMemLength) || (pfh->riff_len < 1024) - || (pfh->id_DSMF != DSMID_DSMF) || (pfh->id_SONG != DSMID_SONG) - || (pfh->song_len > dwMemLength)) return FALSE; - psong = (DSMSONG *)(lpStream + sizeof(DSMFILEHEADER)); - dwMemPos = sizeof(DSMFILEHEADER) + pfh->song_len; - m_nType = MOD_TYPE_DSM; - m_nChannels = psong->numtrk; - if (m_nChannels < 4) m_nChannels = 4; - if (m_nChannels > 16) m_nChannels = 16; - m_nSamples = psong->numsmp; - if (m_nSamples > MAX_SAMPLES) m_nSamples = MAX_SAMPLES; - m_nDefaultSpeed = psong->speed; - m_nDefaultTempo = psong->bpm; - m_nDefaultGlobalVolume = psong->globalvol << 2; - if ((!m_nDefaultGlobalVolume) || (m_nDefaultGlobalVolume > 256)) m_nDefaultGlobalVolume = 256; - m_nSongPreAmp = psong->mastervol & 0x7F; - for (UINT iOrd=0; iOrdnumord) ? psong->orders[iOrd] : 0xFF); - } - for (UINT iPan=0; iPan<16; iPan++) - { - ChnSettings[iPan].nPan = 0x80; - if (psong->panpos[iPan] <= 0x80) - { - ChnSettings[iPan].nPan = psong->panpos[iPan] << 1; - } - } - memcpy(m_szNames[0], psong->songname, 28); - nPat = 0; - nSmp = 1; - while (dwMemPos < dwMemLength - 8) - { - DSMPATT *ppatt = (DSMPATT *)(lpStream + dwMemPos); - DSMINST *pins = (DSMINST *)(lpStream+dwMemPos); - // Reading Patterns - if (ppatt->id_PATT == DSMID_PATT) - { - dwMemPos += 8; - if (dwMemPos + ppatt->patt_len >= dwMemLength) break; - DWORD dwPos = dwMemPos; - dwMemPos += ppatt->patt_len; - MODCOMMAND *m = AllocatePattern(64, m_nChannels); - if (!m) break; - PatternSize[nPat] = 64; - Patterns[nPat] = m; - UINT row = 0; - while ((row < 64) && (dwPos + 2 <= dwMemPos)) - { - UINT flag = lpStream[dwPos++]; - if (flag) - { - UINT ch = (flag & 0x0F) % m_nChannels; - if (flag & 0x80) - { - UINT note = lpStream[dwPos++]; - if (note) - { - if (note <= 12*9) note += 12; - m[ch].note = (BYTE)note; - } - } - if (flag & 0x40) - { - m[ch].instr = lpStream[dwPos++]; - } - if (flag & 0x20) - { - m[ch].volcmd = VOLCMD_VOLUME; - m[ch].vol = lpStream[dwPos++]; - } - if (flag & 0x10) - { - UINT command = lpStream[dwPos++]; - UINT param = lpStream[dwPos++]; - switch(command) - { - // 4-bit Panning - case 0x08: - switch(param & 0xF0) - { - case 0x00: param <<= 4; break; - case 0x10: command = 0x0A; param = (param & 0x0F) << 4; break; - case 0x20: command = 0x0E; param = (param & 0x0F) | 0xA0; break; - case 0x30: command = 0x0E; param = (param & 0x0F) | 0x10; break; - case 0x40: command = 0x0E; param = (param & 0x0F) | 0x20; break; - default: command = 0; - } - break; - // Portamentos - case 0x11: - case 0x12: - command &= 0x0F; - break; - // 3D Sound (?) - case 0x13: - command = 'X' - 55; - param = 0x91; - break; - default: - // Volume + Offset (?) - command = ((command & 0xF0) == 0x20) ? 0x09 : 0; - } - m[ch].command = (BYTE)command; - m[ch].param = (BYTE)param; - if (command) ConvertModCommand(&m[ch]); - } - } else - { - m += m_nChannels; - row++; - } - } - nPat++; - } else - // Reading Samples - if ((nSmp <= m_nSamples) && (pins->id_INST == DSMID_INST)) - { - if (dwMemPos + pins->inst_len >= dwMemLength - 8) break; - DWORD dwPos = dwMemPos + sizeof(DSMINST); - dwMemPos += 8 + pins->inst_len; - memcpy(m_szNames[nSmp], pins->samplename, 28); - MODINSTRUMENT *psmp = &Ins[nSmp]; - memcpy(psmp->name, pins->filename, 13); - psmp->nGlobalVol = 64; - psmp->nC4Speed = pins->c2spd; - psmp->uFlags = (WORD)((pins->flags & 1) ? CHN_LOOP : 0); - psmp->nLength = pins->length; - psmp->nLoopStart = pins->loopstart; - psmp->nLoopEnd = pins->loopend; - psmp->nVolume = (WORD)(pins->volume << 2); - if (psmp->nVolume > 256) psmp->nVolume = 256; - UINT smptype = (pins->flags & 2) ? RS_PCM8S : RS_PCM8U; - ReadSample(psmp, smptype, (LPCSTR)(lpStream+dwPos), dwMemLength - dwPos); - nSmp++; - } else - { - break; - } - } - return TRUE; -} - diff --git a/src/modplug/load_far.cpp b/src/modplug/load_far.cpp deleted file mode 100644 index 4bc2d959..00000000 --- a/src/modplug/load_far.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque -*/ - -//////////////////////////////////////// -// Farandole (FAR) module loader // -//////////////////////////////////////// -#include "stdafx.h" -#include "sndfile.h" - -//#pragma warning(disable:4244) - -#define FARFILEMAGIC 0xFE524146 // "FAR" - -#pragma pack(1) - -typedef struct FARHEADER1 -{ - DWORD id; // file magic FAR= - CHAR songname[40]; // songname - CHAR magic2[3]; // 13,10,26 - WORD headerlen; // remaining length of header in bytes - BYTE version; // 0xD1 - BYTE onoff[16]; - BYTE edit1[9]; - BYTE speed; - BYTE panning[16]; - BYTE edit2[4]; - WORD stlen; -} FARHEADER1; - -typedef struct FARHEADER2 -{ - BYTE orders[256]; - BYTE numpat; - BYTE snglen; - BYTE loopto; - WORD patsiz[256]; -} FARHEADER2; - -typedef struct FARSAMPLE -{ - CHAR samplename[32]; - DWORD length; - BYTE finetune; - BYTE volume; - DWORD reppos; - DWORD repend; - BYTE type; - BYTE loop; -} FARSAMPLE; - -#pragma pack() - - -BOOL CSoundFile::ReadFAR(const BYTE *lpStream, DWORD dwMemLength) -//--------------------------------------------------------------- -{ - FARHEADER1 *pmh1 = (FARHEADER1 *)lpStream; - FARHEADER2 *pmh2; - DWORD dwMemPos = sizeof(FARHEADER1); - UINT headerlen; - BYTE samplemap[8]; - - if ((!lpStream) || (dwMemLength < 1024) || (bswapLE32(pmh1->id) != FARFILEMAGIC) - || (pmh1->magic2[0] != 13) || (pmh1->magic2[1] != 10) || (pmh1->magic2[2] != 26)) return FALSE; - headerlen = bswapLE16(pmh1->headerlen); - pmh1->stlen = bswapLE16( pmh1->stlen ); /* inplace byteswap -- Toad */ - if ((headerlen >= dwMemLength) || (dwMemPos + pmh1->stlen + sizeof(FARHEADER2) >= dwMemLength)) return FALSE; - // Globals - m_nType = MOD_TYPE_FAR; - m_nChannels = 16; - m_nInstruments = 0; - m_nSamples = 0; - m_nSongPreAmp = 0x20; - m_nDefaultSpeed = pmh1->speed; - m_nDefaultTempo = 80; - m_nDefaultGlobalVolume = 256; - - memcpy(m_szNames[0], pmh1->songname, 32); - // Channel Setting - for (UINT nchpan=0; nchpan<16; nchpan++) - { - ChnSettings[nchpan].dwFlags = 0; - ChnSettings[nchpan].nPan = ((pmh1->panning[nchpan] & 0x0F) << 4) + 8; - ChnSettings[nchpan].nVolume = 64; - } - // Reading comment - if (pmh1->stlen) - { - UINT szLen = pmh1->stlen; - if (szLen > dwMemLength - dwMemPos) szLen = dwMemLength - dwMemPos; - if ((m_lpszSongComments = new char[szLen + 1]) != NULL) - { - memcpy(m_lpszSongComments, lpStream+dwMemPos, szLen); - m_lpszSongComments[szLen] = 0; - } - dwMemPos += pmh1->stlen; - } - // Reading orders - pmh2 = (FARHEADER2 *)(lpStream + dwMemPos); - dwMemPos += sizeof(FARHEADER2); - if (dwMemPos >= dwMemLength) return TRUE; - for (UINT iorder=0; iordersnglen) ? pmh2->orders[iorder] : 0xFF; - } - m_nRestartPos = pmh2->loopto; - // Reading Patterns - dwMemPos += headerlen - (869 + pmh1->stlen); - if (dwMemPos >= dwMemLength) return TRUE; - - // byteswap pattern data -- Toad - UINT psfix = 0 ; - while( psfix++ < 256 ) - { - pmh2->patsiz[psfix] = bswapLE16( pmh2->patsiz[psfix] ) ; - } - // end byteswap of pattern data - - WORD *patsiz = (WORD *)pmh2->patsiz; - for (UINT ipat=0; ipat<256; ipat++) if (patsiz[ipat]) - { - UINT patlen = patsiz[ipat]; - if ((ipat >= MAX_PATTERNS) || (patsiz[ipat] < 2)) - { - dwMemPos += patlen; - continue; - } - if (dwMemPos + patlen >= dwMemLength) return TRUE; - UINT rows = (patlen - 2) >> 6; - if (!rows) - { - dwMemPos += patlen; - continue; - } - if (rows > 256) rows = 256; - if (rows < 16) rows = 16; - PatternSize[ipat] = rows; - if ((Patterns[ipat] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE; - MODCOMMAND *m = Patterns[ipat]; - UINT patbrk = lpStream[dwMemPos]; - const BYTE *p = lpStream + dwMemPos + 2; - UINT max = rows*16*4; - if (max > patlen-2) max = patlen-2; - for (UINT len=0; leninstr = ins + 1; - m->note = note + 36; - } - if (vol & 0x0F) - { - m->volcmd = VOLCMD_VOLUME; - m->vol = (vol & 0x0F) << 2; - if (m->vol <= 4) m->vol = 0; - } - switch(eff & 0xF0) - { - // 1.x: Portamento Up - case 0x10: - m->command = CMD_PORTAMENTOUP; - m->param = eff & 0x0F; - break; - // 2.x: Portamento Down - case 0x20: - m->command = CMD_PORTAMENTODOWN; - m->param = eff & 0x0F; - break; - // 3.x: Tone-Portamento - case 0x30: - m->command = CMD_TONEPORTAMENTO; - m->param = (eff & 0x0F) << 2; - break; - // 4.x: Retrigger - case 0x40: - m->command = CMD_RETRIG; - m->param = 6 / (1+(eff&0x0F)) + 1; - break; - // 5.x: Set Vibrato Depth - case 0x50: - m->command = CMD_VIBRATO; - m->param = (eff & 0x0F); - break; - // 6.x: Set Vibrato Speed - case 0x60: - m->command = CMD_VIBRATO; - m->param = (eff & 0x0F) << 4; - break; - // 7.x: Vol Slide Up - case 0x70: - m->command = CMD_VOLUMESLIDE; - m->param = (eff & 0x0F) << 4; - break; - // 8.x: Vol Slide Down - case 0x80: - m->command = CMD_VOLUMESLIDE; - m->param = (eff & 0x0F); - break; - // A.x: Port to vol - case 0xA0: - m->volcmd = VOLCMD_VOLUME; - m->vol = ((eff & 0x0F) << 2) + 4; - break; - // B.x: Set Balance - case 0xB0: - m->command = CMD_PANNING8; - m->param = (eff & 0x0F) << 4; - break; - // F.x: Set Speed - case 0xF0: - m->command = CMD_SPEED; - m->param = eff & 0x0F; - break; - default: - if ((patbrk) && (patbrk+1 == (len >> 6)) && (patbrk+1 != rows-1)) - { - m->command = CMD_PATTERNBREAK; - patbrk = 0; - } - } - } - dwMemPos += patlen; - } - // Reading samples - if (dwMemPos + 8 >= dwMemLength) return TRUE; - memcpy(samplemap, lpStream+dwMemPos, 8); - dwMemPos += 8; - MODINSTRUMENT *pins = &Ins[1]; - for (UINT ismp=0; ismp<64; ismp++, pins++) if (samplemap[ismp >> 3] & (1 << (ismp & 7))) - { - if (dwMemPos + sizeof(FARSAMPLE) > dwMemLength) return TRUE; - FARSAMPLE *pfs = (FARSAMPLE *)(lpStream + dwMemPos); - dwMemPos += sizeof(FARSAMPLE); - m_nSamples = ismp + 1; - memcpy(m_szNames[ismp+1], pfs->samplename, 32); - pfs->length = bswapLE32( pfs->length ) ; /* endian fix - Toad */ - pins->nLength = pfs->length ; - pins->nLoopStart = bswapLE32(pfs->reppos) ; - pins->nLoopEnd = bswapLE32(pfs->repend) ; - pins->nFineTune = 0; - pins->nC4Speed = 8363*2; - pins->nGlobalVol = 64; - pins->nVolume = pfs->volume << 4; - pins->uFlags = 0; - if ((pins->nLength > 3) && (dwMemPos + 4 < dwMemLength)) - { - if (pfs->type & 1) - { - pins->uFlags |= CHN_16BIT; - pins->nLength >>= 1; - pins->nLoopStart >>= 1; - pins->nLoopEnd >>= 1; - } - if ((pfs->loop & 8) && (pins->nLoopEnd > 4)) pins->uFlags |= CHN_LOOP; - ReadSample(pins, (pins->uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S, - (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos); - } - dwMemPos += pfs->length; - } - return TRUE; -} - diff --git a/src/modplug/load_j2b.cpp b/src/modplug/load_j2b.cpp deleted file mode 100644 index 2d86ce82..00000000 --- a/src/modplug/load_j2b.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque -*/ - - -/////////////////////////////////////////////////// -// -// J2B module loader -// -/////////////////////////////////////////////////// -#include "stdafx.h" -#include "sndfile.h" - diff --git a/src/modplug/load_mdl.cpp b/src/modplug/load_mdl.cpp deleted file mode 100644 index 68572b5c..00000000 --- a/src/modplug/load_mdl.cpp +++ /dev/null @@ -1,503 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque -*/ - -////////////////////////////////////////////// -// DigiTracker (MDL) module loader // -////////////////////////////////////////////// -#include "stdafx.h" -#include "sndfile.h" - -//#pragma warning(disable:4244) - -typedef struct MDLSONGHEADER -{ - DWORD id; // "DMDL" = 0x4C444D44 - BYTE version; -} MDLSONGHEADER; - - -typedef struct MDLINFOBLOCK -{ - CHAR songname[32]; - CHAR composer[20]; - WORD norders; - WORD repeatpos; - BYTE globalvol; - BYTE speed; - BYTE tempo; - BYTE channelinfo[32]; - BYTE seq[256]; -} MDLINFOBLOCK; - - -typedef struct MDLPATTERNDATA -{ - BYTE channels; - BYTE lastrow; // nrows = lastrow+1 - CHAR name[16]; - WORD data[1]; -} MDLPATTERNDATA; - - -void ConvertMDLCommand(MODCOMMAND *m, UINT eff, UINT data) -//-------------------------------------------------------- -{ - UINT command = 0, param = data; - switch(eff) - { - case 0x01: command = CMD_PORTAMENTOUP; break; - case 0x02: command = CMD_PORTAMENTODOWN; break; - case 0x03: command = CMD_TONEPORTAMENTO; break; - case 0x04: command = CMD_VIBRATO; break; - case 0x05: command = CMD_ARPEGGIO; break; - case 0x07: command = (param < 0x20) ? CMD_SPEED : CMD_TEMPO; break; - case 0x08: command = CMD_PANNING8; param <<= 1; break; - case 0x0B: command = CMD_POSITIONJUMP; break; - case 0x0C: command = CMD_GLOBALVOLUME; break; - case 0x0D: command = CMD_PATTERNBREAK; param = (data & 0x0F) + (data>>4)*10; break; - case 0x0E: - command = CMD_S3MCMDEX; - switch(data & 0xF0) - { - case 0x00: command = 0; break; // What is E0x in MDL (there is a bunch) ? - case 0x10: if (param & 0x0F) { param |= 0xF0; command = CMD_PANNINGSLIDE; } else command = 0; break; - case 0x20: if (param & 0x0F) { param = (param << 4) | 0x0F; command = CMD_PANNINGSLIDE; } else command = 0; break; - case 0x30: param = (data & 0x0F) | 0x10; break; // glissando - case 0x40: param = (data & 0x0F) | 0x30; break; // vibrato waveform - case 0x60: param = (data & 0x0F) | 0xB0; break; - case 0x70: param = (data & 0x0F) | 0x40; break; // tremolo waveform - case 0x90: command = CMD_RETRIG; param &= 0x0F; break; - case 0xA0: param = (data & 0x0F) << 4; command = CMD_GLOBALVOLSLIDE; break; - case 0xB0: param = data & 0x0F; command = CMD_GLOBALVOLSLIDE; break; - case 0xF0: param = ((data >> 8) & 0x0F) | 0xA0; break; - } - break; - case 0x0F: command = CMD_SPEED; break; - case 0x10: if ((param & 0xF0) != 0xE0) { command = CMD_VOLUMESLIDE; if ((param & 0xF0) == 0xF0) param = ((param << 4) | 0x0F); else param >>= 2; } break; - case 0x20: if ((param & 0xF0) != 0xE0) { command = CMD_VOLUMESLIDE; if ((param & 0xF0) != 0xF0) param >>= 2; } break; - case 0x30: command = CMD_RETRIG; break; - case 0x40: command = CMD_TREMOLO; break; - case 0x50: command = CMD_TREMOR; break; - case 0xEF: if (param > 0xFF) param = 0xFF; command = CMD_OFFSET; break; - } - if (command) - { - m->command = command; - m->param = param; - } -} - - -void UnpackMDLTrack(MODCOMMAND *pat, UINT nChannels, UINT nRows, UINT nTrack, const BYTE *lpTracks) -//------------------------------------------------------------------------------------------------- -{ - MODCOMMAND cmd, *m = pat; - UINT len = *((WORD *)lpTracks); - UINT pos = 0, row = 0, i; - lpTracks += 2; - for (UINT ntrk=1; ntrk> 2; - switch(b & 0x03) - { - case 0x01: - for (i=0; i<=xx; i++) - { - if (row) *m = *(m-nChannels); - m += nChannels; - row++; - if (row >= nRows) break; - } - break; - - case 0x02: - if (xx < row) *m = pat[nChannels*xx]; - m += nChannels; - row++; - break; - - case 0x03: - { - cmd.note = (xx & 0x01) ? lpTracks[pos++] : 0; - cmd.instr = (xx & 0x02) ? lpTracks[pos++] : 0; - cmd.volcmd = cmd.vol = 0; - cmd.command = cmd.param = 0; - if ((cmd.note < 120-12) && (cmd.note)) cmd.note += 12; - UINT volume = (xx & 0x04) ? lpTracks[pos++] : 0; - UINT commands = (xx & 0x08) ? lpTracks[pos++] : 0; - UINT command1 = commands & 0x0F; - UINT command2 = commands & 0xF0; - UINT param1 = (xx & 0x10) ? lpTracks[pos++] : 0; - UINT param2 = (xx & 0x20) ? lpTracks[pos++] : 0; - if ((command1 == 0x0E) && ((param1 & 0xF0) == 0xF0) && (!command2)) - { - param1 = ((param1 & 0x0F) << 8) | param2; - command1 = 0xEF; - command2 = param2 = 0; - } - if (volume) - { - cmd.volcmd = VOLCMD_VOLUME; - cmd.vol = (volume+1) >> 2; - } - ConvertMDLCommand(&cmd, command1, param1); - if ((cmd.command != CMD_SPEED) - && (cmd.command != CMD_TEMPO) - && (cmd.command != CMD_PATTERNBREAK)) - ConvertMDLCommand(&cmd, command2, param2); - *m = cmd; - m += nChannels; - row++; - } - break; - - // Empty Slots - default: - row += xx+1; - m += (xx+1)*nChannels; - if (row >= nRows) break; - } - } -} - - - -BOOL CSoundFile::ReadMDL(const BYTE *lpStream, DWORD dwMemLength) -//--------------------------------------------------------------- -{ - DWORD dwMemPos, dwPos, blocklen, dwTrackPos; - const MDLSONGHEADER *pmsh = (const MDLSONGHEADER *)lpStream; - MDLINFOBLOCK *pmib; - MDLPATTERNDATA *pmpd; - UINT i,j, norders = 0, npatterns = 0, ntracks = 0; - UINT ninstruments = 0, nsamples = 0; - WORD block; - WORD patterntracks[MAX_PATTERNS*32]; - BYTE smpinfo[MAX_SAMPLES]; - BYTE insvolenv[MAX_INSTRUMENTS]; - BYTE inspanenv[MAX_INSTRUMENTS]; - LPCBYTE pvolenv, ppanenv, ppitchenv; - UINT nvolenv, npanenv, npitchenv; - - if ((!lpStream) || (dwMemLength < 1024)) return FALSE; - if ((pmsh->id != 0x4C444D44) || ((pmsh->version & 0xF0) > 0x10)) return FALSE; - memset(patterntracks, 0, sizeof(patterntracks)); - memset(smpinfo, 0, sizeof(smpinfo)); - memset(insvolenv, 0, sizeof(insvolenv)); - memset(inspanenv, 0, sizeof(inspanenv)); - dwMemPos = 5; - dwTrackPos = 0; - pvolenv = ppanenv = ppitchenv = NULL; - nvolenv = npanenv = npitchenv = 0; - m_nSamples = m_nInstruments = 0; - while (dwMemPos+6 < dwMemLength) - { - block = *((WORD *)(lpStream+dwMemPos)); - blocklen = *((DWORD *)(lpStream+dwMemPos+2)); - dwMemPos += 6; - if (dwMemPos + blocklen > dwMemLength) - { - if (dwMemPos == 11) return FALSE; - break; - } - switch(block) - { - // IN: infoblock - case 0x4E49: - pmib = (MDLINFOBLOCK *)(lpStream+dwMemPos); - memcpy(m_szNames[0], pmib->songname, 32); - norders = pmib->norders; - if (norders > MAX_ORDERS) norders = MAX_ORDERS; - m_nRestartPos = pmib->repeatpos; - m_nDefaultGlobalVolume = pmib->globalvol; - m_nDefaultTempo = pmib->tempo; - m_nDefaultSpeed = pmib->speed; - m_nChannels = 4; - for (i=0; i<32; i++) - { - ChnSettings[i].nVolume = 64; - ChnSettings[i].nPan = (pmib->channelinfo[i] & 0x7F) << 1; - if (pmib->channelinfo[i] & 0x80) - ChnSettings[i].dwFlags |= CHN_MUTE; - else - m_nChannels = i+1; - } - for (j=0; jseq[j]; - break; - // ME: song message - case 0x454D: - if (blocklen) - { - if (m_lpszSongComments) delete m_lpszSongComments; - m_lpszSongComments = new char[blocklen]; - if (m_lpszSongComments) - { - memcpy(m_lpszSongComments, lpStream+dwMemPos, blocklen); - m_lpszSongComments[blocklen-1] = 0; - } - } - break; - // PA: Pattern Data - case 0x4150: - npatterns = lpStream[dwMemPos]; - if (npatterns > MAX_PATTERNS) npatterns = MAX_PATTERNS; - dwPos = dwMemPos + 1; - for (i=0; i= dwMemLength) break; - pmpd = (MDLPATTERNDATA *)(lpStream + dwPos); - if (pmpd->channels > 32) break; - PatternSize[i] = pmpd->lastrow+1; - if (m_nChannels < pmpd->channels) m_nChannels = pmpd->channels; - dwPos += 18 + 2*pmpd->channels; - for (j=0; jchannels; j++) - { - patterntracks[i*32+j] = pmpd->data[j]; - } - } - break; - // TR: Track Data - case 0x5254: - if (dwTrackPos) break; - ntracks = *((WORD *)(lpStream+dwMemPos)); - dwTrackPos = dwMemPos+2; - break; - // II: Instruments - case 0x4949: - ninstruments = lpStream[dwMemPos]; - dwPos = dwMemPos+1; - for (i=0; i= MAX_INSTRUMENTS) || (!nins)) break; - if (m_nInstruments < nins) m_nInstruments = nins; - if (!Headers[nins]) - { - UINT note = 12; - if ((Headers[nins] = new INSTRUMENTHEADER) == NULL) break; - INSTRUMENTHEADER *penv = Headers[nins]; - memset(penv, 0, sizeof(INSTRUMENTHEADER)); - memcpy(penv->name, lpStream+dwPos+2, 32); - penv->nGlobalVol = 64; - penv->nPPC = 5*12; - for (j=0; jNoteMap[note] = note+1; - if (ps[0] < MAX_SAMPLES) - { - int ismp = ps[0]; - penv->Keyboard[note] = ps[0]; - Ins[ismp].nVolume = ps[2]; - Ins[ismp].nPan = ps[4] << 1; - Ins[ismp].nVibType = ps[11]; - Ins[ismp].nVibSweep = ps[10]; - Ins[ismp].nVibDepth = ps[9]; - Ins[ismp].nVibRate = ps[8]; - } - penv->nFadeOut = (ps[7] << 8) | ps[6]; - if (penv->nFadeOut == 0xFFFF) penv->nFadeOut = 0; - note++; - } - // Use volume envelope ? - if (ps[3] & 0x80) - { - penv->dwFlags |= ENV_VOLUME; - insvolenv[nins] = (ps[3] & 0x3F) + 1; - } - // Use panning envelope ? - if (ps[5] & 0x80) - { - penv->dwFlags |= ENV_PANNING; - inspanenv[nins] = (ps[5] & 0x3F) + 1; - } - } - } - dwPos += 34 + 14*lpStream[dwPos+1]; - } - for (j=1; j<=m_nInstruments; j++) if (!Headers[j]) - { - Headers[j] = new INSTRUMENTHEADER; - if (Headers[j]) memset(Headers[j], 0, sizeof(INSTRUMENTHEADER)); - } - break; - // VE: Volume Envelope - case 0x4556: - if ((nvolenv = lpStream[dwMemPos]) == 0) break; - if (dwMemPos + nvolenv*32 + 1 <= dwMemLength) pvolenv = lpStream + dwMemPos + 1; - break; - // PE: Panning Envelope - case 0x4550: - if ((npanenv = lpStream[dwMemPos]) == 0) break; - if (dwMemPos + npanenv*32 + 1 <= dwMemLength) ppanenv = lpStream + dwMemPos + 1; - break; - // FE: Pitch Envelope - case 0x4546: - if ((npitchenv = lpStream[dwMemPos]) == 0) break; - if (dwMemPos + npitchenv*32 + 1 <= dwMemLength) ppitchenv = lpStream + dwMemPos + 1; - break; - // IS: Sample Infoblock - case 0x5349: - nsamples = lpStream[dwMemPos]; - dwPos = dwMemPos+1; - for (i=0; i= MAX_SAMPLES) || (!nins)) continue; - if (m_nSamples < nins) m_nSamples = nins; - MODINSTRUMENT *pins = &Ins[nins]; - memcpy(m_szNames[nins], lpStream+dwPos+1, 32); - memcpy(pins->name, lpStream+dwPos+33, 8); - pins->nC4Speed = *((DWORD *)(lpStream+dwPos+41)); - pins->nLength = *((DWORD *)(lpStream+dwPos+45)); - pins->nLoopStart = *((DWORD *)(lpStream+dwPos+49)); - pins->nLoopEnd = pins->nLoopStart + *((DWORD *)(lpStream+dwPos+53)); - if (pins->nLoopEnd > pins->nLoopStart) pins->uFlags |= CHN_LOOP; - pins->nGlobalVol = 64; - if (lpStream[dwPos+58] & 0x01) - { - pins->uFlags |= CHN_16BIT; - pins->nLength >>= 1; - pins->nLoopStart >>= 1; - pins->nLoopEnd >>= 1; - } - if (lpStream[dwPos+58] & 0x02) pins->uFlags |= CHN_PINGPONGLOOP; - smpinfo[nins] = (lpStream[dwPos+58] >> 2) & 3; - } - break; - // SA: Sample Data - case 0x4153: - dwPos = dwMemPos; - for (i=1; i<=m_nSamples; i++) if ((Ins[i].nLength) && (!Ins[i].pSample) && (smpinfo[i] != 3) && (dwPos < dwMemLength)) - { - MODINSTRUMENT *pins = &Ins[i]; - UINT flags = (pins->uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S; - if (!smpinfo[i]) - { - dwPos += ReadSample(pins, flags, (LPSTR)(lpStream+dwPos), dwMemLength - dwPos); - } else - { - DWORD dwLen = *((DWORD *)(lpStream+dwPos)); - dwPos += 4; - if ((dwPos+dwLen <= dwMemLength) && (dwLen > 4)) - { - flags = (pins->uFlags & CHN_16BIT) ? RS_MDL16 : RS_MDL8; - ReadSample(pins, flags, (LPSTR)(lpStream+dwPos), dwLen); - } - dwPos += dwLen; - } - } - break; - } - dwMemPos += blocklen; - } - // Unpack Patterns - if ((dwTrackPos) && (npatterns) && (m_nChannels) && (ntracks)) - { - for (UINT ipat=0; ipatnVolEnv = 15; - for (UINT iv=0; iv<15; iv++) - { - if (iv) vtick += pve[iv*2+1]; - penv->VolPoints[iv] = vtick; - penv->VolEnv[iv] = pve[iv*2+2]; - if (!pve[iv*2+1]) - { - penv->nVolEnv = iv+1; - break; - } - } - penv->nVolSustainBegin = penv->nVolSustainEnd = pve[31] & 0x0F; - if (pve[31] & 0x10) penv->dwFlags |= ENV_VOLSUSTAIN; - if (pve[31] & 0x20) penv->dwFlags |= ENV_VOLLOOP; - penv->nVolLoopStart = pve[32] & 0x0F; - penv->nVolLoopEnd = pve[32] >> 4; - } - } - // Setup panning envelope - if ((npanenv) && (ppanenv) && (inspanenv[iIns])) - { - LPCBYTE ppe = ppanenv; - for (UINT npe=0; npenPanEnv = 15; - for (UINT iv=0; iv<15; iv++) - { - if (iv) vtick += ppe[iv*2+1]; - penv->PanPoints[iv] = vtick; - penv->PanEnv[iv] = ppe[iv*2+2]; - if (!ppe[iv*2+1]) - { - penv->nPanEnv = iv+1; - break; - } - } - if (ppe[31] & 0x10) penv->dwFlags |= ENV_PANSUSTAIN; - if (ppe[31] & 0x20) penv->dwFlags |= ENV_PANLOOP; - penv->nPanLoopStart = ppe[32] & 0x0F; - penv->nPanLoopEnd = ppe[32] >> 4; - } - } - } - m_dwSongFlags |= SONG_LINEARSLIDES; - m_nType = MOD_TYPE_MDL; - return TRUE; -} - - -///////////////////////////////////////////////////////////////////////// -// MDL Sample Unpacking - -// MDL Huffman ReadBits compression -WORD MDLReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, CHAR n) -//----------------------------------------------------------------- -{ - WORD v = (WORD)(bitbuf & ((1 << n) - 1) ); - bitbuf >>= n; - bitnum -= n; - if (bitnum <= 24) - { - bitbuf |= (((DWORD)(*ibuf++)) << bitnum); - bitnum += 8; - } - return v; -} - - diff --git a/src/modplug/load_med.cpp b/src/modplug/load_med.cpp deleted file mode 100644 index 3f7e8280..00000000 --- a/src/modplug/load_med.cpp +++ /dev/null @@ -1,916 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque , - * Adam Goode (endian and char fixes for PPC) -*/ - -#include "stdafx.h" -#include "sndfile.h" - -//#define MED_LOG - -#ifdef MED_LOG -extern void Log(LPCSTR s, ...); -#endif - -////////////////////////////////////////////////////////// -// OctaMed MED file support (import only) - -// flags -#define MMD_FLAG_FILTERON 0x1 -#define MMD_FLAG_JUMPINGON 0x2 -#define MMD_FLAG_JUMP8TH 0x4 -#define MMD_FLAG_INSTRSATT 0x8 // instruments are attached (this is a module) -#define MMD_FLAG_VOLHEX 0x10 -#define MMD_FLAG_STSLIDE 0x20 // SoundTracker mode for slides -#define MMD_FLAG_8CHANNEL 0x40 // OctaMED 8 channel song -#define MMD_FLAG_SLOWHQ 0x80 // HQ slows playing speed (V2-V4 compatibility) -// flags2 -#define MMD_FLAG2_BMASK 0x1F -#define MMD_FLAG2_BPM 0x20 -#define MMD_FLAG2_MIX 0x80 // uses Mixing (V7+) -// flags3: -#define MMD_FLAG3_STEREO 0x1 // mixing in Stereo mode -#define MMD_FLAG3_FREEPAN 0x2 // free panning -#define MMD_FLAG3_GM 0x4 // module designed for GM/XG compatibility - - -// generic MMD tags -#define MMDTAG_END 0 -#define MMDTAG_PTR 0x80000000 // data needs relocation -#define MMDTAG_MUSTKNOW 0x40000000 // loader must fail if this isn't recognized -#define MMDTAG_MUSTWARN 0x20000000 // loader must warn if this isn't recognized - -// ExpData tags -// # of effect groups, including the global group (will -// override settings in MMDSong struct), default = 1 -#define MMDTAG_EXP_NUMFXGROUPS 1 -#define MMDTAG_TRK_NAME (MMDTAG_PTR|1) // trackinfo tags -#define MMDTAG_TRK_NAMELEN 2 // namelen includes zero term. -#define MMDTAG_TRK_FXGROUP 3 -// effectinfo tags -#define MMDTAG_FX_ECHOTYPE 1 -#define MMDTAG_FX_ECHOLEN 2 -#define MMDTAG_FX_ECHODEPTH 3 -#define MMDTAG_FX_STEREOSEP 4 -#define MMDTAG_FX_GROUPNAME (MMDTAG_PTR|5) // the Global Effects group shouldn't have name saved! -#define MMDTAG_FX_GRPNAMELEN 6 // namelen includes zero term. - -#pragma pack(1) - -typedef struct tagMEDMODULEHEADER -{ - DWORD id; // MMD1-MMD3 - DWORD modlen; // Size of file - DWORD song; // Position in file for this song - WORD psecnum; - WORD pseq; - DWORD blockarr; // Position in file for blocks - DWORD mmdflags; - DWORD smplarr; // Position in file for samples - DWORD reserved; - DWORD expdata; // Absolute offset in file for ExpData (0 if not present) - DWORD reserved2; - WORD pstate; - WORD pblock; - WORD pline; - WORD pseqnum; - WORD actplayline; - BYTE counter; - BYTE extra_songs; // # of songs - 1 -} MEDMODULEHEADER; - - -typedef struct tagMMD0SAMPLE -{ - WORD rep, replen; - BYTE midich; - BYTE midipreset; - BYTE svol; - signed char strans; -} MMD0SAMPLE; - - -// Sample header is immediately followed by sample data... -typedef struct tagMMDSAMPLEHEADER -{ - DWORD length; // length of *one* *unpacked* channel in *bytes* - WORD type; - // if non-negative - // bits 0-3 reserved for multi-octave instruments, not supported on the PC - // 0x10: 16 bit (otherwise 8 bit) - // 0x20: Stereo (otherwise mono) - // 0x40: Uses DeltaCode - // 0x80: Packed data - // -1: Synth - // -2: Hybrid - // if type indicates packed data, these fields follow, otherwise we go right to the data - WORD packtype; // Only 1 = ADPCM is supported - WORD subtype; // Packing subtype - // ADPCM subtype - // 1: g723_40 - // 2: g721 - // 3: g723_24 - BYTE commonflags; // flags common to all packtypes (none defined so far) - BYTE packerflags; // flags for the specific packtype - ULONG leftchlen; // packed length of left channel in bytes - ULONG rightchlen; // packed length of right channel in bytes (ONLY PRESENT IN STEREO SAMPLES) - BYTE SampleData[1]; // Sample Data -} MMDSAMPLEHEADER; - - -// MMD0/MMD1 song header -typedef struct tagMMD0SONGHEADER -{ - MMD0SAMPLE sample[63]; - WORD numblocks; // # of blocks - WORD songlen; // # of entries used in playseq - BYTE playseq[256]; // Play sequence - WORD deftempo; // BPM tempo - signed char playtransp; // Play transpose - BYTE flags; // 0x10: Hex Volumes | 0x20: ST/NT/PT Slides | 0x40: 8 Channels song - BYTE flags2; // [b4-b0]+1: Tempo LPB, 0x20: tempo mode, 0x80: mix_conv=on - BYTE tempo2; // tempo TPL - BYTE trkvol[16]; // track volumes - BYTE mastervol; // master volume - BYTE numsamples; // # of samples (max=63) -} MMD0SONGHEADER; - - -// MMD2/MMD3 song header -typedef struct tagMMD2SONGHEADER -{ - MMD0SAMPLE sample[63]; - WORD numblocks; // # of blocks - WORD numsections; // # of sections - DWORD playseqtable; // filepos of play sequence - DWORD sectiontable; // filepos of sections table (WORD array) - DWORD trackvols; // filepos of tracks volume (BYTE array) - WORD numtracks; // # of tracks (max 64) - WORD numpseqs; // # of play sequences - DWORD trackpans; // filepos of tracks pan values (BYTE array) - LONG flags3; // 0x1:stereo_mix, 0x2:free_panning, 0x4:GM/XG compatibility - WORD voladj; // vol_adjust (set to 100 if 0) - WORD channels; // # of channels (4 if =0) - BYTE mix_echotype; // 1:normal,2:xecho - BYTE mix_echodepth; // 1..6 - WORD mix_echolen; // > 0 - signed char mix_stereosep; // -4..4 - BYTE pad0[223]; - WORD deftempo; // BPM tempo - signed char playtransp; // play transpose - BYTE flags; // 0x1:filteron, 0x2:jumpingon, 0x4:jump8th, 0x8:instr_attached, 0x10:hex_vol, 0x20:PT_slides, 0x40:8ch_conv,0x80:hq slows playing speed - BYTE flags2; // 0x80:mix_conv=on, [b4-b0]+1:tempo LPB, 0x20:tempo_mode - BYTE tempo2; // tempo TPL - BYTE pad1[16]; - BYTE mastervol; // master volume - BYTE numsamples; // # of samples (max 63) -} MMD2SONGHEADER; - -// For MMD0 the note information is held in 3 bytes, byte0, byte1, byte2. For reference we -// number the bits in each byte 0..7, where 0 is the low bit. -// The note is held as bits 5..0 of byte0 -// The instrument is encoded in 6 bits, bits 7 and 6 of byte0 and bits 7,6,5,4 of byte1 -// The command number is bits 3,2,1,0 of byte1, command data is in byte2: -// For command 0, byte2 represents the second data byte, otherwise byte2 -// represents the first data byte. -typedef struct tagMMD0BLOCK -{ - BYTE numtracks; - BYTE lines; // File value is 1 less than actual, so 0 -> 1 line -} MMD0BLOCK; // BYTE data[lines+1][tracks][3]; - - -// For MMD1,MMD2,MMD3 the note information is carried in 4 bytes, byte0, byte1, -// byte2 and byte3 -// The note is held as byte0 (values above 0x84 are ignored) -// The instrument is held as byte1 -// The command number is held as byte2, command data is in byte3 -// For commands 0 and 0x19 byte3 represents the second data byte, -// otherwise byte2 represents the first data byte. -typedef struct tagMMD1BLOCK -{ - WORD numtracks; // Number of tracks, may be > 64, but then that data is skipped. - WORD lines; // Stored value is 1 less than actual, so 0 -> 1 line - DWORD info; // Offset of BlockInfo (if 0, no block_info is present) -} MMD1BLOCK; - - -typedef struct tagMMD1BLOCKINFO -{ - DWORD hlmask; // Unimplemented - ignore - DWORD blockname; // file offset of block name - DWORD blocknamelen; // length of block name (including term. 0) - DWORD pagetable; // file offset of command page table - DWORD cmdexttable; // file offset of command extension table - DWORD reserved[4]; // future expansion -} MMD1BLOCKINFO; - - -// A set of play sequences is stored as an array of ULONG files offsets -// Each offset points to the play sequence itself. -typedef struct tagMMD2PLAYSEQ -{ - CHAR name[32]; - DWORD command_offs; // filepos of command table - DWORD reserved; - WORD length; - WORD seq[512]; // skip if > 0x8000 -} MMD2PLAYSEQ; - - -// A command table contains commands that effect a particular play sequence -// entry. The only commands read in are STOP or POSJUMP, all others are ignored -// POSJUMP is presumed to have extra bytes containing a WORD for the position -typedef struct tagMMDCOMMAND -{ - WORD offset; // Offset within current sequence entry - BYTE cmdnumber; // STOP (537) or POSJUMP (538) (others skipped) - BYTE extra_count; - BYTE extra_bytes[4];// [extra_count]; -} MMDCOMMAND; // Last entry has offset == 0xFFFF, cmd_number == 0 and 0 extrabytes - - -typedef struct tagMMD0EXP -{ - DWORD nextmod; // File offset of next Hdr - DWORD exp_smp; // Pointer to extra instrument data - WORD s_ext_entries; // Number of extra instrument entries - WORD s_ext_entrsz; // Size of extra instrument data - DWORD annotxt; - DWORD annolen; - DWORD iinfo; // Instrument names - WORD i_ext_entries; - WORD i_ext_entrsz; - DWORD jumpmask; - DWORD rgbtable; - BYTE channelsplit[4]; // Only used if 8ch_conv (extra channel for every nonzero entry) - DWORD n_info; - DWORD songname; // Song name - DWORD songnamelen; - DWORD dumps; - DWORD mmdinfo; - DWORD mmdrexx; - DWORD mmdcmd3x; - DWORD trackinfo_ofs; // ptr to song->numtracks ptrs to tag lists - DWORD effectinfo_ofs; // ptr to group ptrs - DWORD tag_end; -} MMD0EXP; - -#pragma pack() - - - -static void MedConvert(MODCOMMAND *p, const MMD0SONGHEADER *pmsh) -//--------------------------------------------------------------- -{ - const BYTE bpmvals[9] = { 179,164,152,141,131,123,116,110,104}; - - UINT command = p->command; - UINT param = p->param; - switch(command) - { - case 0x00: if (param) command = CMD_ARPEGGIO; else command = 0; break; - case 0x01: command = CMD_PORTAMENTOUP; break; - case 0x02: command = CMD_PORTAMENTODOWN; break; - case 0x03: command = CMD_TONEPORTAMENTO; break; - case 0x04: command = CMD_VIBRATO; break; - case 0x05: command = CMD_TONEPORTAVOL; break; - case 0x06: command = CMD_VIBRATOVOL; break; - case 0x07: command = CMD_TREMOLO; break; - case 0x0A: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = 0; break; - case 0x0B: command = CMD_POSITIONJUMP; break; - case 0x0C: command = CMD_VOLUME; - if (pmsh->flags & MMD_FLAG_VOLHEX) - { - if (param < 0x80) - { - param = (param+1) / 2; - } else command = 0; - } else - { - if (param <= 0x99) - { - param = (param >> 4)*10+((param & 0x0F) % 10); - if (param > 64) param = 64; - } else command = 0; - } - break; - case 0x09: command = (param < 0x20) ? CMD_SPEED : CMD_TEMPO; break; - case 0x0D: if (param & 0xF0) param &= 0xF0; command = CMD_VOLUMESLIDE; if (!param) command = 0; break; - case 0x0F: // Set Tempo / Special - // F.00 = Pattern Break - if (!param) command = CMD_PATTERNBREAK; else - // F.01 - F.F0: Set tempo/speed - if (param <= 0xF0) - { - if (pmsh->flags & MMD_FLAG_8CHANNEL) - { - param = (param > 10) ? 99 : bpmvals[param-1]; - } else - // F.01 - F.0A: Set Speed - if (param <= 0x0A) - { - command = CMD_SPEED; - } else - // Old tempo - if (!(pmsh->flags2 & MMD_FLAG2_BPM)) - { - param = _muldiv(param, 5*715909, 2*474326); - } - // F.0B - F.F0: Set Tempo (assumes LPB=4) - if (param > 0x0A) - { - command = CMD_TEMPO; - if (param < 0x21) param = 0x21; - if (param > 240) param = 240; - } - } else - switch(param) - { - // F.F1: Retrig 2x - case 0xF1: - command = CMD_MODCMDEX; - param = 0x93; - break; - // F.F2: Note Delay 2x - case 0xF2: - command = CMD_MODCMDEX; - param = 0xD3; - break; - // F.F3: Retrig 3x - case 0xF3: - command = CMD_MODCMDEX; - param = 0x92; - break; - // F.F4: Note Delay 1/3 - case 0xF4: - command = CMD_MODCMDEX; - param = 0xD2; - break; - // F.F5: Note Delay 2/3 - case 0xF5: - command = CMD_MODCMDEX; - param = 0xD4; - break; - // F.F8: Filter Off - case 0xF8: - command = CMD_MODCMDEX; - param = 0x00; - break; - // F.F9: Filter On - case 0xF9: - command = CMD_MODCMDEX; - param = 0x01; - break; - // F.FD: Very fast tone-portamento - case 0xFD: - command = CMD_TONEPORTAMENTO; - param = 0xFF; - break; - // F.FE: End Song - case 0xFE: - command = CMD_SPEED; - param = 0; - break; - // F.FF: Note Cut - case 0xFF: - command = CMD_MODCMDEX; - param = 0xC0; - break; - default: -#ifdef MED_LOG - Log("Unknown Fxx command: cmd=0x%02X param=0x%02X\n", command, param); -#endif - param = command = 0; - } - break; - // 11.0x: Fine Slide Up - case 0x11: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0x10; - break; - // 12.0x: Fine Slide Down - case 0x12: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0x20; - break; - // 14.xx: Vibrato - case 0x14: - command = CMD_VIBRATO; - break; - // 15.xx: FineTune - case 0x15: - command = CMD_MODCMDEX; - param &= 0x0F; - param |= 0x50; - break; - // 16.xx: Pattern Loop - case 0x16: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0x60; - break; - // 18.xx: Note Cut - case 0x18: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xC0; - break; - // 19.xx: Sample Offset - case 0x19: - command = CMD_OFFSET; - break; - // 1A.0x: Fine Volume Up - case 0x1A: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xA0; - break; - // 1B.0x: Fine Volume Down - case 0x1B: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xB0; - break; - // 1D.xx: Pattern Break - case 0x1D: - command = CMD_PATTERNBREAK; - break; - // 1E.0x: Pattern Delay - case 0x1E: - command = CMD_MODCMDEX; - if (param > 0x0F) param = 0x0F; - param |= 0xE0; - break; - // 1F.xy: Retrig - case 0x1F: - command = CMD_RETRIG; - param &= 0x0F; - break; - // 2E.xx: set panning - case 0x2E: - command = CMD_MODCMDEX; - param = ((param + 0x10) & 0xFF) >> 1; - if (param > 0x0F) param = 0x0F; - param |= 0x80; - break; - default: -#ifdef MED_LOG - // 0x2E ? - Log("Unknown command: cmd=0x%02X param=0x%02X\n", command, param); -#endif - command = param = 0; - } - p->command = command; - p->param = param; -} - - -BOOL CSoundFile::ReadMed(const BYTE *lpStream, DWORD dwMemLength) -//--------------------------------------------------------------- -{ - const MEDMODULEHEADER *pmmh; - const MMD0SONGHEADER *pmsh; - const MMD2SONGHEADER *pmsh2; - const MMD0EXP *pmex; - DWORD dwBlockArr, dwSmplArr, dwExpData, wNumBlocks; - LPDWORD pdwTable; - CHAR version; - UINT deftempo; - int playtransp = 0; - - if ((!lpStream) || (dwMemLength < 0x200)) return FALSE; - pmmh = (MEDMODULEHEADER *)lpStream; - if (((pmmh->id & 0x00FFFFFF) != 0x444D4D) || (!pmmh->song)) return FALSE; - // Check for 'MMDx' - DWORD dwSong = bswapBE32(pmmh->song); - if ((dwSong >= dwMemLength) || (dwSong + sizeof(MMD0SONGHEADER) >= dwMemLength)) return FALSE; - version = (signed char)((pmmh->id >> 24) & 0xFF); - if ((version < '0') || (version > '3')) return FALSE; -#ifdef MED_LOG - Log("\nLoading MMD%c module (flags=0x%02X)...\n", version, bswapBE32(pmmh->mmdflags)); - Log(" modlen = %d\n", bswapBE32(pmmh->modlen)); - Log(" song = 0x%08X\n", bswapBE32(pmmh->song)); - Log(" psecnum = %d\n", bswapBE16(pmmh->psecnum)); - Log(" pseq = %d\n", bswapBE16(pmmh->pseq)); - Log(" blockarr = 0x%08X\n", bswapBE32(pmmh->blockarr)); - Log(" mmdflags = 0x%08X\n", bswapBE32(pmmh->mmdflags)); - Log(" smplarr = 0x%08X\n", bswapBE32(pmmh->smplarr)); - Log(" reserved = 0x%08X\n", bswapBE32(pmmh->reserved)); - Log(" expdata = 0x%08X\n", bswapBE32(pmmh->expdata)); - Log(" reserved2= 0x%08X\n", bswapBE32(pmmh->reserved2)); - Log(" pstate = %d\n", bswapBE16(pmmh->pstate)); - Log(" pblock = %d\n", bswapBE16(pmmh->pblock)); - Log(" pline = %d\n", bswapBE16(pmmh->pline)); - Log(" pseqnum = %d\n", bswapBE16(pmmh->pseqnum)); - Log(" actplayline=%d\n", bswapBE16(pmmh->actplayline)); - Log(" counter = %d\n", pmmh->counter); - Log(" extra_songs = %d\n", pmmh->extra_songs); - Log("\n"); -#endif - m_nType = MOD_TYPE_MED; - m_nSongPreAmp = 0x20; - dwBlockArr = bswapBE32(pmmh->blockarr); - dwSmplArr = bswapBE32(pmmh->smplarr); - dwExpData = bswapBE32(pmmh->expdata); - if ((dwExpData) && (dwExpData+sizeof(MMD0EXP) < dwMemLength)) - pmex = (MMD0EXP *)(lpStream+dwExpData); - else - pmex = NULL; - pmsh = (MMD0SONGHEADER *)(lpStream + dwSong); - pmsh2 = (MMD2SONGHEADER *)pmsh; -#ifdef MED_LOG - if (version < '2') - { - Log("MMD0 Header:\n"); - Log(" numblocks = %d\n", bswapBE16(pmsh->numblocks)); - Log(" songlen = %d\n", bswapBE16(pmsh->songlen)); - Log(" playseq = "); - for (UINT idbg1=0; idbg1<16; idbg1++) Log("%2d, ", pmsh->playseq[idbg1]); - Log("...\n"); - Log(" deftempo = 0x%04X\n", bswapBE16(pmsh->deftempo)); - Log(" playtransp = %d\n", (signed char)pmsh->playtransp); - Log(" flags(1,2) = 0x%02X, 0x%02X\n", pmsh->flags, pmsh->flags2); - Log(" tempo2 = %d\n", pmsh->tempo2); - Log(" trkvol = "); - for (UINT idbg2=0; idbg2<16; idbg2++) Log("0x%02X, ", pmsh->trkvol[idbg2]); - Log("...\n"); - Log(" mastervol = 0x%02X\n", pmsh->mastervol); - Log(" numsamples = %d\n", pmsh->numsamples); - } else - { - Log("MMD2 Header:\n"); - Log(" numblocks = %d\n", bswapBE16(pmsh2->numblocks)); - Log(" numsections= %d\n", bswapBE16(pmsh2->numsections)); - Log(" playseqptr = 0x%04X\n", bswapBE32(pmsh2->playseqtable)); - Log(" sectionptr = 0x%04X\n", bswapBE32(pmsh2->sectiontable)); - Log(" trackvols = 0x%04X\n", bswapBE32(pmsh2->trackvols)); - Log(" numtracks = %d\n", bswapBE16(pmsh2->numtracks)); - Log(" numpseqs = %d\n", bswapBE16(pmsh2->numpseqs)); - Log(" trackpans = 0x%04X\n", bswapBE32(pmsh2->trackpans)); - Log(" flags3 = 0x%08X\n", bswapBE32(pmsh2->flags3)); - Log(" voladj = %d\n", bswapBE16(pmsh2->voladj)); - Log(" channels = %d\n", bswapBE16(pmsh2->channels)); - Log(" echotype = %d\n", pmsh2->mix_echotype); - Log(" echodepth = %d\n", pmsh2->mix_echodepth); - Log(" echolen = %d\n", bswapBE16(pmsh2->mix_echolen)); - Log(" stereosep = %d\n", (signed char)pmsh2->mix_stereosep); - Log(" deftempo = 0x%04X\n", bswapBE16(pmsh2->deftempo)); - Log(" playtransp = %d\n", (signed char)pmsh2->playtransp); - Log(" flags(1,2) = 0x%02X, 0x%02X\n", pmsh2->flags, pmsh2->flags2); - Log(" tempo2 = %d\n", pmsh2->tempo2); - Log(" mastervol = 0x%02X\n", pmsh2->mastervol); - Log(" numsamples = %d\n", pmsh->numsamples); - } - Log("\n"); -#endif - wNumBlocks = bswapBE16(pmsh->numblocks); - m_nChannels = 4; - m_nSamples = pmsh->numsamples; - if (m_nSamples > 63) m_nSamples = 63; - // Tempo - m_nDefaultTempo = 125; - deftempo = bswapBE16(pmsh->deftempo); - if (!deftempo) deftempo = 125; - if (pmsh->flags2 & MMD_FLAG2_BPM) - { - UINT tempo_tpl = (pmsh->flags2 & MMD_FLAG2_BMASK) + 1; - if (!tempo_tpl) tempo_tpl = 4; - deftempo *= tempo_tpl; - deftempo /= 4; - #ifdef MED_LOG - Log("newtempo: %3d bpm (bpm=%3d lpb=%2d)\n", deftempo, bswapBE16(pmsh->deftempo), (pmsh->flags2 & MMD_FLAG2_BMASK)+1); - #endif - } else - { - deftempo = _muldiv(deftempo, 5*715909, 2*474326); - #ifdef MED_LOG - Log("oldtempo: %3d bpm (bpm=%3d)\n", deftempo, bswapBE16(pmsh->deftempo)); - #endif - } - // Speed - m_nDefaultSpeed = pmsh->tempo2; - if (!m_nDefaultSpeed) m_nDefaultSpeed = 6; - if (deftempo < 0x21) deftempo = 0x21; - if (deftempo > 255) - { - while ((m_nDefaultSpeed > 3) && (deftempo > 260)) - { - deftempo = (deftempo * (m_nDefaultSpeed - 1)) / m_nDefaultSpeed; - m_nDefaultSpeed--; - } - if (deftempo > 255) deftempo = 255; - } - m_nDefaultTempo = deftempo; - // Reading Samples - for (UINT iSHdr=0; iSHdrnLoopStart = bswapBE16(pmsh->sample[iSHdr].rep) << 1; - pins->nLoopEnd = pins->nLoopStart + (bswapBE16(pmsh->sample[iSHdr].replen) << 1); - pins->nVolume = (pmsh->sample[iSHdr].svol << 2); - pins->nGlobalVol = 64; - if (pins->nVolume > 256) pins->nVolume = 256; - pins->RelativeTone = -12 * pmsh->sample[iSHdr].strans; - pins->nPan = 128; - if (pins->nLoopEnd) pins->uFlags |= CHN_LOOP; - } - // Common Flags - if (!(pmsh->flags & 0x20)) m_dwSongFlags |= SONG_FASTVOLSLIDES; - // Reading play sequence - if (version < '2') - { - UINT nbo = pmsh->songlen >> 8; - if (nbo >= MAX_ORDERS) nbo = MAX_ORDERS-1; - if (!nbo) nbo = 1; - memcpy(Order, pmsh->playseq, nbo); - playtransp = pmsh->playtransp; - } else - { - UINT nOrders, nSections; - UINT nTrks = bswapBE16(pmsh2->numtracks); - if ((nTrks >= 4) && (nTrks <= 32)) m_nChannels = nTrks; - DWORD playseqtable = bswapBE32(pmsh2->playseqtable); - UINT numplayseqs = bswapBE16(pmsh2->numpseqs); - if (!numplayseqs) numplayseqs = 1; - nOrders = 0; - nSections = bswapBE16(pmsh2->numsections); - DWORD sectiontable = bswapBE32(pmsh2->sectiontable); - if ((!nSections) || (!sectiontable) || (sectiontable >= dwMemLength-2)) nSections = 1; - nOrders = 0; - for (UINT iSection=0; iSectionname, 31); - UINT n = bswapBE16(pmps->length); - if (pseq+n <= dwMemLength) - { - for (UINT i=0; iseq[i] >> 8; - if ((seqval < wNumBlocks) && (nOrders < MAX_ORDERS-1)) - { - Order[nOrders++] = seqval; - } - } - } - } - } - playtransp = pmsh2->playtransp; - while (nOrders < MAX_ORDERS) Order[nOrders++] = 0xFF; - } - // Reading Expansion structure - if (pmex) - { - // Channel Split - if ((m_nChannels == 4) && (pmsh->flags & 0x40)) - { - for (UINT i8ch=0; i8ch<4; i8ch++) - { - if (pmex->channelsplit[i8ch]) m_nChannels++; - } - } - // Song Comments - UINT annotxt = bswapBE32(pmex->annotxt); - UINT annolen = bswapBE32(pmex->annolen); - if ((annotxt) && (annolen) && (annotxt+annolen <= dwMemLength)) - { - m_lpszSongComments = new char[annolen+1]; - memcpy(m_lpszSongComments, lpStream+annotxt, annolen); - m_lpszSongComments[annolen] = 0; - } - // Song Name - UINT songname = bswapBE32(pmex->songname); - UINT songnamelen = bswapBE32(pmex->songnamelen); - if ((songname) && (songnamelen) && (songname+songnamelen <= dwMemLength)) - { - if (songnamelen > 31) songnamelen = 31; - memcpy(m_szNames[0], lpStream+songname, songnamelen); - } - // Sample Names - DWORD smpinfoex = bswapBE32(pmex->iinfo); - if (smpinfoex) - { - DWORD iinfoptr = bswapBE32(pmex->iinfo); - UINT ientries = bswapBE16(pmex->i_ext_entries); - UINT ientrysz = bswapBE16(pmex->i_ext_entrsz); - - if ((iinfoptr) && (ientrysz < 256) && (iinfoptr + ientries*ientrysz < dwMemLength)) - { - LPCSTR psznames = (LPCSTR)(lpStream + iinfoptr); - UINT maxnamelen = ientrysz; - if (maxnamelen > 32) maxnamelen = 32; - for (UINT i=0; itrackinfo_ofs); - if ((trackinfo_ofs) && (trackinfo_ofs + m_nChannels * 4 < dwMemLength)) - { - DWORD *ptrktags = (DWORD *)(lpStream + trackinfo_ofs); - for (UINT i=0; i MAX_CHANNELNAME) trknamelen = MAX_CHANNELNAME; - if ((trknameofs) && (trknameofs + trknamelen < dwMemLength)) - { - lstrcpyn(ChnSettings[i].szName, (LPCSTR)(lpStream+trknameofs), MAX_CHANNELNAME); - } - } - } - } - } - // Reading samples - if (dwSmplArr > dwMemLength - 4*m_nSamples) return TRUE; - pdwTable = (LPDWORD)(lpStream + dwSmplArr); - for (UINT iSmp=0; iSmp= dwMemLength) || (dwPos + sizeof(MMDSAMPLEHEADER) >= dwMemLength)) continue; - MMDSAMPLEHEADER *psdh = (MMDSAMPLEHEADER *)(lpStream + dwPos); - UINT len = bswapBE32(psdh->length); - #ifdef MED_LOG - Log("SampleData %d: stype=0x%02X len=%d\n", iSmp, bswapBE16(psdh->type), len); - #endif - if ((len > MAX_SAMPLE_LENGTH) || (dwPos + len + 6 > dwMemLength)) len = 0; - UINT flags = RS_PCM8S, stype = bswapBE16(psdh->type); - LPSTR psdata = (LPSTR)(lpStream + dwPos + 6); - if (stype & 0x80) - { - psdata += (stype & 0x20) ? 14 : 6; - } else - { - if (stype & 0x10) - { - Ins[iSmp+1].uFlags |= CHN_16BIT; - len /= 2; - flags = (stype & 0x20) ? RS_STPCM16M : RS_PCM16M; - } else - { - flags = (stype & 0x20) ? RS_STPCM8S : RS_PCM8S; - } - if (stype & 0x20) len /= 2; - } - Ins[iSmp+1].nLength = len; - ReadSample(&Ins[iSmp+1], flags, psdata, dwMemLength - dwPos - 6); - } - // Reading patterns (blocks) - if (wNumBlocks > MAX_PATTERNS) wNumBlocks = MAX_PATTERNS; - if ((!dwBlockArr) || (dwBlockArr > dwMemLength - 4*wNumBlocks)) return TRUE; - pdwTable = (LPDWORD)(lpStream + dwBlockArr); - playtransp += (version == '3') ? 24 : 48; - for (UINT iBlk=0; iBlk= dwMemLength) || (dwPos >= dwMemLength - 8)) continue; - UINT lines = 64, tracks = 4; - if (version == '0') - { - const MMD0BLOCK *pmb = (const MMD0BLOCK *)(lpStream + dwPos); - lines = pmb->lines + 1; - tracks = pmb->numtracks; - if (!tracks) tracks = m_nChannels; - if ((Patterns[iBlk] = AllocatePattern(lines, m_nChannels)) == NULL) continue; - PatternSize[iBlk] = lines; - MODCOMMAND *p = Patterns[iBlk]; - LPBYTE s = (LPBYTE)(lpStream + dwPos + 2); - UINT maxlen = tracks*lines*3; - if (maxlen + dwPos > dwMemLength - 2) break; - for (UINT y=0; y> 4; - if (s[0] & 0x80) instr |= 0x10; - if (s[0] & 0x40) instr |= 0x20; - if ((note) && (note <= 132)) p->note = note + playtransp; - p->instr = instr; - p->command = s[1] & 0x0F; - p->param = s[2]; - // if (!iBlk) Log("%02X.%02X.%02X | ", s[0], s[1], s[2]); - MedConvert(p, pmsh); - p++; - } - //if (!iBlk) Log("\n"); - } - } else - { - MMD1BLOCK *pmb = (MMD1BLOCK *)(lpStream + dwPos); - #ifdef MED_LOG - Log("MMD1BLOCK: lines=%2d, tracks=%2d, offset=0x%04X\n", - bswapBE16(pmb->lines), bswapBE16(pmb->numtracks), bswapBE32(pmb->info)); - #endif - MMD1BLOCKINFO *pbi = NULL; - BYTE *pcmdext = NULL; - lines = (pmb->lines >> 8) + 1; - tracks = pmb->numtracks >> 8; - if (!tracks) tracks = m_nChannels; - if ((Patterns[iBlk] = AllocatePattern(lines, m_nChannels)) == NULL) continue; - PatternSize[iBlk] = (WORD)lines; - DWORD dwBlockInfo = bswapBE32(pmb->info); - if ((dwBlockInfo) && (dwBlockInfo < dwMemLength - sizeof(MMD1BLOCKINFO))) - { - pbi = (MMD1BLOCKINFO *)(lpStream + dwBlockInfo); - #ifdef MED_LOG - Log(" BLOCKINFO: blockname=0x%04X namelen=%d pagetable=0x%04X &cmdexttable=0x%04X\n", - bswapBE32(pbi->blockname), bswapBE32(pbi->blocknamelen), bswapBE32(pbi->pagetable), bswapBE32(pbi->cmdexttable)); - #endif - if ((pbi->blockname) && (pbi->blocknamelen)) - { - DWORD nameofs = bswapBE32(pbi->blockname); - UINT namelen = bswapBE32(pbi->blocknamelen); - if ((nameofs < dwMemLength) && (nameofs+namelen < dwMemLength)) - { - SetPatternName(iBlk, (LPCSTR)(lpStream+nameofs)); - } - } - if (pbi->cmdexttable) - { - DWORD cmdexttable = bswapBE32(pbi->cmdexttable); - if (cmdexttable < dwMemLength - 4) - { - cmdexttable = bswapBE32(*(DWORD *)(lpStream + cmdexttable)); - if ((cmdexttable) && (cmdexttable <= dwMemLength - lines*tracks)) - { - pcmdext = (BYTE *)(lpStream + cmdexttable); - } - } - } - } - MODCOMMAND *p = Patterns[iBlk]; - LPBYTE s = (LPBYTE)(lpStream + dwPos + 8); - UINT maxlen = tracks*lines*4; - if (maxlen + dwPos > dwMemLength - 8) break; - for (UINT y=0; y 120) rnote = 120; - p->note = (BYTE)rnote; - } - p->instr = s[1]; - p->command = s[2]; - p->param = s[3]; - if (pcmdext) p->vol = pcmdext[x]; - MedConvert(p, pmsh); - p++; - } - if (pcmdext) pcmdext += tracks; - } - } - } - // Setup channel pan positions - for (UINT iCh=0; iCh - - Portability: - All systems - all compilers (hopefully) -*/ - -#include -#include -#include -#include -#include -//#include // for sleep - -#ifdef NEWMIKMOD -#include "mikmod.h" -#include "uniform.h" -#include "itshare.h" // for STMEM_PITCHSLIDE -typedef UBYTE BYTE; -typedef UWORD WORD; -#define MAX_POLYPHONY 12 // max notes in one midi channel -#define MAX_TRACKS 63 // max mod tracks -#define WHEELSHIFT 11 // how many bits the 13bit midi wheel value must shift right -#else -#include "stdafx.h" -#include "sndfile.h" -#define PAN_LEFT 0x30 -#define PAN_RIGHT 0xD0 -#define MAX_POLYPHONY 16 // max notes in one midi channel -#define MAX_TRACKS (MAX_BASECHANNELS-6) // max mod tracks -#define WHEELSHIFT 10 // how many bits the 13bit midi wheel value must shift right -#endif - -#include "load_pat.h" - -#define ROWSPERNOTE 16 -#define ENV_MMMID_SPEED "MMMID_SPEED" -#define ENV_MMMID_DEBUG "MMMID_DEBUG" -#define ENV_MMMID_VERBOSE "MMMID_VERBOSE" - -/************************************************************************** -**************************************************************************/ -#ifdef NEWMIKMOD -static char MID_Version[] = "Musical Instrument Digital Interface"; -#endif - -typedef enum { - none, - wheeldown, - wheelup, - fxbrk, - tmpo, - fxsync, - modwheel, - mainvol, - prog -} MIDEVENT_X_EFFECT; - -typedef struct _MIDEVENT -{ - struct _MIDEVENT *next; - ULONG tracktick; - BYTE flg; // 1 = note present - BYTE note; - BYTE volume; - BYTE smpno; - BYTE fx; - BYTE fxparam; -} MIDEVENT; - -typedef struct _MIDTRACK -{ - struct _MIDTRACK *next; - MIDEVENT *head; - MIDEVENT *tail; - MIDEVENT *workevent; // keeps track of events in track - int balance; // last balance on this track - ULONG vtracktick; // tracktick of last note event (on or off) - BYTE chan; - BYTE vpos; // 0xff is track is free for use, otherwise it's the note playing on this track - BYTE volume; // last note volume on this track - BYTE instr; // current instrument for this track -} MIDTRACK; - -#ifdef NEWMIKMOD - -#define MMFILE MMSTREAM -#define mmfseek(f,p,w) _mm_fseek(f,p,w) -#define mmftell(x) _mm_ftell(x) -#define mmreadUBYTE(f) _mm_read_UBYTE(f) -#define mmreadSBYTES(buf,sz,f) _mm_read_SBYTES(buf,sz,f) -#define mmreadUBYTES(buf,sz,f) _mm_read_UBYTES(buf,sz,f) - -#else - -#define MMSTREAM FILE -#define _mm_fseek(f,pos,whence) fseek(f,pos,whence) -#define _mm_read_UBYTES(buf,sz,f) fread(buf,sz,1,f) -#define _mm_read_SBYTES(buf,sz,f) fread(buf,sz,1,f) -#define DupStr(h,buf,sz) strdup(buf) -#define _mm_calloc(h,n,sz) calloc(n,sz) -#define _mm_recalloc(h,buf,sz,elsz) realloc(buf,sz) -#define _mm_free(h,p) free(p) - -typedef struct { - char *mm; - int sz; - int pos; -} MMFILE; - -static void mmfseek(MMFILE *mmfile, long p, int whence) -{ - switch(whence) { - case SEEK_SET: - mmfile->pos = p; - break; - case SEEK_CUR: - mmfile->pos += p; - break; - case SEEK_END: - mmfile->pos = mmfile->sz + p; - break; - } -} - -static long mmftell(MMFILE *mmfile) -{ - return mmfile->pos; -} - -static BYTE mmreadUBYTE(MMFILE *mmfile) -{ - BYTE b; - b = (BYTE)mmfile->mm[mmfile->pos]; - mmfile->pos++; - return b; -} - -static void mmreadUBYTES(BYTE *buf, long sz, MMFILE *mmfile) -{ - memcpy(buf, &mmfile->mm[mmfile->pos], sz); - mmfile->pos += sz; -} - -static void mmreadSBYTES(char *buf, long sz, MMFILE *mmfile) -{ - memcpy(buf, &mmfile->mm[mmfile->pos], sz); - mmfile->pos += sz; -} - -#endif - -/************************************************************************** -**************************************************************************/ - -typedef struct _MIDHANDLE -{ -#ifdef NEWMIKMOD - MM_ALLOC *allochandle; - MM_ALLOC *trackhandle; -#endif - MMFILE *mmf; - MIDTRACK *track; - MIDTRACK *tp; - ULONG tracktime; - const char *debug; - const char *verbose; - int speed; - int midispeed; - int midiformat; - int resolution; - int miditracks; - int divider; - int tempo; - int percussion; - long deltatime; -} MIDHANDLE; - -static void mid_dump_tracks(MIDHANDLE *h) -{ - MIDTRACK *tr; - MIDEVENT *e; - int t; - printf("tracktime = %ld\n", (long)(h->tracktime)); - printf("speed = %d\n", h->speed); - printf("midispeed = %d\n", h->midispeed); - printf("midiformat = %d\n", h->midiformat); - printf("resolution = %d\n", h->resolution); - printf("miditracks = %d\n", h->miditracks); - printf("divider = %d\n", h->divider); - printf("tempo = %d\n", h->tempo); - printf("percussion = %d\n", h->percussion); - printf("deltatime = %ld\n", h->deltatime); - t = 0; - for( tr=h->track; tr; tr = tr->next ) { - t++; - printf("TRACK %2d chan=%d note=0x%02x vol=%d pan=0x%02x instr=%d\n", t, tr->chan + 1, tr->vpos, tr->balance, tr->volume, tr->instr); - for( e=tr->head; e; e=e->next ) { - printf("%2d %6ld %s %3d %3d %3d ", - t, (long)(e->tracktick), - e->flg? "NOTE": "CTRL", e->note, e->volume, e->smpno); - switch( e->fx ) { - case fxbrk: printf("fxbrk\n");break; - case fxsync: printf("fxsync\n");break; - case prog: printf("prog %d\n", e->fxparam);break; - case mainvol: printf("mainvol %d\n", e->fxparam);break; - case modwheel: printf("modwheel %d\n", e->fxparam);break; - case wheeldown: printf("wheeldown %d\n", e->fxparam);break; - case wheelup: printf("wheelup %d\n", e->fxparam);break; - case tmpo: printf("tmpo %d\n", e->fxparam);break; - default: printf("\n");break; - } - } - } -} - -static void mid_message(const char *s1, const char *s2) -{ - char txt[256]; - if( strlen(s1) + strlen(s2) > 255 ) return; - sprintf(txt, s1, s2); -#ifdef NEWMIKMOD - _mmlog(txt); -#else - fprintf(stderr, "load_mid > %s\n", txt); -#endif -} - -static ULONG miditicks(MIDHANDLE *h, ULONG modtick) -{ - return modtick * h->divider / ROWSPERNOTE / h->speed; -} - -static ULONG modticks(MIDHANDLE *h, ULONG miditick) -{ - return miditick * ROWSPERNOTE * h->speed / h->divider; -} - -static void mid_adjust_for_optimal_tempo(MIDHANDLE *h, int maxtempo) -{ - // the tempo is adjusted so that the maximum tempo is 255 - // this way we have the biggest change that very short notes get played - // and we make sure the tempo doesn't become too large or too small - // if the piece in hand isn't so weird it changes tempo from 20 to 255, that is. - // tempo is only registered in first track (h->track) because it is a global event - MIDEVENT *e; - int d, t; - if( maxtempo < 1 ) return; - d = h->divider; - t = maxtempo; - h->divider = (t * d) / 255; - while( (h->midispeed = miditicks(h, h->speed)) < h->speed ) { - ++t; - h->divider = (t * d) / 255; - } - if( h->verbose && t > maxtempo ) - printf("Adjusted maximum tempo from %d to %d to get %d miditicks per patternrow\n", - maxtempo, 2 * maxtempo - t, h->midispeed); - if( h->track ) { - for( e=h->track->head; e; e=e->next ) { - if( e->fx == tmpo ) - e->fxparam = (255 * e->fxparam ) / t; - } - } -} - -// ===================================================================================== -static MIDEVENT *mid_new_event(MIDHANDLE *h) -// ===================================================================================== -{ - MIDEVENT *retval; - - retval = (MIDEVENT *)_mm_calloc(h->trackhandle, 1,sizeof(MIDEVENT)); - retval->next = NULL; - retval->tracktick = h->tracktime; - retval->flg = 0; - retval->note = 0; - retval->volume = 0; - retval->smpno = 0; - retval->fx = none; - retval->fxparam = 0; - return retval; -} - -// ===================================================================================== -static MIDTRACK *mid_new_track(MIDHANDLE *h, int mch, int pos) -// ===================================================================================== -{ - MIDTRACK *retval; - retval = (MIDTRACK *)_mm_calloc(h->trackhandle, 1,sizeof(MIDTRACK)); - retval->next = NULL; - retval->vpos = pos; - retval->instr = 1; - retval->chan = mch; - retval->head = NULL; - retval->tail = NULL; - retval->workevent = NULL; - retval->vtracktick = 0; - retval->volume = h->track? h->track->volume: 120; - retval->balance = 64; - return retval; -} - -static int mid_numtracks(MIDHANDLE *h) -{ - int n; - MIDTRACK *t; - n=0; - for( t = h->track; t; t=t->next ) - n++; - return n; -} - -// find out how many midichannel we have -static int mid_numchans(MIDHANDLE *h) -{ - int i,c,n; - MIDTRACK *t; - c = 0; - for( t = h->track; t; t=t->next ) - c |= (1<chan); - n = 0; - for( i=0; i<16; i++ ) - if( c & (1<track; t; t=t->next ) - c |= (1<chan); - n = 0; - for( i=0; itracktime = 0; - for( tr = h->track; tr; tr = tr->next ) { - tr->vpos = 0xff; - tr->workevent = tr->head; - tr->vtracktick = 0; - } -} - -static void mid_update_track(MIDTRACK *tr) -{ - MIDEVENT *e; - e = tr->workevent; - if( e->flg ) { - if( e->volume ) tr->vpos = e->note; - else tr->vpos = 0xff; - tr->volume = e->volume; - tr->vtracktick = e->tracktick; - } - if( e->fx == prog ) tr->instr = e->fxparam; -} - -static void mid_sync_track(MIDTRACK *tr, ULONG tracktime) -{ - MIDEVENT *e; - e = tr->workevent; - if( e && e->tracktick > tracktime ) e = tr->head; // start again.... - for( ; e && e->tracktick <= tracktime; e=e->next ) { - tr->workevent = e; - mid_update_track(tr); - } -} - -// ===================================================================================== -static MIDTRACK *mid_find_track(MIDHANDLE *h, int mch, int pos) -// ===================================================================================== -{ - MIDTRACK *tr; - for( tr=h->track; tr; tr=tr->next ) { - mid_sync_track(tr, h->tracktime); - if( tr->chan == mch && tr->vpos == pos ) - return tr; - } - return NULL; -} - -// ===================================================================================== -static MIDTRACK *mid_locate_track(MIDHANDLE *h, int mch, int pos) -// ===================================================================================== -{ - MIDTRACK *tr, *prev, *trunused; - MIDEVENT *e; - int instrno = 1; - int polyphony; - int vol = 0, bal = 0; - int numtracks; - ULONG tmin; - prev = NULL; - trunused = NULL; - polyphony = 0; - numtracks = 0; - tmin = h->midispeed; // minimal distance between note events in track - // look up track with desired channel and pos (note) - for( tr=h->track; tr; tr=tr->next ) { - mid_sync_track(tr, h->tracktime); - if( tr->chan == mch ) { - if( tr->vpos == pos ) - return tr; - if( tr->vpos == 0xff ) { - // check if track with silence is quiet long enough - if( h->tracktime > tr->vtracktick + tmin ) trunused = tr; - } - else vol = tr->volume; - instrno = tr->instr; - bal = tr->balance; - polyphony++; - } - numtracks++; - prev = tr; - } - if( trunused ) { - trunused->vpos = pos; - return trunused; - } - if( polyphony > MAX_POLYPHONY || (polyphony > 0 && numtracks > MAX_TRACKS) ) { // do not use up too much channels - for( tr=h->track; tr; tr=tr->next ) { - if( tr->chan == mch ) { - e = tr->workevent; - if( h->tracktime > e->tracktick + tmin ) { - tmin = h->tracktime - e->tracktick; - trunused = tr; - } - } - } - if( trunused ) { - trunused->vpos = pos; - return trunused; - } - } - if( numtracks > MAX_TRACKS ) { // we can not allocate new tracks - tmin = 0; - for( tr=h->track; tr; tr=tr->next ) { - if( tr->chan == mch ) { - e = tr->workevent; - if( h->tracktime >= e->tracktick + tmin ) { - tmin = h->tracktime - e->tracktick; - trunused = tr; - } - } - } - if( trunused ) { - trunused->vpos = pos; - return trunused; - } - tmin = 0; - for( tr=h->track; tr; tr=tr->next ) { - e = tr->workevent; - if( h->tracktime >= e->tracktick + tmin ) { - tmin = h->tracktime - e->tracktick; - trunused = tr; - } - } - if( trunused ) { - trunused->vpos = pos; - trunused->chan = mch; - return trunused; - } - } - tr = mid_new_track(h, mch, pos); - tr->instr = instrno; - tr->volume = vol; - tr->balance = bal; - if( prev ) prev->next = tr; - else h->track = tr; - return tr; -} - -static void mid_add_event(MIDHANDLE *h, MIDTRACK *tp, MIDEVENT *e) -{ - MIDEVENT *ew, *ep; - ep = NULL; - ew = tp->workevent; - if( ew && ew->tracktick > e->tracktick ) ew = tp->head; // start again from the beginning... - for( ; ew && ew->tracktick <= e->tracktick; ew = ew->next ) { - ep = ew; - tp->workevent = ew; - mid_update_track(tp); - } - if( ep ) { - ep->next = e; - e->next = ew; - } - else { - e->next = tp->head; - tp->head = e; - } - if( !e->next ) - tp->tail = e; - tp->workevent = e; - mid_update_track(tp); -} - -static void mid_add_tempo_event(MIDHANDLE *h, int tempo) -{ - MIDEVENT *e; - e = mid_new_event(h); - e->flg = 0; - e->fx = tmpo; - e->fxparam = tempo; - mid_add_event(h, h->track, e); -} - -static void mid_add_partbreak(MIDHANDLE *h) -{ - MIDEVENT *e; - e = mid_new_event(h); - e->flg = 0; - e->fx = fxbrk; - mid_add_event(h, h->track, e); -} - -static void mid_add_noteoff(MIDHANDLE *h, MIDTRACK *tp) -{ - MIDEVENT *e; - e = mid_new_event(h); - e->flg = 1; - e->note = tp->vpos; - e->smpno = tp->instr; - mid_add_event(h, tp, e); -} - -static void mid_add_noteon(MIDHANDLE *h, MIDTRACK *tp, int n, int vol) -{ - MIDEVENT *e; - e = mid_new_event(h); - e->flg = 1; - e->note = n; - e->smpno = tp->instr; - e->volume = vol; - mid_add_event(h, tp, e); -} - -static BYTE modtremolo(int midimod) -{ - int m; - if( midimod == 0 ) return 0; - if( midimod > 63 ) { - m = (128 - midimod) / 4; - if( m==0 ) m = 1; - return m|0xf0; // find slide down - } - m = midimod / 4; - if( m==0 ) m = 1; - return (m<<4)|0x0f; // find slide up -} - -// ===================================================================================== -static void mid_mod_wheel(MIDHANDLE *h, int mch, int mod) -// ===================================================================================== -{ - MIDTRACK *tr; - MIDEVENT *e; - for( tr=h->track; tr; tr=tr->next ) { - if( tr->chan == mch ) { - mid_sync_track(tr, h->tracktime); - if( tr->vpos != 0xff ) { // only on tracks with notes on... - e = mid_new_event(h); - e->flg = 0; - e->fx = modwheel; - e->fxparam = modtremolo(mod); - mid_add_event(h, tr, e); - } - } - } -} - -// ===================================================================================== -static void mid_main_volume(MIDHANDLE *h, int mch, int vol) -// ===================================================================================== -{ - MIDTRACK *tr; - MIDEVENT *e; - for( tr=h->track; tr; tr=tr->next ) { - if( tr->chan == mch ) { - e = mid_new_event(h); - e->flg = 0; - e->fx = mainvol; - e->fxparam = vol; - mid_add_event(h, tr, e); - } - } -} - -// transform 0..63..127 to left..center..right in 2n+1 areas -static int modpan(int midipan, int n) -{ - int npan, area, x; - x = 2 * n + 1; - area = (midipan * x * (PAN_RIGHT - PAN_LEFT))>>7; - npan = (PAN_LEFT * x + area) / x; - return npan; -} - -// ===================================================================================== -static void mid_pan(MIDHANDLE *h, int mch, int pan) -// ===================================================================================== -{ - MIDTRACK *tr; - int hits; - hits = 0; - for( tr=h->track; tr; tr=tr->next ) { - if( tr->chan == mch ) { - hits++; - tr->balance = pan; - } - } - if( !hits ) { - tr = mid_locate_track(h, mch, 0xff); - tr->balance = pan; - } -} - -// ===================================================================================== -static void mid_add_program(MIDHANDLE *h, int mch, int pr) -// ===================================================================================== -{ - MIDTRACK *tr; - MIDEVENT *e; - int hits; - hits = 0; - for( tr=h->track; tr; tr=tr->next ) { - if( tr->chan == mch ) { - hits++; - e = mid_new_event(h); - e->flg = 0; - e->fx = prog; - e->fxparam = pat_gmtosmp(pr + 1); - mid_add_event(h, tr, e); - } - } - if( !hits ) { - tr = mid_locate_track(h, mch, 0xff); - e = mid_new_event(h); - e->flg = 0; - e->fx = prog; - e->fxparam = pat_gmtosmp(pr + 1); - mid_add_event(h, tr, e); - } -} - -// ===================================================================================== -static void mid_all_notes_off(MIDHANDLE *h, int mch) -// ===================================================================================== -{ - MIDTRACK *tr; - if( h->debug ) printf("%ld %d all notes off\n",(long)(h->tracktime), mch+1); - for( tr=h->track; tr; tr=tr->next ) { - if( tr->chan == mch || mch == -1 ) { - mid_sync_track(tr, h->tracktime); - if( tr->vpos != 0xff ) - mid_add_noteoff(h, tr); - } - } -} - -#ifndef NEWMIKMOD -static void mid_add_sync(MIDHANDLE *h, MIDTRACK *tp) -{ - MIDEVENT *e; - e = mid_new_event(h); - e->flg = 0; - e->fx = fxsync; - mid_add_event(h, tp, e); -} -#endif - -static BYTE mid_to_mod_wheel(unsigned int midwheel) -{ - unsigned int i; - if( midwheel == 0 ) return 0; - i = midwheel >> WHEELSHIFT; - return i+1; -} - -static void mid_add_wheel(MIDHANDLE *h, MIDTRACK *tp, int wheel) -{ - MIDEVENT *e; - e = mid_new_event(h); - e->flg = 0; - if( wheel < 0 ) { - e->fx = wheeldown; - e->fxparam = mid_to_mod_wheel(-wheel); - } - else { - e->fx = wheelup; - e->fxparam = mid_to_mod_wheel(wheel); - } - mid_add_event(h, tp, e); -} - -static void mid_add_pitchwheel(MIDHANDLE *h, int mch, int wheel) -{ - MIDTRACK *tr; - int hits; - hits = 0; - for( tr=h->track; tr; tr=tr->next ) { - if( tr->chan == mch ) { - hits++; - mid_sync_track(tr, h->tracktime); - if( tr->vpos != 0xff ) // only on tracks with notes on... - mid_add_wheel(h, tr, wheel); - } - } - if( !hits ) { // special case in midiformat 1 events in first track... - tr = mid_locate_track(h, mch, 0xff); - mid_add_wheel(h, tr, wheel); - } -} - -static long int mid_read_long(MIDHANDLE *h) -{ - BYTE buf[4]; - mmreadUBYTES(buf, 4, h->mmf); - return (buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|buf[3]; -} - -static short int mid_read_short(MIDHANDLE *h) -{ - BYTE buf[2]; - mmreadUBYTES(buf, 2, h->mmf); - return (buf[0]<<8)|buf[1]; -} - -static BYTE mid_read_byte(MIDHANDLE *h) -{ - return mmreadUBYTE(h->mmf); -} - -static int mid_read_delta(MIDHANDLE *h) -{ - BYTE bits; - int i, d; - d = 0; - for( i=0; i<4; ) { - bits = mid_read_byte(h); - i++; - d = (d<<7)|(bits&0x7f); - if( !(bits & 0x80) ) - break; - } - h->deltatime = d; - return i; -} - -// ===================================================================================== -#ifdef NEWMIKMOD -BOOL MID_Test(MMSTREAM *mmfile) -#else -BOOL CSoundFile::TestMID(const BYTE *lpStream, DWORD dwMemLength) -#endif -// ===================================================================================== -{ - char id[5]; - MIDHANDLE h; -#ifdef NEWMIKMOD - h.mmf = mmfile; -#else - MMFILE mm; - mm.mm = (char *)lpStream; - mm.sz = dwMemLength; - h.mmf = &mm; -#endif - mmfseek(h.mmf,0,SEEK_SET); - mmreadSBYTES(id, 4, h.mmf); - id[4] = '\0'; - return !strcmp(id,"MThd") && mid_read_long(&h) == 6; -} - -// ===================================================================================== -static MIDHANDLE *MID_Init(void) -{ - MIDHANDLE *retval; -#ifdef NEWMIKMOD - MM_ALLOC *allochandle; - - allochandle = _mmalloc_create("Load_MID", NULL); - retval = (MIDHANDLE *)_mm_calloc(allochandle, 1,sizeof(MIDHANDLE)); - if( !retval ) return NULL; - retval->allochandle = allochandle; - allochandle = _mmalloc_create("Load_ABC_tracks", NULL); - retval->trackhandle = allochandle; -#else - retval = (MIDHANDLE *)calloc(1,sizeof(MIDHANDLE)); - if( !retval ) return NULL; -#endif - retval->track = NULL; - retval->percussion = 0; - retval->debug = NULL; - return retval; -} - -#ifndef NEWMIKMOD -static void MID_CleanupTrack(MIDTRACK *tp) -{ - MIDEVENT *ep, *en; - if( tp ) { - for( ep=tp->head; ep; ep = en ) { - en=ep->next; - free(ep); - } - tp->head = NULL; - } -} -#endif - -// ===================================================================================== -static void MID_CleanupTracks(MIDHANDLE *handle) -// ===================================================================================== -{ -#ifdef NEWMIKMOD - if(handle && handle->trackhandle) { - _mmalloc_close(handle->trackhandle); - handle->trackhandle = 0; - } -#else - MIDTRACK *tp, *tn; - if(handle) { - for( tp=handle->track; tp; tp = tn ) { - tn=tp->next; - MID_CleanupTrack(tp); - } - handle->track = NULL; - } -#endif -} - -// ===================================================================================== -static void MID_Cleanup(MIDHANDLE *handle) -// ===================================================================================== -{ -#ifdef NEWMIKMOD - if(handle && handle->allochandle) { - MID_CleanupTracks(handle); - _mmalloc_close(handle->allochandle); - handle->allochandle = 0; - } -#else - if(handle) { - MID_CleanupTracks(handle); - free(handle); - handle = 0; - } -#endif -} - -static int mid_is_global_event(MIDEVENT *e) -{ - return (e->fx == tmpo || e->fx == fxbrk); -} - -static MIDEVENT *mid_next_global(MIDEVENT *e) -{ - for( ; e && !mid_is_global_event(e); e=e->next ) ; - return e; -} - -static MIDEVENT *mid_next_fx(MIDEVENT *e) -{ - for( ; e && e->fx == none; e=e->next ) ; - return e; -} - -static int mid_is_note_event(MIDEVENT *e) -{ -#ifdef LOOPED_NOTES_OFF - return (e->flg == 0); -#else - if( e->flg == 0 ) return 0; - if( e->volume ) return 1; - return pat_smplooped(e->smpno); // let non looping samples die out... -#endif -} - -static MIDEVENT *mid_next_note(MIDEVENT *e) -{ - for( ; e && !mid_is_note_event(e); e=e->next ) ; - return e; -} - -// ===================================================================================== -#ifdef NEWMIKMOD -static void MID_ReadPatterns(UNIMOD *of, MIDHANDLE *h, int numpat) -// ===================================================================================== -{ - int pat,row,i,ch,trkset; - BYTE n,ins,vol; - MIDTRACK *t; - MIDEVENT *e, *en, *ef, *el; - ULONG tt1, tt2; - UNITRK_EFFECT eff; - - // initialize start points of event list in tracks - for( t = h->track; t; t = t->next ) t->workevent = t->head; - for( pat = 0; pat < numpat; pat++ ) { - utrk_reset(of->ut); - for( row = 0; row < 64; row++ ) { - tt1 = miditicks(h, (pat * 64 + row ) * h->speed); - tt2 = tt1 + h->midispeed; - for( e=mid_next_global(h->track->workevent); e && e->tracktick < tt2; e=mid_next_global(e->next) ) { - if( e && e->tracktick >= tt1 ) { // we have a controller event in this row - switch( e->fx ) { - case tmpo: - eff.effect = UNI_GLOB_TEMPO; - eff.param.u = e->fxparam; - eff.framedly = UFD_RUNONCE; - utrk_write_global(of->ut, &eff, PTMEM_TEMPO); - break; - case fxbrk: - eff.effect = UNI_GLOB_PATBREAK; - eff.param.u = 0; - eff.framedly = UFD_RUNONCE; - utrk_write_global(of->ut, &eff, UNIMEM_NONE); - break; - } - } - } - ch = 0; - for( t = h->track; t; t = t->next ) { - trkset = 0; - e = NULL; - for( el=mid_next_fx(t->workevent); el && el->tracktick < tt2; el=mid_next_fx(el->next) ) { - if( el && el->tracktick >= tt1 ) { - switch( el->fx ) { - case modwheel: - case wheelup: - case wheeldown: - e = el; - default: - break; - } - } - } - if( e ) { // we have a controller event in this row - switch( e->fx ) { - case modwheel: - if( !trkset ) { - utrk_settrack(of->ut, ch); - trkset = 1; - } - eff.effect = UNI_VOLSLIDE; - eff.framedly = UFD_RUNONCE; - if( (e->fxparam & 0x0f) == 0x0f ) - eff.param.s = (e->fxparam >> 3)&0x1f; - else - eff.param.s = -((e->fxparam & 0x0f)*2); - utrk_write_local(of->ut, &eff, STMEM_VOLSLIDE); - break; - case wheelup: - if( !trkset ) { - utrk_settrack(of->ut, ch); - trkset = 1; - } - eff.effect = UNI_PITCHSLIDE; - eff.framedly = UFD_RUNONCE; - eff.param.s = e->fxparam; - utrk_write_local(of->ut, &eff, STMEM_PITCHSLIDE); - break; - case wheeldown: - if( !trkset ) { - utrk_settrack(of->ut, ch); - trkset = 1; - } - eff.effect = UNI_PITCHSLIDE; - eff.framedly = UFD_RUNONCE; - eff.param.s = -(int)(e->fxparam); - utrk_write_local(of->ut, &eff, STMEM_PITCHSLIDE); - break; - } - } - for( e=mid_next_note(t->workevent); e && e->tracktick < tt1; e=mid_next_note(e->next) ) - t->workevent = e; - i = 0; - ef = NULL; - en = e; - el = e; - for( ; e && e->tracktick < tt2; e=mid_next_note(e->next) ) { // we have a note event in this row - t->workevent = e; - i++; - if( e->volume ) { - if( !ef ) ef = e; - el = e; - } - } - if( i ) { - if( !trkset ) { - utrk_settrack(of->ut, ch); - trkset = 1; - } - if( i == 1 || ef == el || !ef ) { // only one event in this row - if( ef ) e = ef; - else e = en; - el = t->workevent; - n = pat_modnote(e->note); - ins = e->smpno; - eff.framedly = modticks(h, e->tracktick - tt1); - eff.param.u = 0; - eff.param.byte_a = n; - eff.param.byte_b = ins; - vol = e->volume; - if( vol == 0 ) { - eff.effect = UNI_NOTEKILL; - utrk_write_local(of->ut, &eff, UNIMEM_NONE); - } - else { - if( el->volume == 0 ) { - eff.framedly = modticks(h, el->tracktick - tt1); - eff.effect = UNI_NOTEKILL; - utrk_write_local(of->ut, &eff, UNIMEM_NONE); - } - else { - if( eff.framedly ) { - eff.effect = UNI_NOTEDELAY; - utrk_write_local(of->ut, &eff, UNIMEM_NONE); - } - } - } - utrk_write_inst(of->ut, ins); - utrk_write_note(of->ut, n); // <- normal note - pt_write_effect(of->ut, 0xc, vol); - } - else { - // two notes in one row, use FINEPITCHSLIDE runonce effect - // start first note on first tick and framedly runonce on seconds note tick - // use volume and instrument of last note - n = pat_modnote(ef->note); - i = pat_modnote(el->note); - ins = el->smpno; - vol = el->volume; - eff.effect = UNI_PITCHSLIDE; - eff.framedly = modticks(h, el->tracktick - tt1)|UFD_RUNONCE; - eff.param.s = ((i > n)?i-n:n-i); - utrk_write_inst(of->ut, ins); - utrk_write_note(of->ut, n); // <- normal note - pt_write_effect(of->ut, 0xc, vol); - utrk_write_local(of->ut, &eff, (i > n)? PTMEM_PITCHSLIDEUP: PTMEM_PITCHSLIDEDN); - } - } - ch++; - } - utrk_newline(of->ut); - } - if(!utrk_dup_pattern(of->ut,of)) return; - } -} - -#else - -static int MID_ReadPatterns(MODCOMMAND *pattern[], WORD psize[], MIDHANDLE *h, int numpat, int channels) -// ===================================================================================== -{ - int pat,row,i,ch; - BYTE n,ins,vol; - MIDTRACK *t; - MIDEVENT *e, *en, *ef, *el; - ULONG tt1, tt2; - MODCOMMAND *m; - int patbrk, tempo; - if( numpat > MAX_PATTERNS ) numpat = MAX_PATTERNS; - - // initialize start points of event list in tracks - for( t = h->track; t; t = t->next ) t->workevent = t->head; - for( pat = 0; pat < numpat; pat++ ) { - pattern[pat] = CSoundFile::AllocatePattern(64, channels); - if( !pattern[pat] ) return 0; - psize[pat] = 64; - for( row = 0; row < 64; row++ ) { - tt1 = miditicks(h, (pat * 64 + row ) * h->speed); - tt2 = tt1 + h->midispeed; - ch = 0; - tempo = 0; - patbrk = 0; - for( e=mid_next_global(h->track->workevent); e && e->tracktick < tt2; e=mid_next_global(e->next) ) { - if( e && e->tracktick >= tt1 ) { // we have a controller event in this row - switch( e->fx ) { - case tmpo: - tempo = e->fxparam; - break; - case fxbrk: - patbrk = 1; - break; - } - } - } - for( t = h->track; t; t = t->next ) { - m = &pattern[pat][row * channels + ch]; - m->param = 0; - m->command = CMD_NONE; - for( e=mid_next_fx(t->workevent); e && e->tracktick < tt2; e=mid_next_fx(e->next) ) { - if( e && e->tracktick >= tt1 ) { // we have a controller event in this row - switch( e->fx ) { - case modwheel: - m->param = e->fxparam; - m->command = CMD_VOLUMESLIDE; - break; - case wheelup: - m->param = e->fxparam|0x10; - m->command = CMD_XFINEPORTAUPDOWN; - break; - case wheeldown: - m->param = e->fxparam|0x20; - m->command = CMD_XFINEPORTAUPDOWN; - break; - } - } - } - for( e=mid_next_note(t->workevent); e && e->tracktick < tt1; e=mid_next_note(e->next) ) - t->workevent = e; - i = 0; - ef = NULL; - en = e; - el = e; - for( ; e && e->tracktick < tt2; e=mid_next_note(e->next) ) { // we have a note event in this row - t->workevent = e; - i++; - if( e->volume ) { - if( !ef ) ef = e; - el = e; - } - } - if( i ) { - if( i == 1 || ef == el || !ef ) { // only one event in this row or a note on with some note off - if( ef ) e = ef; - else e = en; - el = t->workevent; - n = pat_modnote(e->note); - ins = e->smpno; - if( e->volume == 0 ) { - m->param = modticks(h, e->tracktick - tt1); - if( m->param ) { // note cut - m->command = CMD_S3MCMDEX; - m->param |= 0xC0; - } - else { - m->param = 0; - m->command = CMD_KEYOFF; - } - vol = 0; - } - else { - vol = e->volume/2; - if( el->volume == 0 ) { - m->param = modticks(h, el->tracktick - tt1); - if( m->param ) { // note cut - m->command = CMD_S3MCMDEX; - m->param |= 0xC0; - } - } - else { - m->param = modticks(h, e->tracktick - tt1); - if( m->param ) { // note delay - m->command = CMD_S3MCMDEX; - m->param |= 0xD0; - } - } - } - m->instr = ins; - m->note = n; // <- normal note - m->volcmd = VOLCMD_VOLUME; - m->vol = vol; - } - else { - // two notes in one row, use FINEPITCHSLIDE runonce effect - // start first note on first tick and framedly runonce on seconds note tick - // use volume and instrument of last note - n = pat_modnote(ef->note); - i = pat_modnote(el->note); - ins = el->smpno; - vol = el->volume/2; - if( vol > 64 ) vol = 64; - m->instr = ins; - m->note = n; // <- normal note - m->volcmd = VOLCMD_VOLUME; - m->vol = vol; - m->param = ((i > n)?i-n:n-i); - if( m->param < 16 ) { - if( m->param ) { - m->command = CMD_XFINEPORTAUPDOWN; - m->param |= (i > n)? 0x10: 0x20; - } - else { // retrigger same note... - m->command = CMD_RETRIG; - m->param = modticks(h, el->tracktick - tt1); - } - } - else - m->command = (i > n)? CMD_PORTAMENTOUP: CMD_PORTAMENTODOWN; - } - } - if( m->param == 0 && m->command == CMD_NONE ) { - if( tempo ) { - m->command = CMD_TEMPO; - m->param = tempo; - tempo = 0; - } - else { - if( patbrk ) { - m->command = CMD_PATTERNBREAK; - patbrk = 0; - } - } - } - ch++; - } - if( tempo || patbrk ) return 1; - } - } - return 0; -} - -#endif - -static ULONG mid_next_tracktick(MIDEVENT *e) -{ - MIDEVENT *en; - en = e->next; - if( en ) return en->tracktick; - return 0x7fffffff; // practically indefinite -} - -// cut off alle events that follow the given event -static void mid_stripoff(MIDTRACK *tp, MIDEVENT *e) -{ -#ifndef NEWMIKMOD - MIDEVENT *ep, *en; - for( ep=e->next; ep; ep = en ) { - en=ep->next; - free(ep); - } -#endif - e->next = NULL; - tp->tail = e; - tp->workevent = tp->head; - mid_sync_track(tp, e->tracktick); -} - -static void mid_notes_to_percussion(MIDTRACK *tp, ULONG adjust, ULONG tmin) -{ - MIDEVENT *e, *lno = 0; - int n = 0,v; - ULONG ton, toff = 0, tnext; - v = 0x7f; // as loud as it gets - ton = 0; - for( e=tp->head; e; e=e->next ) { - if( e->tracktick < adjust ) e->tracktick = 0; - else e->tracktick -= adjust; - if( e->flg == 1 ) { - if( e->volume > 0 ) { - n = e->note; - e->smpno = pat_gmtosmp(pat_gm_drumnr(n)); - e->note = pat_gm_drumnote(n); - e->volume = (v * e->volume) / 128; - if( v && !e->volume ) e->volume = 1; - ton = e->tracktick; - } - else { - toff = ton + tmin; - if( toff > e->tracktick ) { - tnext = mid_next_tracktick(e); - if( toff + tmin < tnext ) e->tracktick = toff; - else { - if( toff < tnext ) e->tracktick = toff - 1; - else e->tracktick = tnext - 1; - } - } - toff = e->tracktick; - lno = e; - } - } - else { - if( e->fx == mainvol ) { - v = e->fxparam; - if( !v && ton > toff ) { - e->flg = 1; - e->volume = 0; - e->note = pat_gm_drumnote(n); - toff = e->tracktick; - lno = e; - } - } - } - } - if( ton > toff ) { - char info[32]; - sprintf(info,"%ld > %ld note %d", (long)ton, (long)toff, n); - mid_message("drum track ends with note on (%s)", info); - } - if( lno && lno->next ) mid_stripoff(tp, lno); -} - -static void mid_prog_to_notes(MIDTRACK *tp, ULONG adjust, ULONG tmin) -{ - MIDEVENT *e, *lno = 0; - int i = 0, n = 0, v = 0x7f; - ULONG ton, toff = 0, tnext; - ton = 0; - for( e=tp->head; e; e=e->next ) { - if( e->tracktick < adjust ) e->tracktick = 0; - else e->tracktick -= adjust; - if( e->flg == 1 ) { - if( !i ) i = pat_gmtosmp(1); // happens in eternal2.mid - e->smpno = i; - n = e->note; - if( e->volume > 0 ) { - e->volume = (v * e->volume) / 128; - if( v && !e->volume ) e->volume = 1; - ton = e->tracktick; - } - else { - toff = ton + tmin; - if( toff > e->tracktick ) { - tnext = mid_next_tracktick(e); - if( toff + tmin < tnext ) e->tracktick = toff; - else { - if( toff < tnext ) e->tracktick = toff - 1; - else e->tracktick = tnext - 1; - } - } - toff = e->tracktick; - lno = e; - } - } - else { - if( e->fx == prog ) i = e->fxparam; - if( e->fx == mainvol ) { - v = e->fxparam; - if( !v && ton > toff ) { - e->flg = 1; - e->volume = 0; - e->note = n; - toff = e->tracktick; - lno = e; - } - } - } - } - if( ton > toff ) { - char info[40]; - sprintf(info,"channel %d, %ld > %ld note %d", tp->chan + 1, (long)ton, (long)toff, n); - mid_message("melody track ends with note on (%s)", info); - } - if( lno && lno->next ) mid_stripoff(tp, lno); -} - -static int midiword(BYTE *b) -{ - int i; - i = (b[0]&0x7f)|((b[1]&0x7f)<<7); - return i; -} - -static int midishort(BYTE *b) -{ - return midiword(b) - 0x2000; -} - -ULONG mid_first_noteonevent_tick(MIDEVENT *e) -{ - while( e && (e->flg == 0 || e->volume == 0) ) e=e->next; - if( !e ) return 0x7fffffff; - return e->tracktick; -} - -// ===================================================================================== -#ifdef NEWMIKMOD -BOOL MID_Load(MIDHANDLE *h, UNIMOD *of, MMSTREAM *mmfile) -#else -BOOL CSoundFile::ReadMID(const BYTE *lpStream, DWORD dwMemLength) -#endif -{ - static int avoid_reentry = 0; -#ifdef NEWMIKMOD -#define m_nDefaultTempo of->inittempo -#else - MIDHANDLE *h; - MMFILE mm; -#endif - int ch, dmulti, maxtempo, panlow, panhigh, numchans, numtracks; - MIDTRACK *ttp; - uint t, numpats; - char buf[256]; - long miditracklen; - BYTE runningstatus; - BYTE cmd; - BYTE midibyte[2]; - long metalen, delta; - BYTE *p; - while( avoid_reentry ) sleep(1); - avoid_reentry = 1; -#ifdef NEWMIKMOD - h->mmf = mmfile; -#else - if( !TestMID(lpStream, dwMemLength) ) { - avoid_reentry = 0; - return FALSE; - } - h = MID_Init(); - if( !h ) { - avoid_reentry = 0; - return FALSE; - } - h->mmf = &mm; - mm.mm = (char *)lpStream; - mm.sz = dwMemLength; - mm.pos = 0; -#endif - h->debug = getenv(ENV_MMMID_DEBUG); - h->verbose = getenv(ENV_MMMID_VERBOSE); - pat_resetsmp(); - pat_init_patnames(); - mmfseek(h->mmf,8,SEEK_SET); - h->midiformat = mid_read_short(h); - h->miditracks = mid_read_short(h); - h->resolution = mid_read_short(h); - // at this point the h->mmf is positioned at first miditrack - if( h->midiformat == 0 ) h->miditracks = 1; - if( h->resolution & 0x8000 ) - h->divider = ((h->resolution & 0x7f00)>>8)*(h->resolution & 0xff); - else - h->divider = h->resolution; - h->divider <<= 2; // ticks per quartnote ==> ticks per note - h->tempo = 122; - m_nDefaultTempo = 0; - h->tracktime = 0; - h->speed = 6; - p = (BYTE *)getenv(ENV_MMMID_SPEED); - if( p && isdigit(*p) && p[0] != '0' && p[1] == '\0' ) { - // transform speed - t = *p - '0'; - h->speed *= t; - h->divider *= t; - h->speed /= 6; - h->divider /= 6; - } - // calculate optimal delta multiplier dmulti keeping tempo adjustments - // from 10 to 255 in mind (hoping there will be no midi's with tempo's - // lower than 10, that is sooo sick...) - // this is necessary for the tracks to patterns routine - dmulti = 1; - maxtempo = h->divider; - while( (h->midispeed = miditicks(h, h->speed)) * 10 < 255 * h->speed ) { - ++dmulti; - h->divider = maxtempo * dmulti; - } - h->tp = NULL; - memset(buf,0,sizeof(buf)); -#ifdef NEWMIKMOD - of->songname = NULL; -#else - strcpy(m_szNames[0], ""); -#endif - maxtempo = 0; - panlow = 64; - panhigh = 64; - if( h->verbose ) { - printf("Scanning MIDI with format: %d resolution: %d tracks: %d\n", - h->midiformat, - h->resolution, - h->miditracks); - } - if( h->verbose && dmulti > 1 ) { - printf("Multiplying resolution and deltatimes by %d to get %d miditicks per patternrow\n", - dmulti, h->midispeed); - } - for( t=0; t<(uint)h->miditracks; t++ ) { - if( h->verbose ) printf("Parsing track %d\n", t+1); - mmreadSBYTES(buf,4,h->mmf); - buf[4] = '\0'; - if( strcmp(buf,"MTrk") ) { - mid_message("invalid track-chunk '%s' is not 'MTrk'",buf); - avoid_reentry = 0; - return FALSE; - } - miditracklen = mid_read_long(h); - runningstatus = 0; - if( t && h->midiformat == 1 ) mid_rewind_tracks(h); // tracks sound simultaneously - while( miditracklen > 0 ) { - miditracklen -= mid_read_delta(h); - midibyte[0] = mid_read_byte(h); - miditracklen--; - if( midibyte[0] & 0x80 ) { - runningstatus = midibyte[0]; - switch( runningstatus ) { - case 0xf1: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - case 0xf8: - case 0xf9: - case 0xfa: - case 0xfb: - case 0xfc: - case 0xfd: - case 0xfe: - break; - default: - midibyte[0] = mid_read_byte(h); - miditracklen--; - break; - } - } - h->tracktime += dmulti * h->deltatime; - ch = runningstatus & 0x0f; - cmd = runningstatus & 0xf0; - switch( cmd ) { - case 0x80: // note off - midibyte[1] = mid_read_byte(h); - miditracklen--; - ttp = mid_find_track(h, ch, midibyte[0]); - if( ttp ) mid_add_noteoff(h, ttp); - if( h->debug ) - printf("%2d %08ld Note off: ch %d 0x%02x 0x%02x\n", - t, (long)(h->tracktime), - ch + 1, midibyte[0], midibyte[1]); - break; - case 0x90: // note on - midibyte[1] = mid_read_byte(h); - miditracklen--; - if( midibyte[1] ) { - ttp = mid_locate_track(h, ch, midibyte[0]); - mid_add_noteon(h, ttp, midibyte[0], midibyte[1]); - if( h->debug ) - printf("%2d %08ld Note on: ch %d 0x%02x 0x%02x\n", - t, (long)(h->tracktime), - ch + 1, midibyte[0], midibyte[1]); - } - else { - ttp = mid_find_track(h, ch, midibyte[0]); - if( ttp ) mid_add_noteoff(h, ttp); - if( h->debug ) - printf("%2d %08ld note off: ch %d 0x%02x\n", - t, (long)(h->tracktime), - ch + 1, midibyte[0]); - } - break; - case 0xa0: // polyphonic key pressure - midibyte[1] = mid_read_byte(h); - miditracklen--; - if( h->debug ) - printf("%2d %08ld polyphonic key pressure: ch %d 0x%02x 0x%02x\n", t, (long)(h->tracktime), ch + 1, midibyte[0], midibyte[1]); - break; - case 0xb0: // control change - midibyte[1] = mid_read_byte(h); - miditracklen--; - switch(midibyte[0]) { - case 0x01: // mod wheel - mid_mod_wheel(h, ch, midibyte[1]); - break; - case 0x07: // main volume - mid_main_volume(h, ch, midibyte[1]); - break; - case 0x0a: // pan - if( midibyte[1] < panlow ) panlow = midibyte[1]; - if( midibyte[1] > panhigh ) panhigh = midibyte[1]; - mid_pan(h, ch, midibyte[1]); - break; - case 0x0b: // expression - break; - case 0x7b: - if( midibyte[1] == 0x00 ) // all notes off - mid_all_notes_off(h, ch); - break; - default: - break; - } - if( h->debug ) - printf("%2d %08ld control change: ch %d 0x%02x 0x%02x\n", - t, (long)(h->tracktime), ch + 1, midibyte[0], midibyte[1]); - break; - case 0xc0: // program change - mid_add_program(h, ch, midibyte[0]); - if( h->debug ) - printf("%2d %08ld program change: ch %d %d\n", - t, (long)(h->tracktime), ch + 1, midibyte[0]); - break; - case 0xd0: // channel pressure - if( h->debug ) - printf("%2d %08ld channel pressure: ch %d 0x%02x\n", t, (long)(h->tracktime), ch + 1, midibyte[0]); - break; - case 0xe0: // pitch wheel change - midibyte[1] = mid_read_byte(h); - miditracklen--; - if( h->debug ) - printf("%2d %08ld pitch wheel change: ch %d %d\n", - t, (long)(h->tracktime), ch + 1, midishort(midibyte)); - mid_add_pitchwheel(h, ch, midishort(midibyte)); - break; - case 0xf0: // system & realtime - switch( runningstatus ) { - case 0xf0: // sysex - if( h->debug ) printf("%2d %08ld sysex: 0x%02x", - t, (long)(h->tracktime), midibyte[0]); - while( midibyte[0] != 0xf7 ) { - midibyte[0] = mid_read_byte(h); - miditracklen--; - if( h->debug ) printf(" %02X", midibyte[0]); - } - if( h->debug ) printf("\n"); - break; - case 0xf2: // song position pointer - midibyte[1] = mid_read_byte(h); - miditracklen--; - if( h->debug ) - printf("%2d %08ld song position pointer: %d", - t, (long)(h->tracktime), midishort(midibyte)); - break; - case 0xf7: - delta = h->deltatime; - miditracklen -= mid_read_delta(h); - metalen = h->deltatime; - if( h->debug ) - printf("%2d %08ld sysex continued: %ld", - t, (long)(h->tracktime), metalen); - while( metalen > 0 ) { - midibyte[1] = mid_read_byte(h); - metalen--; - miditracklen--; - if( h->debug ) printf(" %02X", midibyte[1]); - } - h->deltatime = delta; - break; - case 0xff: // meta event - delta = h->deltatime; - miditracklen -= mid_read_delta(h); - metalen = h->deltatime; - if( metalen > 31 ) metalen = 31; - if( metalen ) { - mmreadSBYTES(buf, metalen, h->mmf); - miditracklen -= metalen; - } - buf[metalen] = '\0'; - metalen = h->deltatime - metalen; - while( metalen > 0 ) { - midibyte[1] = mid_read_byte(h); - metalen--; - miditracklen--; - } - h->deltatime = delta; - switch( midibyte[0] ) { - case 0x03: // type: track name - if( h->debug ) - printf("%2d %08ld META trackname:%s\n", t, (long)(h->tracktime), buf); -#ifdef NEWMIKMOD - if( !of->songname ) - of->songname = DupStr(of->allochandle, buf, strlen(buf)); -#else - if( m_szNames[0][0] == '\0' ) - strcpy(m_szNames[0], buf); -#endif - break; - case 0x51: // type: tempo - p=(BYTE *)buf; - delta = (p[0]<<16)|(p[1]<<8)|p[2]; - if( delta ) - h->tempo = 60000000 / delta; - if( h->debug ) printf("%2d %08ld META tempo:%d\n", t, (long)(h->tracktime), h->tempo); - if( m_nDefaultTempo == 0 ) m_nDefaultTempo = h->tempo; - else { - ttp = h->track; - if( !ttp ) ttp = mid_locate_track(h, 0, 0xff); - mid_add_tempo_event(h,h->tempo); - } - if( h->tempo > maxtempo ) maxtempo = h->tempo; - break; - case 0x2f: // type: end of track - if( h->debug ) printf("%2d %08ld META end of track\n", t, (long)(h->tracktime)); - if( miditracklen > 0 ) { - sprintf(buf, "%ld", miditracklen); - mid_message("Meta event not at end of track, %s bytes left in track", buf); - miditracklen = 0; - } - break; - default: - if( h->debug ) printf("%2d %08ld META type 0x%02x\n", t, (long)(h->tracktime), midibyte[0]); - break; - } - break; - default: - if( h->debug ) printf("%2d %08ld System type 0x%02x\n", t, (long)(h->tracktime), midibyte[0]); - break; - } - break; - default: // no running status, just skip it... - if( h->debug ) printf("%2d %08ld unknown runningstatus: 0x%02x skipped:0x%02x\n", t, (long)(h->tracktime), runningstatus, midibyte[0]); - break; - } - if( miditracklen < 1 && (runningstatus != 0xff || midibyte[0] != 0x2f) ) { - delta = mmftell(h->mmf); - mmreadSBYTES(buf,4,h->mmf); - buf[4] = '\0'; - if( strcmp(buf,"MTrk") ) { - miditracklen = 0x7fffffff; - mid_message("Meta event not at end of track, %s bytes left in track", "superfluous"); - } - else - mid_message("Meta event not at end of track, %s bytes left in track", "no"); - mmfseek(h->mmf,delta,SEEK_SET); - } - } - } - if( h->verbose ) printf("Determining percussion channel\n"); - // get the lowest event time and the used channels - delta = 0x7fffffff; - metalen = 0; // use as bit bucket for used channels - for( ttp=h->track; ttp; ttp=ttp->next ) { - metalen |= (1<chan); - if( ttp->head ) { - ULONG tt; - tt = mid_first_noteonevent_tick(ttp->head); - if( tt < (ULONG)delta ) - delta = tt; - } - } - if( metalen & 0x03ff ) { - if( (metalen & 0x0f00) == 0x0400 ) - h->percussion = 10; // buggy sng2mid uses channel 10 - else - h->percussion = 9; - } - else h->percussion = 15; - if( h->verbose ) - printf("Percussion channel is %d\nStripping off silences and other optimalisations\n", h->percussion + 1); - // last but not least shut off all pending events, transform drumnotes when appropriate - // strip off silences at begin and end and get the greatest tracktime - h->tracktime = 0; - metalen = h->midispeed; - for( ttp=h->track; ttp; ttp=ttp->next ) { - if( ttp->chan == h->percussion ) - mid_notes_to_percussion(ttp, delta, metalen); - else - mid_prog_to_notes(ttp, delta, metalen); - if( ttp->tail && ttp->tail->tracktick > h->tracktime ) - h->tracktime = ttp->tail->tracktick; - } - h->tracktime += h->divider >> 2; // add one quartnote to the song for silence - mid_add_partbreak(h); - if( h->debug ) - mid_dump_tracks(h); - numchans = mid_numchans(h); - if( panlow > 48 || panhigh < 80 ) { - for( ttp=h->track; ttp; ttp=ttp->next ) { - ttp->balance = ((0x40*numchans+0x80*mid_ordchan(h, ttp->chan))/numchans)&0x7f; - } - } - // set module variables - numtracks = mid_numtracks(h); - if( m_nDefaultTempo == 0 ) m_nDefaultTempo = h->tempo; - if( maxtempo == 0 ) maxtempo = h->tempo; - if( maxtempo != 255 ) { - if( h->verbose ) printf("Adjusting tempo %d to 255\n", maxtempo); - mid_adjust_for_optimal_tempo(h, maxtempo); - } - if( maxtempo > 0 ) m_nDefaultTempo = (255 * m_nDefaultTempo) / maxtempo; - numpats = 1 + (modticks(h, h->tracktime) / h->speed / 64 ); - if( h->verbose ) printf("Generating %d patterns with speed %d\n", numpats, h->speed); -#ifdef NEWMIKMOD - if( !of->songname ) of->songname = DupStr(of->allochandle, "Untitled", 8); - of->memsize = STMEM_LAST; // Number of memory slots to reserve! - of->modtype = _mm_strdup(of->allochandle, MID_Version); - of->numpat = numpats; - of->numpos = of->numpat; - of->reppos = 0; - of->initspeed = h->speed; - of->numchn = numtracks; - of->numtrk = of->numpat * of->numchn; - of->initvolume = 64; - of->pansep = 128; - // orderlist - if(!AllocPositions(of, of->numpos)) { - avoid_reentry = 0; - return FALSE; - } - for(t=0; tnumpos; t++) - of->positions[t] = t; - if( !PAT_Load_Instruments(of) ) { - avoid_reentry = 0; - return FALSE; - } - // ============================== - // Load the pattern info now! - if(!AllocTracks(of)) { - avoid_reentry = 0; - return FALSE; - } - if(!AllocPatterns(of)) { - avoid_reentry = 0; - return FALSE; - } - of->ut = utrk_init(of->numchn, h->allochandle); - utrk_memory_reset(of->ut); - utrk_local_memflag(of->ut, PTMEM_PORTAMENTO, TRUE, FALSE); - MID_ReadPatterns(of, h, numpats); - // ============================================================ - // set panning positions - t = 0; - for( ttp=h->track; ttp; ttp=ttp->next ) { - of->panning[t] = modpan(ttp->balance, numchans / 2); - t++; - } -#else - m_nType = MOD_TYPE_MID; - m_nDefaultSpeed = h->speed; - m_nChannels = numtracks; - m_dwSongFlags = SONG_LINEARSLIDES; - m_nMinPeriod = 28 << 2; - m_nMaxPeriod = 1712 << 3; - // orderlist - for(t=0; t < numpats; t++) - Order[t] = t; - if( !PAT_Load_Instruments(this) ) { - avoid_reentry = 0; - return FALSE; - } - // ============================== - // Load the pattern info now! - if( MID_ReadPatterns(Patterns, PatternSize, h, numpats, m_nChannels) ) { - // :^( need one more channel to handle the global events ;^b - m_nChannels++; - h->tp = mid_new_track(h, h->track->chan, 0xff); - for( ttp=h->track; ttp->next; ttp=ttp->next ) ; - ttp->next = h->tp; - mid_add_sync(h, h->tp); - for( t=0; ttrack; ttp; ttp=ttp->next ) { - ChnSettings[t].nPan = modpan(ttp->balance, numchans / 2); - ChnSettings[t].nVolume = 64; - t++; - } - MID_Cleanup(h); // we dont need it anymore -#endif - if( h->verbose ) printf("Done\n"); - avoid_reentry = 0; // it is safe now, I'm finished - return TRUE; -} - -#ifdef NEWMIKMOD -// ===================================================================================== -CHAR *MID_LoadTitle(MMSTREAM *mmfile) -// ===================================================================================== -{ - int t; - char buf[24]; - long miditracklen; - BYTE runningstatus; - BYTE cmd; - BYTE midibyte[2]; - long metalen; - MIDHANDLE hh, *h; - h = &hh; - h->mmf = mmfile; - mmfseek(h->mmf,8,SEEK_SET); - h->midiformat = mid_read_short(h); - h->miditracks = mid_read_short(h); - h->resolution = mid_read_short(h); - // at this point the h->mmf is positioned at first miditrack - if( h->midiformat == 0 ) h->miditracks = 1; - h->tracktime = 0; - for( t=0; tmiditracks; t++ ) { - mmreadSBYTES(buf,4,h->mmf); - miditracklen = mid_read_long(h); - runningstatus = 0; - while( miditracklen > 0 ) { - miditracklen -= mid_read_delta(h); - midibyte[0] = mid_read_byte(h); - miditracklen--; - if( midibyte[0] & 0x80 ) { - runningstatus = midibyte[0]; - switch( runningstatus ) { - case 0xf1: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - case 0xf8: - case 0xf9: - case 0xfa: - case 0xfb: - case 0xfc: - case 0xfd: - case 0xfe: - break; - default: - midibyte[0] = mid_read_byte(h); - miditracklen--; - break; - } - } - cmd = runningstatus & 0xf0; - switch( cmd ) { - case 0x80: // note off - case 0x90: // note on - case 0xa0: // polyphonic key pressure - case 0xb0: // control change - case 0xe0: // pitch wheel change - midibyte[1] = mid_read_byte(h); - miditracklen--; - case 0xc0: // program change - case 0xd0: // channel pressure - break; - case 0xf0: // system & realtime - switch( runningstatus ) { - case 0xf0: // sysex - while( midibyte[0] != 0xf7 ) { - midibyte[0] = mid_read_byte(h); - miditracklen--; - } - break; - case 0xf2: // song position pointer - midibyte[1] = mid_read_byte(h); - miditracklen--; - break; - case 0xf7: - miditracklen -= mid_read_delta(h); - metalen = h->deltatime; - while( metalen > 0 ) { - midibyte[1] = mid_read_byte(h); - metalen--; - miditracklen--; - } - break; - case 0xff: // meta event - miditracklen -= mid_read_delta(h); - metalen = h->deltatime; - if( metalen > 21 ) metalen = 21; - if( metalen ) { - mmreadSBYTES(buf, metalen, h->mmf); - miditracklen -= metalen; - } - buf[metalen] = '\0'; - metalen = h->deltatime - metalen; - while( metalen > 0 ) { - midibyte[1] = mid_read_byte(h); - metalen--; - miditracklen--; - } - switch( midibyte[0] ) { - case 0x03: // type: track name - return DupStr(NULL, buf, strlen(buf)); - break; - case 0x2f: // type: end of track - miditracklen = 0; - break; - default: - break; - } - break; - default: - break; - } - break; - default: // no running status, just skip it... - break; - } - if( miditracklen < 1 && (runningstatus != 0xff || midibyte[0] != 0x2f) ) { - metalen = mmftell(h->mmf); - mmreadSBYTES(buf,4,h->mmf); - buf[4] = '\0'; - if( strcmp(buf,"MTrk") ) miditracklen = 0x7fffffff; - mmfseek(h->mmf,metalen,SEEK_SET); - } - } - } - return DupStr(NULL, "Untitled" ,8); -} - -MLOADER load_mid = -{ - "MID", - "Musical Instrument Digital Interface", - 0x30, - NULL, - MID_Test, - (void *(*)(void))MID_Init, - (void (*)(ML_HANDLE *))MID_Cleanup, - /* Every single loader seems to need one of these! */ - (BOOL (*)(ML_HANDLE *, UNIMOD *, MMSTREAM *))MID_Load, - MID_LoadTitle -}; -#endif diff --git a/src/modplug/load_mod.cpp b/src/modplug/load_mod.cpp index 38cdb626..ad9b6dda 100644 --- a/src/modplug/load_mod.cpp +++ b/src/modplug/load_mod.cpp @@ -200,11 +200,13 @@ BOOL CSoundFile::ReadMod(const BYTE *lpStream, DWORD dwMemLength) if (IsMagic(s,"16CN")) m_nChannels = 16; else if (IsMagic(s,"32CN")) m_nChannels = 32; else { + // Todo: Check the header for proper fields and let it pass if they seem to contain sensible data + + m_nSamples = 15; + // don't accept any unknown magics. If we did this the code would play all garbage that's - // thrown at it - //if (s[0] || s[1] || s[2] || s[3]) - return FALSE; - //m_nSamples = 15; + // thrown at it. + return FALSE; } // Load Samples nErr = 0; diff --git a/src/modplug/load_mt2.cpp b/src/modplug/load_mt2.cpp deleted file mode 100644 index 563839f1..00000000 --- a/src/modplug/load_mt2.cpp +++ /dev/null @@ -1,635 +0,0 @@ -#include "stdafx.h" -#include "sndfile.h" - -//#define MT2DEBUG - -#pragma pack(1) - -typedef struct _MT2FILEHEADER -{ - DWORD dwMT20; // 0x3032544D "MT20" - DWORD dwSpecial; - WORD wVersion; - CHAR szTrackerName[32]; // "MadTracker 2.0" - CHAR szSongName[64]; - WORD nOrders; - WORD wRestart; - WORD wPatterns; - WORD wChannels; - WORD wSamplesPerTick; - BYTE bTicksPerLine; - BYTE bLinesPerBeat; - DWORD fulFlags; // b0=packed patterns - WORD wInstruments; - WORD wSamples; - BYTE Orders[256]; -} MT2FILEHEADER; - -typedef struct _MT2PATTERN -{ - WORD wLines; - DWORD wDataLen; -} MT2PATTERN; - -typedef struct _MT2COMMAND -{ - BYTE note; // 0=nothing, 97=note off - BYTE instr; - BYTE vol; - BYTE pan; - BYTE fxcmd; - BYTE fxparam1; - BYTE fxparam2; -} MT2COMMAND; - -typedef struct _MT2DRUMSDATA -{ - WORD wDrumPatterns; - WORD wDrumSamples[8]; - BYTE DrumPatternOrder[256]; -} MT2DRUMSDATA; - -typedef struct _MT2AUTOMATION -{ - DWORD dwFlags; - DWORD dwEffectId; - DWORD nEnvPoints; -} MT2AUTOMATION; - -typedef struct _MT2INSTRUMENT -{ - CHAR szName[32]; - DWORD dwDataLen; - WORD wSamples; - BYTE GroupsMapping[96]; - BYTE bVibType; - BYTE bVibSweep; - BYTE bVibDepth; - BYTE bVibRate; - WORD wFadeOut; - WORD wNNA; - WORD wInstrFlags; - WORD wEnvFlags1; - WORD wEnvFlags2; -} MT2INSTRUMENT; - -typedef struct _MT2ENVELOPE -{ - BYTE nFlags; - BYTE nPoints; - BYTE nSustainPos; - BYTE nLoopStart; - BYTE nLoopEnd; - BYTE bReserved[3]; - BYTE EnvData[64]; -} MT2ENVELOPE; - -typedef struct _MT2SYNTH -{ - BYTE nSynthId; - BYTE nFxId; - WORD wCutOff; - BYTE nResonance; - BYTE nAttack; - BYTE nDecay; - BYTE bReserved[25]; -} MT2SYNTH; - -typedef struct _MT2SAMPLE -{ - CHAR szName[32]; - DWORD dwDataLen; - DWORD dwLength; - DWORD dwFrequency; - BYTE nQuality; - BYTE nChannels; - BYTE nFlags; - BYTE nLoop; - DWORD dwLoopStart; - DWORD dwLoopEnd; - WORD wVolume; - BYTE nPan; - BYTE nBaseNote; - WORD wSamplesPerBeat; -} MT2SAMPLE; - -typedef struct _MT2GROUP -{ - BYTE nSmpNo; - BYTE nVolume; // 0-128 - BYTE nFinePitch; - BYTE Reserved[5]; -} MT2GROUP; - -#pragma pack() - - -static VOID ConvertMT2Command(CSoundFile *that, MODCOMMAND *m, MT2COMMAND *p) -//--------------------------------------------------------------------------- -{ - // Note - m->note = 0; - if (p->note) m->note = (p->note > 96) ? 0xFF : p->note+12; - // Instrument - m->instr = p->instr; - // Volume Column - if ((p->vol >= 0x10) && (p->vol <= 0x90)) - { - m->volcmd = VOLCMD_VOLUME; - m->vol = (p->vol - 0x10) >> 1; - } else - if ((p->vol >= 0xA0) && (p->vol <= 0xAF)) - { - m->volcmd = VOLCMD_VOLSLIDEDOWN; - m->vol = (p->vol & 0x0f); - } else - if ((p->vol >= 0xB0) && (p->vol <= 0xBF)) - { - m->volcmd = VOLCMD_VOLSLIDEUP; - m->vol = (p->vol & 0x0f); - } else - if ((p->vol >= 0xC0) && (p->vol <= 0xCF)) - { - m->volcmd = VOLCMD_FINEVOLDOWN; - m->vol = (p->vol & 0x0f); - } else - if ((p->vol >= 0xD0) && (p->vol <= 0xDF)) - { - m->volcmd = VOLCMD_FINEVOLUP; - m->vol = (p->vol & 0x0f); - } else - { - m->volcmd = 0; - m->vol = 0; - } - // Effects - m->command = 0; - m->param = 0; - if ((p->fxcmd) || (p->fxparam1) || (p->fxparam2)) - { - if (!p->fxcmd) - { - m->command = p->fxparam2; - m->param = p->fxparam1; - that->ConvertModCommand(m); - } else - { - // TODO: MT2 Effects - } - } -} - - -BOOL CSoundFile::ReadMT2(LPCBYTE lpStream, DWORD dwMemLength) -//----------------------------------------------------------- -{ - MT2FILEHEADER *pfh = (MT2FILEHEADER *)lpStream; - DWORD dwMemPos, dwDrumDataPos, dwExtraDataPos; - UINT nDrumDataLen, nExtraDataLen; - MT2DRUMSDATA *pdd; - MT2INSTRUMENT *InstrMap[255]; - MT2SAMPLE *SampleMap[256]; - - if ((!lpStream) || (dwMemLength < sizeof(MT2FILEHEADER)) - || (pfh->dwMT20 != 0x3032544D) - || (pfh->wVersion < 0x0200) || (pfh->wVersion >= 0x0300) - || (pfh->wChannels < 4) || (pfh->wChannels > 64)) return FALSE; - pdd = NULL; - m_nType = MOD_TYPE_MT2; - m_nChannels = pfh->wChannels; - m_nRestartPos = pfh->wRestart; - m_nDefaultSpeed = pfh->bTicksPerLine; - m_nDefaultTempo = 125; - if ((pfh->wSamplesPerTick > 100) && (pfh->wSamplesPerTick < 5000)) - { - m_nDefaultTempo = 110250 / pfh->wSamplesPerTick; - } - for (UINT iOrd=0; iOrdnOrders) ? pfh->Orders[iOrd] : 0xFF); - } - memcpy(m_szNames[0], pfh->szSongName, 32); - m_szNames[0][31] = 0; - dwMemPos = sizeof(MT2FILEHEADER); - nDrumDataLen = *(WORD *)(lpStream + dwMemPos); - dwDrumDataPos = dwMemPos + 2; - if (nDrumDataLen >= 2) pdd = (MT2DRUMSDATA *)(lpStream+dwDrumDataPos); - dwMemPos += 2 + nDrumDataLen; -#ifdef MT2DEBUG - - Log("MT2 v%03X: \"%s\" (flags=%04X)\n", pfh->wVersion, m_szNames[0], pfh->fulFlags); - Log("%d Channels, %d Patterns, %d Instruments, %d Samples\n", pfh->wChannels, pfh->wPatterns, pfh->wInstruments, pfh->wSamples); - Log("Drum Data: %d bytes @%04X\n", nDrumDataLen, dwDrumDataPos); -#endif - if (dwMemPos >= dwMemLength-12) return TRUE; - if (!*(DWORD *)(lpStream+dwMemPos)) dwMemPos += 4; - if (!*(DWORD *)(lpStream+dwMemPos)) dwMemPos += 4; - nExtraDataLen = *(DWORD *)(lpStream+dwMemPos); - dwExtraDataPos = dwMemPos + 4; - dwMemPos += 4; -#ifdef MT2DEBUG - Log("Extra Data: %d bytes @%04X\n", nExtraDataLen, dwExtraDataPos); -#endif - if (dwMemPos + nExtraDataLen >= dwMemLength) return TRUE; - while (dwMemPos+8 < dwExtraDataPos + nExtraDataLen) - { - DWORD dwId = *(DWORD *)(lpStream+dwMemPos); - DWORD dwLen = *(DWORD *)(lpStream+dwMemPos+4); - dwMemPos += 8; - if (dwMemPos + dwLen > dwMemLength) return TRUE; -#ifdef MT2DEBUG - CHAR s[5]; - memcpy(s, &dwId, 4); - s[4] = 0; - Log("pos=0x%04X: %s: %d bytes\n", dwMemPos-8, s, dwLen); -#endif - switch(dwId) - { - // MSG - case 0x0047534D: - if ((dwLen > 3) && (!m_lpszSongComments)) - { - DWORD nTxtLen = dwLen; - if (nTxtLen > 32000) nTxtLen = 32000; - m_lpszSongComments = new char[nTxtLen]; // changed from CHAR - if (m_lpszSongComments) - { - memcpy(m_lpszSongComments, lpStream+dwMemPos+1, nTxtLen-1); - m_lpszSongComments[nTxtLen-1] = 0; - } - } - break; - // SUM -> author name (or "Unregistered") - // TMAP - // TRKS - case 0x534b5254: - break; - } - dwMemPos += dwLen; - } - // Load Patterns - dwMemPos = dwExtraDataPos + nExtraDataLen; - for (UINT iPat=0; iPatwPatterns; iPat++) if (dwMemPos < dwMemLength-6) - { - MT2PATTERN *pmp = (MT2PATTERN *)(lpStream+dwMemPos); - UINT wDataLen = (pmp->wDataLen + 1) & ~1; - dwMemPos += 6; - if (dwMemPos + wDataLen > dwMemLength) break; - UINT nLines = pmp->wLines; - if ((iPat < MAX_PATTERNS) && (nLines > 0) && (nLines <= 256)) - { - #ifdef MT2DEBUG - Log("Pattern #%d @%04X: %d lines, %d bytes\n", iPat, dwMemPos-6, nLines, pmp->wDataLen); - #endif - PatternSize[iPat] = nLines; - Patterns[iPat] = AllocatePattern(nLines, m_nChannels); - if (!Patterns[iPat]) return TRUE; - MODCOMMAND *m = Patterns[iPat]; - UINT len = wDataLen; - if (pfh->fulFlags & 1) // Packed Patterns - { - BYTE *p = (BYTE *)(lpStream+dwMemPos); - UINT pos = 0, row=0, ch=0; - while (pos < len) - { - MT2COMMAND cmd; - UINT infobyte = p[pos++]; - UINT rptcount = 0; - if (infobyte == 0xff) - { - rptcount = p[pos++]; - infobyte = p[pos++]; - #if 0 - Log("(%d.%d) FF(%02X).%02X\n", row, ch, rptcount, infobyte); - } else - { - Log("(%d.%d) %02X\n", row, ch, infobyte); - #endif - } - if (infobyte & 0x7f) - { - UINT patpos = row*m_nChannels+ch; - cmd.note = cmd.instr = cmd.vol = cmd.pan = cmd.fxcmd = cmd.fxparam1 = cmd.fxparam2 = 0; - if (infobyte & 1) cmd.note = p[pos++]; - if (infobyte & 2) cmd.instr = p[pos++]; - if (infobyte & 4) cmd.vol = p[pos++]; - if (infobyte & 8) cmd.pan = p[pos++]; - if (infobyte & 16) cmd.fxcmd = p[pos++]; - if (infobyte & 32) cmd.fxparam1 = p[pos++]; - if (infobyte & 64) cmd.fxparam2 = p[pos++]; - #ifdef MT2DEBUG - if (cmd.fxcmd) - { - Log("(%d.%d) MT2 FX=%02X.%02X.%02X\n", row, ch, cmd.fxcmd, cmd.fxparam1, cmd.fxparam2); - } - #endif - ConvertMT2Command(this, &m[patpos], &cmd); - } - row += rptcount+1; - while (row >= nLines) { row-=nLines; ch++; } - if (ch >= m_nChannels) break; - } - } else - { - MT2COMMAND *p = (MT2COMMAND *)(lpStream+dwMemPos); - UINT n = 0; - while ((len > sizeof(MT2COMMAND)) && (n < m_nChannels*nLines)) - { - ConvertMT2Command(this, m, p); - len -= sizeof(MT2COMMAND); - n++; - p++; - m++; - } - } - } - dwMemPos += wDataLen; - } - // Skip Drum Patterns - if (pdd) - { - #ifdef MT2DEBUG - Log("%d Drum Patterns at offset 0x%08X\n", pdd->wDrumPatterns, dwMemPos); - #endif - for (UINT iDrm=0; iDrmwDrumPatterns; iDrm++) - { - if (dwMemPos > dwMemLength-2) return TRUE; - UINT nLines = *(WORD *)(lpStream+dwMemPos); - #ifdef MT2DEBUG - if (nLines != 64) Log("Drum Pattern %d: %d Lines @%04X\n", iDrm, nLines, dwMemPos); - #endif - dwMemPos += 2 + nLines * 32; - } - } - // Automation - if (pfh->fulFlags & 2) - { - #ifdef MT2DEBUG - Log("Automation at offset 0x%08X\n", dwMemPos); - #endif - UINT nAutoCount = m_nChannels; - if (pfh->fulFlags & 0x10) nAutoCount++; // Master Automation - if ((pfh->fulFlags & 0x08) && (pdd)) nAutoCount += 8; // Drums Automation - nAutoCount *= pfh->wPatterns; - for (UINT iAuto=0; iAuto= dwMemLength) return TRUE; - MT2AUTOMATION *pma = (MT2AUTOMATION *)(lpStream+dwMemPos); - dwMemPos += (pfh->wVersion <= 0x201) ? 4 : 8; - for (UINT iEnv=0; iEnv<14; iEnv++) - { - if (pma->dwFlags & (1 << iEnv)) - { - #ifdef MT2DEBUG - UINT nPoints = *(DWORD *)(lpStream+dwMemPos); - Log(" Env[%d/%d] %04X @%04X: %d points\n", iAuto, nAutoCount, 1 << iEnv, dwMemPos-8, nPoints); - #endif - dwMemPos += 260; - } - } - } - } - // Load Instruments -#ifdef MT2DEBUG - Log("Loading instruments at offset 0x%08X\n", dwMemPos); -#endif - memset(InstrMap, 0, sizeof(InstrMap)); - m_nInstruments = (pfh->wInstruments < MAX_INSTRUMENTS) ? pfh->wInstruments : MAX_INSTRUMENTS-1; - for (UINT iIns=1; iIns<=255; iIns++) - { - if (dwMemPos+36 > dwMemLength) return TRUE; - MT2INSTRUMENT *pmi = (MT2INSTRUMENT *)(lpStream+dwMemPos); - INSTRUMENTHEADER *penv = NULL; - if (iIns <= m_nInstruments) - { - penv = new INSTRUMENTHEADER; - Headers[iIns] = penv; - if (penv) - { - memset(penv, 0, sizeof(INSTRUMENTHEADER)); - memcpy(penv->name, pmi->szName, 32); - penv->nGlobalVol = 64; - penv->nPan = 128; - for (UINT i=0; i<120; i++) - { - penv->NoteMap[i] = i+1; - } - } - } - #ifdef MT2DEBUG - if (iIns <= pfh->wInstruments) Log(" Instrument #%d at offset %04X: %d bytes\n", iIns, dwMemPos, pmi->dwDataLen); - #endif - if (((LONG)pmi->dwDataLen > 0) && (dwMemPos + pmi->dwDataLen + 40 <= dwMemLength)) - { - InstrMap[iIns-1] = pmi; - if (penv) - { - penv->nFadeOut = pmi->wFadeOut; - penv->nNNA = pmi->wNNA & 3; - penv->nDCT = (pmi->wNNA>>8) & 3; - penv->nDNA = (pmi->wNNA>>12) & 3; - MT2ENVELOPE *pehdr[4]; - WORD *pedata[4]; - if (pfh->wVersion <= 0x201) - { - DWORD dwEnvPos = dwMemPos + sizeof(MT2INSTRUMENT) - 4; - pehdr[0] = (MT2ENVELOPE *)(lpStream+dwEnvPos); - pehdr[1] = (MT2ENVELOPE *)(lpStream+dwEnvPos+8); - pehdr[2] = pehdr[3] = NULL; - pedata[0] = (WORD *)(lpStream+dwEnvPos+16); - pedata[1] = (WORD *)(lpStream+dwEnvPos+16+64); - pedata[2] = pedata[3] = NULL; - } else - { - DWORD dwEnvPos = dwMemPos + sizeof(MT2INSTRUMENT); - for (UINT i=0; i<4; i++) - { - if (pmi->wEnvFlags1 & (1<EnvData; - dwEnvPos += sizeof(MT2ENVELOPE); - } else - { - pehdr[i] = NULL; - pedata[i] = NULL; - } - } - } - // Load envelopes - for (UINT iEnv=0; iEnv<4; iEnv++) if (pehdr[iEnv]) - { - MT2ENVELOPE *pme = pehdr[iEnv]; - WORD *pEnvPoints = NULL; - BYTE *pEnvData = NULL; - #ifdef MT2DEBUG - Log(" Env %d.%d @%04X: %d points\n", iIns, iEnv, (UINT)(((BYTE *)pme)-lpStream), pme->nPoints); - #endif - switch(iEnv) - { - // Volume Envelope - case 0: - if (pme->nFlags & 1) penv->dwFlags |= ENV_VOLUME; - if (pme->nFlags & 2) penv->dwFlags |= ENV_VOLSUSTAIN; - if (pme->nFlags & 4) penv->dwFlags |= ENV_VOLLOOP; - penv->nVolEnv = (pme->nPoints > 16) ? 16 : pme->nPoints; - penv->nVolSustainBegin = penv->nVolSustainEnd = pme->nSustainPos; - penv->nVolLoopStart = pme->nLoopStart; - penv->nVolLoopEnd = pme->nLoopEnd; - pEnvPoints = penv->VolPoints; - pEnvData = penv->VolEnv; - break; - - // Panning Envelope - case 1: - if (pme->nFlags & 1) penv->dwFlags |= ENV_PANNING; - if (pme->nFlags & 2) penv->dwFlags |= ENV_PANSUSTAIN; - if (pme->nFlags & 4) penv->dwFlags |= ENV_PANLOOP; - penv->nPanEnv = (pme->nPoints > 16) ? 16 : pme->nPoints; - penv->nPanSustainBegin = penv->nPanSustainEnd = pme->nSustainPos; - penv->nPanLoopStart = pme->nLoopStart; - penv->nPanLoopEnd = pme->nLoopEnd; - pEnvPoints = penv->PanPoints; - pEnvData = penv->PanEnv; - break; - - // Pitch/Filter envelope - default: - if (pme->nFlags & 1) penv->dwFlags |= (iEnv==3) ? (ENV_PITCH|ENV_FILTER) : ENV_PITCH; - if (pme->nFlags & 2) penv->dwFlags |= ENV_PITCHSUSTAIN; - if (pme->nFlags & 4) penv->dwFlags |= ENV_PITCHLOOP; - penv->nPitchEnv = (pme->nPoints > 16) ? 16 : pme->nPoints; - penv->nPitchSustainBegin = penv->nPitchSustainEnd = pme->nSustainPos; - penv->nPitchLoopStart = pme->nLoopStart; - penv->nPitchLoopEnd = pme->nLoopEnd; - pEnvPoints = penv->PitchPoints; - pEnvData = penv->PitchEnv; - } - // Envelope data - if ((pEnvPoints) && (pEnvData) && (pedata[iEnv])) - { - WORD *psrc = pedata[iEnv]; - for (UINT i=0; i<16; i++) - { - pEnvPoints[i] = psrc[i*2]; - pEnvData[i] = (BYTE)psrc[i*2+1]; - } - } - } - } - dwMemPos += pmi->dwDataLen + 36; - if (pfh->wVersion > 0x201) dwMemPos += 4; // ? - } else - { - dwMemPos += 36; - } - } -#ifdef MT2DEBUG - Log("Loading samples at offset 0x%08X\n", dwMemPos); -#endif - memset(SampleMap, 0, sizeof(SampleMap)); - m_nSamples = (pfh->wSamples < MAX_SAMPLES) ? pfh->wSamples : MAX_SAMPLES-1; - for (UINT iSmp=1; iSmp<=256; iSmp++) - { - if (dwMemPos+36 > dwMemLength) return TRUE; - MT2SAMPLE *pms = (MT2SAMPLE *)(lpStream+dwMemPos); - #ifdef MT2DEBUG - if (iSmp <= m_nSamples) Log(" Sample #%d at offset %04X: %d bytes\n", iSmp, dwMemPos, pms->dwDataLen); - #endif - if (iSmp < MAX_SAMPLES) - { - memcpy(m_szNames[iSmp], pms->szName, 32); - } - if (pms->dwDataLen > 0) - { - SampleMap[iSmp-1] = pms; - if (iSmp < MAX_SAMPLES) - { - MODINSTRUMENT *psmp = &Ins[iSmp]; - psmp->nGlobalVol = 64; - psmp->nVolume = (pms->wVolume >> 7); - psmp->nPan = (pms->nPan == 0x80) ? 128 : (pms->nPan^0x80); - psmp->nLength = pms->dwLength; - psmp->nC4Speed = pms->dwFrequency; - psmp->nLoopStart = pms->dwLoopStart; - psmp->nLoopEnd = pms->dwLoopEnd; - FrequencyToTranspose(psmp); - psmp->RelativeTone -= pms->nBaseNote - 49; - psmp->nC4Speed = TransposeToFrequency(psmp->RelativeTone, psmp->nFineTune); - if (pms->nQuality == 2) { psmp->uFlags |= CHN_16BIT; psmp->nLength >>= 1; } - if (pms->nChannels == 2) { psmp->nLength >>= 1; } - if (pms->nLoop == 1) psmp->uFlags |= CHN_LOOP; - if (pms->nLoop == 2) psmp->uFlags |= CHN_LOOP|CHN_PINGPONGLOOP; - } - dwMemPos += pms->dwDataLen + 36; - } else - { - dwMemPos += 36; - } - } -#ifdef MT2DEBUG - Log("Loading groups at offset 0x%08X\n", dwMemPos); -#endif - for (UINT iMap=0; iMap<255; iMap++) if (InstrMap[iMap]) - { - if (dwMemPos+8 > dwMemLength) return TRUE; - MT2INSTRUMENT *pmi = InstrMap[iMap]; - INSTRUMENTHEADER *penv = NULL; - if (iMapwSamples; iGrp++) - { - if (penv) - { - MT2GROUP *pmg = (MT2GROUP *)(lpStream+dwMemPos); - for (UINT i=0; i<96; i++) - { - if (pmi->GroupsMapping[i] == iGrp) - { - UINT nSmp = pmg->nSmpNo+1; - penv->Keyboard[i+12] = (BYTE)nSmp; - if (nSmp <= m_nSamples) - { - Ins[nSmp].nVibType = pmi->bVibType; - Ins[nSmp].nVibSweep = pmi->bVibSweep; - Ins[nSmp].nVibDepth = pmi->bVibDepth; - Ins[nSmp].nVibRate = pmi->bVibRate; - } - } - } - } - dwMemPos += 8; - } - } -#ifdef MT2DEBUG - Log("Loading sample data at offset 0x%08X\n", dwMemPos); -#endif - for (UINT iData=0; iData<256; iData++) if ((iData < m_nSamples) && (SampleMap[iData])) - { - MT2SAMPLE *pms = SampleMap[iData]; - MODINSTRUMENT *psmp = &Ins[iData+1]; - if (!(pms->nFlags & 5)) - { - if (psmp->nLength > 0) - { - #ifdef MT2DEBUG - Log(" Reading sample #%d at offset 0x%04X (len=%d)\n", iData+1, dwMemPos, psmp->nLength); - #endif - UINT rsflags; - - if (pms->nChannels == 2) - rsflags = (psmp->uFlags & CHN_16BIT) ? RS_STPCM16D : RS_STPCM8D; - else - rsflags = (psmp->uFlags & CHN_16BIT) ? RS_PCM16D : RS_PCM8D; - - dwMemPos += ReadSample(psmp, rsflags, (LPCSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); - } - } else - if (dwMemPos+4 < dwMemLength) - { - UINT nNameLen = *(DWORD *)(lpStream+dwMemPos); - dwMemPos += nNameLen + 16; - } - if (dwMemPos+4 >= dwMemLength) break; - } - return TRUE; -} diff --git a/src/modplug/load_mtm.cpp b/src/modplug/load_mtm.cpp deleted file mode 100644 index f5f0299e..00000000 --- a/src/modplug/load_mtm.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque -*/ - -#include "stdafx.h" -#include "sndfile.h" - -//#pragma warning(disable:4244) - -////////////////////////////////////////////////////////// -// MTM file support (import only) - -#pragma pack(1) - - -typedef struct tagMTMSAMPLE -{ - char samplename[22]; // changed from CHAR - DWORD length; - DWORD reppos; - DWORD repend; - CHAR finetune; - BYTE volume; - BYTE attribute; -} MTMSAMPLE; - - -typedef struct tagMTMHEADER -{ - char id[4]; // MTM file marker + version // changed from CHAR - char songname[20]; // ASCIIZ songname // changed from CHAR - WORD numtracks; // number of tracks saved - BYTE lastpattern; // last pattern number saved - BYTE lastorder; // last order number to play (songlength-1) - WORD commentsize; // length of comment field - BYTE numsamples; // number of samples saved - BYTE attribute; // attribute byte (unused) - BYTE beatspertrack; - BYTE numchannels; // number of channels used - BYTE panpos[32]; // voice pan positions -} MTMHEADER; - - -#pragma pack() - - -BOOL CSoundFile::ReadMTM(LPCBYTE lpStream, DWORD dwMemLength) -//----------------------------------------------------------- -{ - MTMHEADER *pmh = (MTMHEADER *)lpStream; - DWORD dwMemPos = 66; - - if ((!lpStream) || (dwMemLength < 0x100)) return FALSE; - if ((strncmp(pmh->id, "MTM", 3)) || (pmh->numchannels > 32) - || (pmh->numsamples >= MAX_SAMPLES) || (!pmh->numsamples) - || (!pmh->numtracks) || (!pmh->numchannels) - || (!pmh->lastpattern) || (pmh->lastpattern > MAX_PATTERNS)) - return FALSE; - strncpy(m_szNames[0], pmh->songname, 20); - m_szNames[0][20] = 0; - if (dwMemPos + 37*pmh->numsamples + 128 + 192*pmh->numtracks - + 64 * (pmh->lastpattern+1) + pmh->commentsize >= dwMemLength) - return FALSE; - m_nType = MOD_TYPE_MTM; - m_nSamples = pmh->numsamples; - m_nChannels = pmh->numchannels; - // Reading instruments - for (UINT i=1; i<=m_nSamples; i++) - { - MTMSAMPLE *pms = (MTMSAMPLE *)(lpStream + dwMemPos); - strncpy(m_szNames[i], pms->samplename, 22); - m_szNames[i][22] = 0; - Ins[i].nVolume = pms->volume << 2; - Ins[i].nGlobalVol = 64; - DWORD len = pms->length; - if ((len > 4) && (len <= MAX_SAMPLE_LENGTH)) - { - Ins[i].nLength = len; - Ins[i].nLoopStart = pms->reppos; - Ins[i].nLoopEnd = pms->repend; - if (Ins[i].nLoopEnd > Ins[i].nLength) - Ins[i].nLoopEnd = Ins[i].nLength; - if (Ins[i].nLoopStart + 4 >= Ins[i].nLoopEnd) - Ins[i].nLoopStart = Ins[i].nLoopEnd = 0; - if (Ins[i].nLoopEnd) Ins[i].uFlags |= CHN_LOOP; - Ins[i].nFineTune = MOD2XMFineTune(pms->finetune); - if (pms->attribute & 0x01) - { - Ins[i].uFlags |= CHN_16BIT; - Ins[i].nLength >>= 1; - Ins[i].nLoopStart >>= 1; - Ins[i].nLoopEnd >>= 1; - } - Ins[i].nPan = 128; - } - dwMemPos += 37; - } - // Setting Channel Pan Position - for (UINT ich=0; ichpanpos[ich] & 0x0F) << 4) + 8; - ChnSettings[ich].nVolume = 64; - } - // Reading pattern order - memcpy(Order, lpStream + dwMemPos, pmh->lastorder+1); - dwMemPos += 128; - // Reading Patterns - LPCBYTE pTracks = lpStream + dwMemPos; - dwMemPos += 192 * pmh->numtracks; - LPWORD pSeq = (LPWORD)(lpStream + dwMemPos); - for (UINT pat=0; pat<=pmh->lastpattern; pat++) - { - PatternSize[pat] = 64; - if ((Patterns[pat] = AllocatePattern(64, m_nChannels)) == NULL) break; - for (UINT n=0; n<32; n++) if ((pSeq[n]) && (pSeq[n] <= pmh->numtracks) && (n < m_nChannels)) - { - LPCBYTE p = pTracks + 192 * (pSeq[n]-1); - MODCOMMAND *m = Patterns[pat] + n; - for (UINT i=0; i<64; i++, m+=m_nChannels, p+=3) - { - if (p[0] & 0xFC) m->note = (p[0] >> 2) + 37; - m->instr = ((p[0] & 0x03) << 4) | (p[1] >> 4); - UINT cmd = p[1] & 0x0F; - UINT param = p[2]; - if (cmd == 0x0A) - { - if (param & 0xF0) param &= 0xF0; else param &= 0x0F; - } - m->command = cmd; - m->param = param; - if ((cmd) || (param)) ConvertModCommand(m); - } - } - pSeq += 32; - } - dwMemPos += 64*(pmh->lastpattern+1); - if ((pmh->commentsize) && (dwMemPos + pmh->commentsize < dwMemLength)) - { - UINT n = pmh->commentsize; - m_lpszSongComments = new char[n+1]; - if (m_lpszSongComments) - { - memcpy(m_lpszSongComments, lpStream+dwMemPos, n); - m_lpszSongComments[n] = 0; - for (UINT i=0; icommentsize; - // Reading Samples - for (UINT ismp=1; ismp<=m_nSamples; ismp++) - { - if (dwMemPos >= dwMemLength) break; - dwMemPos += ReadSample(&Ins[ismp], (Ins[ismp].uFlags & CHN_16BIT) ? RS_PCM16U : RS_PCM8U, - (LPSTR)(lpStream + dwMemPos), dwMemLength - dwMemPos); - } - m_nMinPeriod = 64; - m_nMaxPeriod = 32767; - return TRUE; -} - diff --git a/src/modplug/load_okt.cpp b/src/modplug/load_okt.cpp deleted file mode 100644 index 4c4e08a1..00000000 --- a/src/modplug/load_okt.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque , - * Adam Goode (endian and char fixes for PPC) -*/ - -////////////////////////////////////////////// -// Oktalyzer (OKT) module loader // -////////////////////////////////////////////// -#include "stdafx.h" -#include "sndfile.h" - -//#pragma warning(disable:4244) - -typedef struct OKTFILEHEADER -{ - DWORD okta; // "OKTA" - DWORD song; // "SONG" - DWORD cmod; // "CMOD" - DWORD fixed8; - BYTE chnsetup[8]; - DWORD samp; // "SAMP" - DWORD samplen; -} OKTFILEHEADER; - - -typedef struct OKTSAMPLE -{ - CHAR name[20]; - DWORD length; - WORD loopstart; - WORD looplen; - BYTE pad1; - BYTE volume; - BYTE pad2; - BYTE pad3; -} OKTSAMPLE; - - -BOOL CSoundFile::ReadOKT(const BYTE *lpStream, DWORD dwMemLength) -//--------------------------------------------------------------- -{ - OKTFILEHEADER *pfh = (OKTFILEHEADER *)lpStream; - DWORD dwMemPos = sizeof(OKTFILEHEADER); - UINT nsamples = 0, npatterns = 0, norders = 0; - - if ((!lpStream) || (dwMemLength < 1024)) return FALSE; - if ((pfh->okta != 0x41544B4F) || (pfh->song != 0x474E4F53) - || (pfh->cmod != 0x444F4D43) || (pfh->chnsetup[0]) || (pfh->chnsetup[2]) - || (pfh->chnsetup[4]) || (pfh->chnsetup[6]) || (pfh->fixed8 != 0x08000000) - || (pfh->samp != 0x504D4153)) return FALSE; - m_nType = MOD_TYPE_OKT; - m_nChannels = 4 + pfh->chnsetup[1] + pfh->chnsetup[3] + pfh->chnsetup[5] + pfh->chnsetup[7]; - if (m_nChannels > MAX_CHANNELS) m_nChannels = MAX_CHANNELS; - nsamples = bswapBE32(pfh->samplen) >> 5; - m_nSamples = nsamples; - if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1; - // Reading samples - for (UINT smp=1; smp <= nsamples; smp++) - { - if (dwMemPos >= dwMemLength) return TRUE; - if (smp < MAX_SAMPLES) - { - OKTSAMPLE *psmp = (OKTSAMPLE *)(lpStream + dwMemPos); - MODINSTRUMENT *pins = &Ins[smp]; - - memcpy(m_szNames[smp], psmp->name, 20); - pins->uFlags = 0; - pins->nLength = bswapBE32(psmp->length) & ~1; - pins->nLoopStart = bswapBE16(psmp->loopstart); - pins->nLoopEnd = pins->nLoopStart + bswapBE16(psmp->looplen); - if (pins->nLoopStart + 2 < pins->nLoopEnd) pins->uFlags |= CHN_LOOP; - pins->nGlobalVol = 64; - pins->nVolume = psmp->volume << 2; - pins->nC4Speed = 8363; - } - dwMemPos += sizeof(OKTSAMPLE); - } - // SPEE - if (dwMemPos >= dwMemLength) return TRUE; - if (*((DWORD *)(lpStream + dwMemPos)) == 0x45455053) - { - m_nDefaultSpeed = lpStream[dwMemPos+9]; - dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; - } - // SLEN - if (dwMemPos >= dwMemLength) return TRUE; - if (*((DWORD *)(lpStream + dwMemPos)) == 0x4E454C53) - { - npatterns = lpStream[dwMemPos+9]; - dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; - } - // PLEN - if (dwMemPos >= dwMemLength) return TRUE; - if (*((DWORD *)(lpStream + dwMemPos)) == 0x4E454C50) - { - norders = lpStream[dwMemPos+9]; - dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; - } - // PATT - if (dwMemPos >= dwMemLength) return TRUE; - if (*((DWORD *)(lpStream + dwMemPos)) == 0x54544150) - { - UINT orderlen = norders; - if (orderlen >= MAX_ORDERS) orderlen = MAX_ORDERS-1; - for (UINT i=0; i1; j--) { if (Order[j-1]) break; Order[j-1] = 0xFF; } - dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; - } - // PBOD - UINT npat = 0; - while ((dwMemPos+10 < dwMemLength) && (*((DWORD *)(lpStream + dwMemPos)) == 0x444F4250)) - { - DWORD dwPos = dwMemPos + 10; - UINT rows = lpStream[dwMemPos+9]; - if (!rows) rows = 64; - if (npat < MAX_PATTERNS) - { - if ((Patterns[npat] = AllocatePattern(rows, m_nChannels)) == NULL) return TRUE; - MODCOMMAND *m = Patterns[npat]; - PatternSize[npat] = rows; - UINT imax = m_nChannels*rows; - for (UINT i=0; i dwMemLength) break; - const BYTE *p = lpStream+dwPos; - UINT note = p[0]; - if (note) - { - m->note = note + 48; - m->instr = p[1] + 1; - } - UINT command = p[2]; - UINT param = p[3]; - m->param = param; - switch(command) - { - // 0: no effect - case 0: - break; - // 1: Portamento Up - case 1: - case 17: - case 30: - if (param) m->command = CMD_PORTAMENTOUP; - break; - // 2: Portamento Down - case 2: - case 13: - case 21: - if (param) m->command = CMD_PORTAMENTODOWN; - break; - // 10: Arpeggio - case 10: - case 11: - case 12: - m->command = CMD_ARPEGGIO; - break; - // 15: Filter - case 15: - m->command = CMD_MODCMDEX; - m->param = param & 0x0F; - break; - // 25: Position Jump - case 25: - m->command = CMD_POSITIONJUMP; - break; - // 28: Set Speed - case 28: - m->command = CMD_SPEED; - break; - // 31: Volume Control - case 31: - if (param <= 0x40) m->command = CMD_VOLUME; else - if (param <= 0x50) { m->command = CMD_VOLUMESLIDE; m->param &= 0x0F; if (!m->param) m->param = 0x0F; } else - if (param <= 0x60) { m->command = CMD_VOLUMESLIDE; m->param = (param & 0x0F) << 4; if (!m->param) m->param = 0xF0; } else - if (param <= 0x70) { m->command = CMD_MODCMDEX; m->param = 0xB0 | (param & 0x0F); if (!(param & 0x0F)) m->param = 0xBF; } else - if (param <= 0x80) { m->command = CMD_MODCMDEX; m->param = 0xA0 | (param & 0x0F); if (!(param & 0x0F)) m->param = 0xAF; } - break; - } - } - } - npat++; - dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; - } - // SBOD - UINT nsmp = 1; - while ((dwMemPos+10 < dwMemLength) && (*((DWORD *)(lpStream + dwMemPos)) == 0x444F4253)) - { - if (nsmp < MAX_SAMPLES) ReadSample(&Ins[nsmp], RS_PCM8S, (LPSTR)(lpStream+dwMemPos+8), dwMemLength-dwMemPos-8); - dwMemPos += bswapBE32(*((DWORD *)(lpStream + dwMemPos + 4))) + 8; - nsmp++; - } - return TRUE; -} - diff --git a/src/modplug/load_pat.cpp b/src/modplug/load_pat.cpp deleted file mode 100644 index 68d82068..00000000 --- a/src/modplug/load_pat.cpp +++ /dev/null @@ -1,1576 +0,0 @@ -/* - - MikMod Sound System - - By Jake Stine of Divine Entertainment (1996-2000) - - Support: - If you find problems with this code, send mail to: - air@divent.org - - Distribution / Code rights: - Use this source code in any fashion you see fit. Giving me credit where - credit is due is optional, depending on your own levels of integrity and - honesty. - - ----------------------------------------- - Module: LOAD_PAT - - PAT sample loader. - by Peter Grootswagers (2006) - - - It's primary purpose is loading samples for the .abc and .mid modules - Can also be used stand alone, in that case a tune (frere Jacques) - is generated using al samples available in the .pat file - - Portability: - All systems - all compilers (hopefully) -*/ - -#include -#include -#include -#include -#include -//#include // for sleep - -#ifdef NEWMIKMOD -#include "mikmod.h" -#include "uniform.h" -typedef UBYTE BYTE; -typedef UWORD WORD; -#else -#include "stdafx.h" -#include "sndfile.h" -#endif - -#include "load_pat.h" - -#ifdef MSC_VER -#define DIRDELIM '\\' -#define TIMIDITYCFG "C:\\TIMIDITY\\TIMIDITY.CFG" -#define PATHFORPAT "C:\\TIMIDITY\\INSTRUMENTS" -#else -#define DIRDELIM '/' -#define TIMIDITYCFG "/usr/local/share/timidity/timidity.cfg" -#define PATHFORPAT "/usr/local/share/timidity/instruments" -#endif - -#define PAT_ENV_PATH2CFG "MMPAT_PATH_TO_CFG" - -// 128 gm and 63 drum -#define MAXSMP 191 -static char midipat[MAXSMP][40]; -static char pathforpat[128]; -static char timiditycfg[128]; - -#pragma pack(1) - -typedef struct { - char header[12]; // ascizz GF1PATCH110 - char gravis_id[10]; // allways ID#000002 - char description[60]; - BYTE instruments; - BYTE voices; - BYTE channels; - WORD waveforms; - WORD master_volume; - DWORD data_size; - char reserved[36]; -} PatchHeader; - -typedef struct { - WORD instrument_id; - char instrument_name[16]; - DWORD instrument_size; - BYTE layers; - char reserved[40]; -} InstrumentHeader; - -typedef struct { - BYTE layer_dup; - BYTE layer_id; - DWORD layer_size; - BYTE samples; - char reserved[40]; -} LayerHeader; - -typedef struct { - char wave_name[7]; - BYTE fractions; - DWORD wave_size; - DWORD start_loop; - DWORD end_loop; - WORD sample_rate; - DWORD low_frequency ; - DWORD high_frequency; - DWORD root_frequency; - short int tune; - BYTE balance; - BYTE envelope_rate[6]; - BYTE envelope_offset[6]; - BYTE tremolo_sweep; - BYTE tremolo_rate; - BYTE tremolo_depth; - BYTE vibrato_sweep; - BYTE vibrato_rate; - BYTE vibrato_depth; - BYTE modes; - DWORD scale_frequency; - DWORD scale_factor; - char reserved[32]; -} WaveHeader; - -// WaveHeader.modes bits -#define PAT_16BIT 1 -#define PAT_UNSIGNED 2 -#define PAT_LOOP 4 -#define PAT_PINGPONG 8 -#define PAT_BACKWARD 16 -#define PAT_SUSTAIN 32 -#define PAT_ENVELOPE 64 -#define PAT_CLAMPED 128 - -#define C4SPD 8363 -#define C4mHz 523251 -#define C4 523.251 -#define PI 3.141592653589793 -#define OMEGA ((2.0 * PI * C4)/(float)C4SPD) - -/************************************************************************** -**************************************************************************/ -#ifdef NEWMIKMOD -static char PAT_Version[] = "Timidity GUS Patch v1.0"; -#endif -static BYTE pat_gm_used[MAXSMP]; -static BYTE pat_loops[MAXSMP]; - -/************************************************************************** -**************************************************************************/ - -typedef struct _PATHANDLE -{ -#ifdef NEWMIKMOD - MM_ALLOC *allochandle; -#endif - char patname[16]; - int samples; -} PATHANDLE; - -// local prototypes -static int pat_getopt(const char *s, const char *o, int dflt); - -static void pat_message(const char *s1, const char *s2) -{ - char txt[256]; - if( strlen(s1) + strlen(s2) > 255 ) return; - sprintf(txt, s1, s2); -#ifdef NEWMIKMOD - _mmlog(txt); -#else - fprintf(stderr, "load_pat > %s\n", txt); -#endif -} - -void pat_resetsmp(void) -{ - int i; - for( i=0; i MAXSMP ) { - sprintf(buf, "invalid gm %d", gm); - return buf; - } - return midipat[gm - 1]; -} - -int pat_gm_drumnr(int n) -{ - if( n < 25 ) return 129; - if( n+129-25 < MAXSMP ) - return 129+n-25; // timidity.cfg drum patches start at 25 - return MAXSMP; -} - -int pat_gm_drumnote(int n) -{ - char *p; - p = strchr(midipat[pat_gm_drumnr(n)-1], ':'); - if( p ) return pat_getopt(p+1, "note", n); - return n; -} - -static float pat_sinus(int i) -{ - float res = sinf(OMEGA * (float)i); - return res; -} - -static float pat_square(int i) -{ - float res = 30.0 * sinf(OMEGA * (float)i); - if( res > 0.99 ) return 0.99; - if( res < -0.99 ) return -0.99; - return res; -} - -static float pat_sawtooth(int i) -{ - float res = OMEGA * (float)i; - while( res > 2 * PI ) - res -= 2 * PI; - i = 2; - if( res > PI ) { - res = PI - res; - i = -2; - } - res = (float)i * res / PI; - if( res > 0.9 ) return 1.0 - res; - if( res < -0.9 ) return 1.0 + res; - return res; -} - -typedef float (*PAT_SAMPLE_FUN)(int); - -static PAT_SAMPLE_FUN pat_fun[] = { pat_sinus, pat_square, pat_sawtooth }; - -#ifdef NEWMIKMOD - -#define MMFILE MMSTREAM -#define mmftell(x) _mm_ftell(x) -#define mmfseek(f,p,w) _mm_fseek(f,p,w) -#define mmreadUBYTES(buf,sz,f) _mm_read_UBYTES(buf,sz,f) - -#else - -#define MMSTREAM FILE -#define _mm_fopen(name,mode) fopen(name,mode) -#define _mm_fgets(f,buf,sz) fgets(buf,sz,f) -#define _mm_fseek(f,pos,whence) fseek(f,pos,whence) -#define _mm_ftell(f) ftell(f) -#define _mm_read_UBYTES(buf,sz,f) fread(buf,sz,1,f) -#define _mm_read_SBYTES(buf,sz,f) fread(buf,sz,1,f) -#define _mm_feof(f) feof(f) -#define _mm_fclose(f) fclose(f) -#define DupStr(h,buf,sz) strdup(buf) -#define _mm_calloc(h,n,sz) calloc(n,sz) -#define _mm_recalloc(h,buf,sz,elsz) realloc(buf,sz) -#define _mm_free(h,p) free(p) - -typedef struct { - char *mm; - int sz; - int pos; -} MMFILE; - -static long mmftell(MMFILE *mmfile) -{ - return mmfile->pos; -} - -static void mmfseek(MMFILE *mmfile, long p, int whence) -{ - switch(whence) { - case SEEK_SET: - mmfile->pos = p; - break; - case SEEK_CUR: - mmfile->pos += p; - break; - case SEEK_END: - mmfile->pos = mmfile->sz + p; - break; - } -} - -static void mmreadUBYTES(BYTE *buf, long sz, MMFILE *mmfile) -{ - memcpy(buf, &mmfile->mm[mmfile->pos], sz); - mmfile->pos += sz; -} - -static void mmreadSBYTES(char *buf, long sz, MMFILE *mmfile) -{ - memcpy(buf, &mmfile->mm[mmfile->pos], sz); - mmfile->pos += sz; -} - -#endif - -void pat_init_patnames(void) -{ - int i,j; - char *p, *q; - char line[80]; - MMSTREAM *mmcfg; - strcpy(pathforpat, PATHFORPAT); - strcpy(timiditycfg, TIMIDITYCFG); - p = getenv(PAT_ENV_PATH2CFG); - if( p ) { - strcpy(timiditycfg,p); - strcpy(pathforpat,p); - strcat(timiditycfg,"/timidity.cfg"); - strcat(pathforpat,"/instruments"); - } - mmcfg = _mm_fopen(timiditycfg,"r"); - for( i=0; i= 0 ) { - p = strchr(line,'/')+1; - if(j) - q = midipat[pat_gm_drumnr(i)-1]; - else - q = midipat[i]; - while( *p && !isspace(*p) ) *q++ = *p++; - if( isspace(*p) ) { - *q++ = ':'; - while( isspace(*p) ) { - while( isspace(*p) ) p++; - while( *p && !isspace(*p) ) *q++ = *p++; - if( isspace(*p) ) *q++ = ' '; - } - } - *q++ = '\0'; - } - } - if( !strncmp(line,"drumset",7) ) j = 1; - _mm_fgets(mmcfg, line, 80); - } - _mm_fclose(mmcfg); - } - q = midipat[0]; - j = 0; - for( i=0; i 0; ) { - if( midipat[i][0] ) q = midipat[i]; - else strcpy(midipat[i],q); - } - } -} - -static char *pat_build_path(char *fname, int pat) -{ - char *ps; - ps = strrchr(midipat[pat], ':'); - if( ps ) { - sprintf(fname, "%s%c%s", pathforpat, DIRDELIM, midipat[pat]); - strcpy(strrchr(fname, ':'), ".pat"); - return ps; - } - sprintf(fname, "%s%c%s.pat", pathforpat, DIRDELIM, midipat[pat]); - return 0; -} - -static void pat_read_patname(PATHANDLE *h, MMFILE *mmpat) { - InstrumentHeader ih; - mmfseek(mmpat,sizeof(PatchHeader), SEEK_SET); - mmreadUBYTES((BYTE *)&ih, sizeof(InstrumentHeader), mmpat); - strncpy(h->patname, ih.instrument_name, 16); - h->patname[15] = '\0'; -} - -static void pat_read_layerheader(MMSTREAM *mmpat, LayerHeader *hl) -{ - _mm_fseek(mmpat,sizeof(PatchHeader)+sizeof(InstrumentHeader), SEEK_SET); - _mm_read_UBYTES((BYTE *)hl, sizeof(LayerHeader), mmpat); -} - -static void pat_get_layerheader(MMFILE *mmpat, LayerHeader *hl) -{ - InstrumentHeader ih; - mmfseek(mmpat,sizeof(PatchHeader), SEEK_SET); - mmreadUBYTES((BYTE *)&ih, sizeof(InstrumentHeader), mmpat); - mmreadUBYTES((BYTE *)hl, sizeof(LayerHeader), mmpat); - strncpy(hl->reserved, ih.instrument_name, 40); -} - -static int pat_read_numsmp(MMFILE *mmpat) { - LayerHeader hl; - pat_get_layerheader(mmpat, &hl); - return hl.samples; -} - -static void pat_read_waveheader(MMSTREAM *mmpat, WaveHeader *hw, int layer) -{ - long int pos, bestpos=0; - LayerHeader hl; - ULONG bestfreq, freqdist; - int i; - // read the very first and maybe only sample - pat_read_layerheader(mmpat, &hl); - if( hl.samples > 1 ) { - if( layer ) { - if( layer > hl.samples ) layer = hl.samples; // you don't fool me.... - for( i=1; iwave_size, SEEK_CUR); - } - } - else { - bestfreq = C4mHz * 1000; // big enough - for( i=0; iroot_frequency > C4mHz ) - freqdist = hw->root_frequency - C4mHz; - else - freqdist = 2 * (C4mHz - hw->root_frequency); - if( freqdist < bestfreq ) { - bestfreq = freqdist; - bestpos = pos; - } - _mm_fseek(mmpat, hw->wave_size, SEEK_CUR); - } - _mm_fseek(mmpat, bestpos, SEEK_SET); - } - } - _mm_read_UBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat); - strncpy(hw->reserved, hl.reserved, 36); - if( hw->start_loop >= hw->wave_size ) { - hw->start_loop = 0; - hw->end_loop = 0; - hw->modes &= ~PAT_LOOP; // mask off loop indicator - } - if( hw->end_loop > hw->wave_size ) - hw->end_loop = hw->wave_size; -} - -#ifndef NEWMIKMOD -static void pat_get_waveheader(MMFILE *mmpat, WaveHeader *hw, int layer) -{ - long int pos, bestpos=0; - LayerHeader hl; - ULONG bestfreq, freqdist; - int i; - // read the very first and maybe only sample - pat_get_layerheader(mmpat, &hl); - if( hl.samples > 1 ) { - if( layer ) { - if( layer > hl.samples ) layer = hl.samples; // you don't fool me.... - for( i=1; iwave_size, SEEK_CUR); - } - } - else { - bestfreq = C4mHz * 1000; // big enough - for( i=0; iroot_frequency > C4mHz ) - freqdist = hw->root_frequency - C4mHz; - else - freqdist = 2 * (C4mHz - hw->root_frequency); - if( freqdist < bestfreq ) { - bestfreq = freqdist; - bestpos = pos; - } - mmfseek(mmpat, hw->wave_size, SEEK_CUR); - } - mmfseek(mmpat, bestpos, SEEK_SET); - } - } - mmreadUBYTES((BYTE *)hw, sizeof(WaveHeader), mmpat); - if( hw->start_loop >= hw->wave_size ) { - hw->start_loop = 0; - hw->end_loop = 0; - hw->modes &= ~PAT_LOOP; // mask off loop indicator - } - if( hw->end_loop > hw->wave_size ) - hw->end_loop = hw->wave_size; -} -#endif - -static int pat_readpat_attr(int pat, WaveHeader *hw, int layer) -{ - char fname[128]; - MMSTREAM *mmpat; - pat_build_path(fname, pat); - mmpat = _mm_fopen(fname, "r"); - if( !mmpat ) - return 0; - pat_read_waveheader(mmpat, hw, layer); - _mm_fclose(mmpat); - return 1; -} - -static void pat_amplify(char *b, int num, int amp, int m) -{ - char *pb; - BYTE *pu; - short int *pi; - WORD *pw; - int i,n,v; - n = num; - if( m & PAT_16BIT ) { // 16 bit - n >>= 1; - if( m & 2 ) { // unsigned - pw = (WORD *)b; - for( i=0; i 0x7fff ) v = 0x7fff; - *pw++ = v + 0x8000; - } - } - else { - pi = (short int *)b; - for( i=0; i 0x7fff ) v = 0x7fff; - *pi++ = v; - } - } - } - else { - if( m & 2 ) { // unsigned - pu = (BYTE *)b; - for( i=0; i 0x7f ) v = 0x7f; - *pu++ = v + 0x80; - } - } - else { - pb = (char *)b; - for( i=0; i 0x7f ) v = 0x7f; - *pb++ = v; - } - } - } -} - -static int pat_getopt(const char *s, const char *o, int dflt) -{ - const char *p; - if( !s ) return dflt; - p = strstr(s,o); - if( !p ) return dflt; - return atoi(strchr(p,'=')+1); -} - -static void pat_readpat(int pat, char *dest, int num) -{ - static int readlasttime = 0, wavesize = 0; - static MMSTREAM *mmpat = 0; - static char *opt = 0; - int amp; - char fname[128]; - WaveHeader hw; -#ifdef NEWMIKMOD - static int patlast = MAXSMP; - if( !dest ) { // reset - if( mmpat ) _mm_fclose(mmpat); - readlasttime = 0; - wavesize = 0; - mmpat = 0; - patlast = MAXSMP; - return; - } - if( pat != patlast ) { // reset for other instrument - if( mmpat ) _mm_fclose(mmpat); - readlasttime = 0; - patlast = pat; - } -#endif - if( !readlasttime ) { - opt=pat_build_path(fname, pat); - mmpat = _mm_fopen(fname, "r"); - if( !mmpat ) - return; - pat_read_waveheader(mmpat, &hw, 0); - wavesize = hw.wave_size; - } - _mm_read_SBYTES(dest, num, mmpat); - amp = pat_getopt(opt,"amp",100); - if( amp != 100 ) pat_amplify(dest, num, amp, hw.modes); - readlasttime += num; - if( readlasttime < wavesize ) return; - readlasttime = 0; - _mm_fclose(mmpat); - mmpat = 0; -} - -#ifdef NEWMIKMOD -// next code pinched from dec_raw.c and rebuild to load bytes from different places -// ===================================================================================== -static void *dec_pat_Init(MMSTREAM *mmfp) -{ - pat_readpat(0,0,0); // initialize pat loader - return (void *)mmfp; -} - -static void dec_pat_Cleanup(void *raw) -{ -} - -static BOOL dec_pat_Decompress16Bit(void *raw, short int *dest, int cbcount, MMSTREAM *mmfp) -{ - long samplenum = _mm_ftell(mmfp) - 1; -#else -static BOOL dec_pat_Decompress16Bit(short int *dest, int cbcount, int samplenum) -{ -#endif - int i; - PAT_SAMPLE_FUN f; - if( samplenum < MAXSMP ) pat_readpat(samplenum, (char *)dest, cbcount*2); - else { - f = pat_fun[(samplenum - MAXSMP) % 3]; - for( i=0; iallochandle) { - _mmalloc_close(handle->allochandle); - handle->allochandle = 0; - } -#else - if(handle) { - free(handle); - } -#endif -} - -static char tune[] = "c d e c|c d e c|e f g..|e f g..|gagfe c|gagfe c|c G c..|c G c..|"; -static int pat_note(int abc) -{ - switch( abc ) { - case 'C': return 48; - case 'D': return 50; - case 'E': return 52; - case 'F': return 53; - case 'G': return 55; - case 'A': return 57; - case 'B': return 59; - case 'c': return 60; - case 'd': return 62; - case 'e': return 64; - case 'f': return 65; - case 'g': return 67; - case 'a': return 69; - case 'b': return 71; - default: - break; - } - return 0; -} - -int pat_modnote(int midinote) -{ - int n; - n = midinote; -#ifdef NEWMIKMOD - if( n < 12 ) n++; - else n-=11; -#else - n += 13; -#endif - return n; -} - -// ===================================================================================== -#ifdef NEWMIKMOD -static void PAT_ReadPatterns(UNIMOD *of, PATHANDLE *h, int numpat) -// ===================================================================================== -{ - int pat,row,i,ch; - BYTE n,ins,vol; - int t; - int tt1, tt2; - UNITRK_EFFECT eff; - - tt2 = (h->samples - 1) * 16 + 128; - for( pat = 0; pat < numpat; pat++ ) { - utrk_reset(of->ut); - for( row = 0; row < 64; row++ ) { - tt1 = (pat * 64 + row); - for( ch = 0; ch < h->samples; ch++ ) { - t = tt1 - ch * 16; - if( t >= 0 ) { - i = tt2 - 16 * ((h->samples - 1 - ch) & 3); - if( tt1 < i ) { - t = t % 64; - if( isalpha(tune[t]) ) { - utrk_settrack(of->ut, ch); - n = pat_modnote(pat_note(tune[t])); - ins = ch; - vol = 100; - if( (t % 16) == 0 ) { - vol += vol / 10; - if( vol > 127 ) vol = 127; - } - utrk_write_inst(of->ut, ins); - utrk_write_note(of->ut, n); // <- normal note - pt_write_effect(of->ut, 0xc, vol); - } - if( tt1 == i - 1 && ch == 0 && row < 63 ) { - eff.effect = UNI_GLOB_PATBREAK; - eff.param.u = 0; - eff.framedly = UFD_RUNONCE; - utrk_write_global(of->ut, &eff, UNIMEM_NONE); - } - } - else { - if( tt1 == i ) { - eff.param.u = 0; - eff.effect = UNI_NOTEKILL; - utrk_write_local(of->ut, &eff, UNIMEM_NONE); - } - } - } - } - utrk_newline(of->ut); - } - if(!utrk_dup_pattern(of->ut,of)) return; - } -} - -#else - -static void PAT_ReadPatterns(MODCOMMAND *pattern[], WORD psize[], PATHANDLE *h, int numpat) -// ===================================================================================== -{ - int pat,row,i,ch; - BYTE n,ins,vol; - int t; - int tt1, tt2; - MODCOMMAND *m; - if( numpat > MAX_PATTERNS ) numpat = MAX_PATTERNS; - - tt2 = (h->samples - 1) * 16 + 128; - for( pat = 0; pat < numpat; pat++ ) { - pattern[pat] = CSoundFile::AllocatePattern(64, h->samples); - if( !pattern[pat] ) return; - psize[pat] = 64; - for( row = 0; row < 64; row++ ) { - tt1 = (pat * 64 + row); - for( ch = 0; ch < h->samples; ch++ ) { - t = tt1 - ch * 16; - m = &pattern[pat][row * h->samples + ch]; - m->param = 0; - m->command = CMD_NONE; - if( t >= 0 ) { - i = tt2 - 16 * ((h->samples - 1 - ch) & 3); - if( tt1 < i ) { - t = t % 64; - if( isalpha(tune[t]) ) { - n = pat_modnote(pat_note(tune[t])); - ins = ch + 1; - vol = 40; - if( (t % 16) == 0 ) { - vol += vol / 10; - if( vol > 64 ) vol = 64; - } - m->instr = ins; - m->note = n; // <- normal note - m->volcmd = VOLCMD_VOLUME; - m->vol = vol; - } - if( tt1 == i - 1 && ch == 0 && row < 63 ) { - m->command = CMD_PATTERNBREAK; - } - } - else { - if( tt1 == i ) { - m->param = 0; - m->command = CMD_KEYOFF; - m->volcmd = VOLCMD_VOLUME; - m->vol = 0; - } - } - } - } - } - } -} - -#endif - -// calculate the best speed that approximates the pat root frequency as a C note -static ULONG pat_patrate_to_C4SPD(ULONG patRate , ULONG patMilliHz) -{ - ULONG u; - double x, y; - u = patMilliHz; - x = 0.1 * patRate; - x = x * C4mHz; - y = u * 0.4; - x = x / y; - u = (ULONG)(x+0.5); - return u; -} - -// return relative position in samples for the rate starting with offset start ending with offset end -static int pat_envelope_rpos(int rate, int start, int end) -{ - int r, p, t, s; - // rate byte is 3 bits exponent and 6 bits increment size - // eeiiiiii - // every 8 to the power ee the volume is incremented/decremented by iiiiii - // Thank you Gravis for this weirdness... - r = 3 - ((rate >> 6) & 3) * 3; - p = rate & 0x3f; - if( !p ) return 0; - t = end - start; - if( !t ) return 0; - if (t < 0) t = -t; - s = (t << r)/ p; - return s; -} - -static void pat_modenv(WaveHeader *hw, int mpos[6], int mvol[6]) -{ - int i, sum, s; - BYTE *prate = hw->envelope_rate, *poffset = hw->envelope_offset; - for( i=0; i<6; i++ ) { - mpos[i] = 0; - mvol[i] = 64; - } - if( !memcmp(prate, "??????", 6) || poffset[5] >= 100 ) return; // weird rates or high env end volume - if( !(hw->modes & PAT_SUSTAIN) ) return; // no sustain thus no need for envelope - s = hw->wave_size; - if (s == 0) return; - if( hw->modes & PAT_16BIT ) - s >>= 1; - // offsets 0 1 2 3 4 5 are distributed over 0 2 4 6 8 10, the odd numbers are set in between - sum = 0; - for( i=0; i<6; i++ ) { - mvol[i] = poffset[i]; - mpos[i] = pat_envelope_rpos(prate[i], i? poffset[i-1]: 0, poffset[i]); - sum += mpos[i]; - } - if( sum == 0 ) return; - if( sum > s ) { - for( i=0; i<6; i++ ) - mpos[i] = (s * mpos[i]) / sum; - } - for( i=1; i<6; i++ ) - mpos[i] += mpos[i-1]; - for( i=0; i<6 ; i++ ) { - mpos[i] = (256 * mpos[i]) / s; - mpos[i]++; - if( i > 0 && mpos[i] <= mpos[i-1] ) { - if( mvol[i] == mvol[i-1] ) mpos[i] = mpos[i-1]; - else mpos[i] = mpos[i-1] + 1; - } - if( mpos[i] > 256 ) mpos[i] = 256; - } - mvol[5] = 0; // kill Bill.... -} - -#ifdef NEWMIKMOD -static void pat_setpat_inst(WaveHeader *hw, INSTRUMENT *d, int smp) -{ - int u, inuse; - int envpoint[6], envvolume[6]; - for(u=0; u<120; u++) { - d->samplenumber[u] = smp; - d->samplenote[u] = smp; - } - d->globvol = 64; - d->volfade = 0; - d->volflg = EF_CARRY; - d->panflg = EF_CARRY; - if( hw->modes & PAT_ENVELOPE ) d->volflg |= EF_ON; - if( hw->modes & PAT_SUSTAIN ) d->volflg |= EF_SUSTAIN; - if( (hw->modes & PAT_LOOP) && (hw->start_loop != hw->end_loop) ) d->volflg |= EF_LOOP; - d->volsusbeg = 1; - d->volsusend = 1; - d->volbeg = 1; - d->volend = 2; - d->volpts = 6; - // scale volume envelope: - inuse = 0; - pat_modenv(hw, envpoint, envvolume); - for(u=0; u<6; u++) - { - if( envvolume[u] != 64 ) inuse = 1; - d->volenv[u].val = envvolume[u]<<2; - d->volenv[u].pos = envpoint[u]; - } - if(!inuse) d->volpts = 0; - d->pansusbeg = 0; - d->pansusend = 0; - d->panbeg = 0; - d->panend = 0; - d->panpts = 0; - // scale panning envelope: - for(u=0; u<12; u++) - { - d->panenv[u].val = 0; - d->panenv[u].pos = 0; - } - d->panpts = 0; -} -#else -static void pat_setpat_inst(WaveHeader *hw, INSTRUMENTHEADER *d, int smp) -{ - int u, inuse; - int envpoint[6], envvolume[6]; - d->nMidiProgram = 0; - d->nFadeOut = 0; - d->nPan = 128; - d->nPPC = 5*12; - d->dwFlags = 0; - if( hw->modes & PAT_ENVELOPE ) d->dwFlags |= ENV_VOLUME; - if( hw->modes & PAT_SUSTAIN ) d->dwFlags |= ENV_VOLSUSTAIN; - if( (hw->modes & PAT_LOOP) && (hw->start_loop != hw->end_loop) ) d->dwFlags |= ENV_VOLLOOP; - d->nVolEnv = 6; - //if (!d->nVolEnv) d->dwFlags &= ~ENV_VOLUME; - d->nPanEnv = 0; - d->nVolSustainBegin = 1; - d->nVolSustainEnd = 1; - d->nVolLoopStart = 1; - d->nVolLoopEnd = 2; - d->nPanSustainBegin = 0; - d->nPanSustainEnd = 0; - d->nPanLoopStart = 0; - d->nPanLoopEnd = 0; - d->nGlobalVol = 64; - pat_modenv(hw, envpoint, envvolume); - inuse = 0; - for( u=0; u<6; u++) - { - if( envvolume[u] != 64 ) inuse = 1; - d->VolPoints[u] = envpoint[u]; - d->VolEnv[u] = envvolume[u]; - d->PanPoints[u] = 0; - d->PanEnv[u] = 0; - if (u) - { - if (d->VolPoints[u] < d->VolPoints[u-1]) - { - d->VolPoints[u] &= 0xFF; - d->VolPoints[u] += d->VolPoints[u-1] & 0xFF00; - if (d->VolPoints[u] < d->VolPoints[u-1]) d->VolPoints[u] += 0x100; - } - } - } - if( !inuse ) d->nVolEnv = 0; - for( u=0; u<128; u++) - { - d->NoteMap[u] = u+1; - d->Keyboard[u] = smp; - } -} -#endif -#ifdef NEWMIKMOD -static void PATinst(UNIMOD *of, INSTRUMENT *d, int smp, int gm) -#else -static void PATinst(INSTRUMENTHEADER *d, int smp, int gm) -#endif -{ - WaveHeader hw; - char s[32]; - memset(s,0,32); - if( pat_readpat_attr(gm-1, &hw, 0) ) { - pat_setpat_inst(&hw, d, smp); - } - else { - hw.modes = PAT_16BIT|PAT_ENVELOPE|PAT_SUSTAIN|PAT_LOOP; - hw.start_loop = 0; - hw.end_loop = 30000; - hw.wave_size = 30000; -// envelope rates and offsets pinched from timidity's acpiano.pat sample no 1 - hw.envelope_rate[0] = 0x3f; - hw.envelope_rate[1] = 0x3f; - hw.envelope_rate[2] = 0x3f; - hw.envelope_rate[3] = 0x08|(3<<6); - hw.envelope_rate[4] = 0x3f; - hw.envelope_rate[5] = 0x3f; - hw.envelope_offset[0] = 246; - hw.envelope_offset[1] = 246; - hw.envelope_offset[2] = 246; - hw.envelope_offset[3] = 0; - hw.envelope_offset[4] = 0; - hw.envelope_offset[5] = 0; - strncpy(hw.reserved, midipat[gm-1], 36); - pat_setpat_inst(&hw, d, smp); - } - if( hw.reserved[0] ) - strncpy(s, hw.reserved, 32); - else - strncpy(s, midipat[gm-1], 32); -#ifdef NEWMIKMOD - d->insname = DupStr(of->allochandle, s,28); -#else - s[31] = '\0'; - memset(d->name, 0, 32); - strcpy((char *)d->name, s); - strncpy(s, midipat[gm-1], 12); - s[11] = '\0'; - memset(d->filename, 0, 12); - strcpy((char *)d->filename, s); -#endif -} - -#ifdef NEWMIKMOD -static void pat_setpat_attr(WaveHeader *hw, UNISAMPLE *q, int gm) -{ - q->seekpos = gm; // dec_pat expects the midi samplenumber in this - q->speed = pat_patrate_to_C4SPD(hw->sample_rate , hw->root_frequency); - q->length = hw->wave_size; - q->loopstart = hw->start_loop; - q->loopend = hw->end_loop; - q->volume = 0x40; - if( hw->modes & PAT_16BIT ) { - q->format |= SF_16BITS; - q->length >>= 1; - q->loopstart >>= 1; - q->loopend >>= 1; - q->speed <<= 1; - } - if( (hw->modes & PAT_UNSIGNED)==0 ) q->format |= SF_SIGNED; - if( hw->modes & PAT_LOOP ) { - q->flags |= SL_LOOP; - if( hw->modes & PAT_PINGPONG ) q->flags |= SL_SUSTAIN_BIDI; - if( hw->modes & PAT_SUSTAIN ) q->flags |= SL_SUSTAIN_LOOP; - } -} -#else -static void pat_setpat_attr(WaveHeader *hw, MODINSTRUMENT *q) -{ - q->nC4Speed = pat_patrate_to_C4SPD(hw->sample_rate , hw->root_frequency); - q->nLength = hw->wave_size; - q->nLoopStart = hw->start_loop; - q->nLoopEnd = hw->end_loop; - q->nVolume = 256; - if( hw->modes & PAT_16BIT ) { - q->nLength >>= 1; - q->nLoopStart >>= 1; - q->nLoopEnd >>= 1; - } - if( hw->modes & PAT_LOOP ) { - q->uFlags |= CHN_LOOP; - if( hw->modes & PAT_PINGPONG ) q->uFlags |= CHN_PINGPONGSUSTAIN; - if( hw->modes & PAT_SUSTAIN ) q->uFlags |= CHN_SUSTAINLOOP; - } -} -#endif - -// ========================== -// Load those darned Samples! -#ifdef NEWMIKMOD -static void PATsample(UNIMOD *of, UNISAMPLE *q, int smp, int gm) -#else -static void PATsample(CSoundFile *cs, MODINSTRUMENT *q, int smp, int gm) -#endif -{ - WaveHeader hw; - char s[32]; - sprintf(s, "%d:%s", smp-1, midipat[gm-1]); -#ifdef NEWMIKMOD - q->samplename = DupStr(of->allochandle, s,28); - if( pat_readpat_attr(gm-1, &hw, 0) ) { - pat_setpat_attr(&hw, q, gm); - pat_loops[smp-1] = (q->flags & (SL_LOOP | SL_SUSTAIN_LOOP))? 1: 0; - } - else { - q->seekpos = smp + MAXSMP + 1; // dec_pat expects the samplenumber in this - q->speed = C4SPD; - q->length = 30000; - q->loopstart = 0; - q->loopend = 30000; - q->volume = 0x40; - - // Enable aggressive declicking for songs that do not loop and that - // are long enough that they won't be adversely affected. - - q->flags |= SL_LOOP; - q->format |= SF_16BITS; - q->format |= SF_SIGNED; - } - if(!(q->flags & (SL_LOOP | SL_SUSTAIN_LOOP)) && (q->length > 5000)) - q->flags |= SL_DECLICK; -#else - s[31] = '\0'; - memset(cs->m_szNames[smp], 0, 32); - strcpy(cs->m_szNames[smp], s); - q->nGlobalVol = 64; - q->nPan = 128; - q->uFlags = CHN_16BIT; - if( pat_readpat_attr(gm-1, &hw, 0) ) { - char *p; - pat_setpat_attr(&hw, q); - pat_loops[smp-1] = (q->uFlags & CHN_LOOP)? 1: 0; - if( hw.modes & PAT_16BIT ) p = (char *)malloc(hw.wave_size); - else p = (char *)malloc(hw.wave_size * sizeof(short int)); - if( p ) { - if( hw.modes & PAT_16BIT ) { - dec_pat_Decompress16Bit((short int *)p, hw.wave_size>>1, gm - 1); - cs->ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size); - } - else { - dec_pat_Decompress8Bit((short int *)p, hw.wave_size, gm - 1); - cs->ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size * sizeof(short int)); - } - free(p); - } - } - else { - char *p; - q->nC4Speed = C4SPD; - q->nLength = 30000; - q->nLoopStart = 0; - q->nLoopEnd = 30000; - q->nVolume = 256; - q->uFlags |= CHN_LOOP; - q->uFlags |= CHN_16BIT; - p = (char *)malloc(q->nLength*sizeof(short int)); - if( p ) { - dec_pat_Decompress8Bit((short int *)p, q->nLength, smp + MAXSMP - 1); - cs->ReadSample(q, RS_PCM16S, (LPSTR)p, q->nLength*2); - free(p); - } - } -#endif -} - -// ===================================================================================== -BOOL PAT_Load_Instruments(void *c) -{ - uint t; -#ifdef NEWMIKMOD - UNIMOD *of = (UNIMOD *)c; - INSTRUMENT *d; - UNISAMPLE *q; - if( !pat_numsmp() ) pat_gmtosmp(1); // make sure there is a sample - of->numsmp = pat_numsmp(); - of->numins = pat_numinstr(); - if(!AllocInstruments(of)) return FALSE; - if(!AllocSamples(of, 0)) return FALSE; - d = of->instruments; - for(t=1; t<=of->numins; t++) { - PATinst(of, d, t, pat_smptogm(t)); - d++; - } - q = of->samples; - for(t=1; t<=of->numsmp; t++) { - PATsample(of, q, t, pat_smptogm(t)); - q++; - } - SL_RegisterDecompressor(&dec_pat); // fool him to generate samples -#else - CSoundFile *of=(CSoundFile *)c; - if( !pat_numsmp() ) pat_gmtosmp(1); // make sure there is a sample - of->m_nSamples = pat_numsmp() + 1; // xmms modplug does not use slot zero - of->m_nInstruments = pat_numinstr() + 1; - for(t=1; tm_nInstruments; t++) { // xmms modplug doesn't use slot zero - if( (of->Headers[t] = new INSTRUMENTHEADER) == NULL ) return FALSE; - memset(of->Headers[t], 0, sizeof(INSTRUMENTHEADER)); - PATinst(of->Headers[t], t, pat_smptogm(t)); - } - for(t=1; tm_nSamples; t++) { // xmms modplug doesn't use slot zero - PATsample(of, &of->Ins[t], t, pat_smptogm(t)); - } - // copy last of the mohicans to entry 0 for XMMS modinfo to work.... - t = of->m_nInstruments - 1; - if( (of->Headers[0] = new INSTRUMENTHEADER) == NULL ) return FALSE; - memcpy(of->Headers[0], of->Headers[t], sizeof(INSTRUMENTHEADER)); - memset(of->Headers[0]->name, 0, 32); - strncpy((char *)of->Headers[0]->name, "Timidity GM patches", 32); - t = of->m_nSamples - 1; - memcpy(&of->Ins[0], &of->Ins[t], sizeof(MODINSTRUMENT)); -#endif - return TRUE; -} -// ===================================================================================== -#ifdef NEWMIKMOD -BOOL PAT_Load(PATHANDLE *h, UNIMOD *of, MMSTREAM *mmfile) -#else -BOOL CSoundFile::ReadPAT(const BYTE *lpStream, DWORD dwMemLength) -#endif -{ - static int avoid_reentry = 0; - char buf[60]; - int t; -#ifdef NEWMIKMOD - UNISAMPLE *q; - INSTRUMENT *d; -#define m_nDefaultTempo of->inittempo -#else - PATHANDLE *h; - int numpat; - MMFILE mm, *mmfile; - MODINSTRUMENT *q; - INSTRUMENTHEADER *d; - if( !TestPAT(lpStream, dwMemLength) ) return FALSE; - h = PAT_Init(); - if( !h ) return FALSE; - mmfile = &mm; - mm.mm = (char *)lpStream; - mm.sz = dwMemLength; - mm.pos = 0; -#endif - while( avoid_reentry ) sleep(1); - avoid_reentry = 1; - pat_read_patname(h, mmfile); - h->samples = pat_read_numsmp(mmfile); - if( strlen(h->patname) ) - sprintf(buf,"%s canon %d-v (Fr. Jacques)", h->patname, h->samples); - else - sprintf(buf,"%d-voice canon (Fr. Jacques)", h->samples); -#ifdef NEWMIKMOD - of->songname = DupStr(of->allochandle, buf, strlen(buf)); -#else - if( strlen(buf) > 31 ) buf[31] = '\0'; // chop it of - strcpy(m_szNames[0], buf); -#endif - m_nDefaultTempo = 60; // 120 / 2 - t = (h->samples - 1) * 16 + 128; - if( t % 64 ) t += 64; - t = t / 64; -#ifdef NEWMIKMOD - of->memsize = PTMEM_LAST; // Number of memory slots to reserve! - of->modtype = _mm_strdup(of->allochandle, PAT_Version); - of->reppos = 0; - of->numins = h->samples; - of->numsmp = h->samples; - of->initspeed = 6; - of->numchn = h->samples; - of->numpat = t; - of->numpos = of->numpat; // one repeating pattern - of->numtrk = of->numpat * of->numchn; - of->initvolume = 64; - of->pansep=128; - // allocate resources - if(!AllocPositions(of, of->numpos)) { - avoid_reentry = 0; - return FALSE; - } - if(!AllocInstruments(of)) { - avoid_reentry = 0; - return FALSE; - } - if(!AllocSamples(of, 0)) { - avoid_reentry = 0; - return FALSE; - } - // orderlist - for(t=0; tnumpos; t++) - of->positions[t] = t; - d = of->instruments; - for(t=1; t<=of->numins; t++) { - WaveHeader hw; - char s[32]; - sprintf(s, "%s", h->patname); - d->insname = DupStr(of->allochandle, s,28); - pat_read_waveheader(mmfile, &hw, t); - pat_setpat_inst(&hw, d, t); - } - q = of->samples; - for(t=1; t<=of->numsmp; t++) { - WaveHeader hw; - char s[28]; - pat_read_waveheader(mmfile, &hw, t); - pat_setpat_attr(&hw, q, _mm_ftell(mmfile)); - memset(s,0,28); - if( hw.wave_name[0] ) - sprintf(s, "%d:%s", t, hw.wave_name); - else - sprintf(s, "%d:%s", t, h->patname); - q->samplename = DupStr(of->allochandle, s,28); - if(!(q->flags & (SL_LOOP | SL_SUSTAIN_LOOP)) && (q->length > 5000)) - q->flags |= SL_DECLICK; - q++; - } -#else - m_nType = MOD_TYPE_PAT; - m_nInstruments = h->samples + 1; // we know better but use each sample in the pat... - m_nSamples = h->samples + 1; // xmms modplug does not use slot zero - m_nDefaultSpeed = 6; - m_nChannels = h->samples; - numpat = t; - - m_dwSongFlags = SONG_LINEARSLIDES; - m_nMinPeriod = 28 << 2; - m_nMaxPeriod = 1712 << 3; - // orderlist - for(t=0; t < numpat; t++) - Order[t] = t; - for(t=1; t<(int)m_nInstruments; t++) { // xmms modplug doesn't use slot zero - WaveHeader hw; - char s[32]; - if( (d = new INSTRUMENTHEADER) == NULL ) { - avoid_reentry = 0; - return FALSE; - } - memset(d, 0, sizeof(INSTRUMENTHEADER)); - Headers[t] = d; - sprintf(s, "%s", h->patname); - s[31] = '\0'; - memset(d->name, 0, 32); - strcpy((char *)d->name, s); - s[11] = '\0'; - memset(d->filename, 0, 12); - strcpy((char *)d->filename, s); - pat_get_waveheader(mmfile, &hw, t); - pat_setpat_inst(&hw, d, t); - } - for(t=1; t<(int)m_nSamples; t++) { // xmms modplug doesn't use slot zero - WaveHeader hw; - char s[32]; - char *p; - q = &Ins[t]; // we do not use slot zero - q->nGlobalVol = 64; - q->nPan = 128; - q->uFlags = CHN_16BIT; - pat_get_waveheader(mmfile, &hw, t); - pat_setpat_attr(&hw, q); - memset(s,0,32); - if( hw.wave_name[0] ) - sprintf(s, "%d:%s", t, hw.wave_name); - else { - if( h->patname[0] ) - sprintf(s, "%d:%s", t, h->patname); - else - sprintf(s, "%d:Untitled GM patch", t); - } - s[31] = '\0'; - memset(m_szNames[t], 0, 32); - strcpy(m_szNames[t], s); - if( hw.modes & PAT_16BIT ) p = (char *)malloc(hw.wave_size); - else p = (char *)malloc(hw.wave_size * sizeof(short int)); - if( p ) { - mmreadSBYTES(p, hw.wave_size, mmfile); - if( hw.modes & PAT_16BIT ) { - ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size); - } - else { - pat_blowup_to16bit((short int *)p, hw.wave_size); - ReadSample(q, (hw.modes&PAT_UNSIGNED)?RS_PCM16U:RS_PCM16S, (LPSTR)p, hw.wave_size * sizeof(short int)); - } - free(p); - } - } - // copy last of the mohicans to entry 0 for XMMS modinfo to work.... - t = m_nInstruments - 1; - if( (Headers[0] = new INSTRUMENTHEADER) == NULL ) { - avoid_reentry = 0; - return FALSE; - } - memcpy(Headers[0], Headers[t], sizeof(INSTRUMENTHEADER)); - memset(Headers[0]->name, 0, 32); - if( h->patname[0] ) - strncpy((char *)Headers[0]->name, h->patname, 32); - else - strncpy((char *)Headers[0]->name, "Timidity GM patch", 32); - t = m_nSamples - 1; - memcpy(&Ins[0], &Ins[t], sizeof(MODINSTRUMENT)); -#endif -#ifdef NEWMIKMOD - // ============================== - // Load the pattern info now! - if(!AllocTracks(of)) return 0; - if(!AllocPatterns(of)) return 0; - of->ut = utrk_init(of->numchn, h->allochandle); - utrk_memory_reset(of->ut); - utrk_local_memflag(of->ut, PTMEM_PORTAMENTO, TRUE, FALSE); - PAT_ReadPatterns(of, h, of->numpat); - // ============================================================ - // set panning positions - for(t=0; tnumchn; t++) { - of->panning[t] = PAN_LEFT+((t+2)%5)*((PAN_RIGHT - PAN_LEFT)/5); // 0x30 = std s3m val - } -#else - // ============================== - // Load the pattern info now! - PAT_ReadPatterns(Patterns, PatternSize, h, numpat); - // ============================================================ - // set panning positions - for(t=0; t<(int)m_nChannels; t++) { - ChnSettings[t].nPan = 0x30+((t+2)%5)*((0xD0 - 0x30)/5); // 0x30 = std s3m val - ChnSettings[t].nVolume = 64; - } -#endif - avoid_reentry = 0; // it is safe now, I'm finished -#ifndef NEWMIKMOD - PAT_Cleanup(h); // we dont need it anymore -#endif - return 1; -} - -#ifdef NEWMIKMOD -// ===================================================================================== -CHAR *PAT_LoadTitle(MMSTREAM *mmfile) -// ===================================================================================== -{ - PATHANDLE dummy; - pat_read_patname(&dummy, mmfile); - return(DupStr(NULL, dummy.patname,strlen(dummy.patname))); -} - -MLOADER load_pat = -{ - "PAT", - "PAT loader 1.0", - 0x30, - NULL, - PAT_Test, - (void *(*)(void))PAT_Init, - (void (*)(ML_HANDLE *))PAT_Cleanup, - /* Every single loader seems to need one of these! */ - (BOOL (*)(ML_HANDLE *, UNIMOD *, MMSTREAM *))PAT_Load, - PAT_LoadTitle -}; -#endif diff --git a/src/modplug/load_pat.h b/src/modplug/load_pat.h deleted file mode 100644 index 21db1f7b..00000000 --- a/src/modplug/load_pat.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef LOAD_PAT_H -#define LOAD_PAT_H - -#ifdef __cplusplus -extern "C" { -#endif - -void pat_init_patnames(void); -void pat_resetsmp(void); -int pat_numinstr(void); -int pat_numsmp(void); -int pat_smptogm(int smp); -int pat_gmtosmp(int gm); -int pat_gm_drumnr(int n); -int pat_gm_drumnote(int n); -const char *pat_gm_name(int gm); -int pat_modnote(int midinote); -int pat_smplooped(int smp); -//#ifdef NEWMIKMOD -BOOL PAT_Load_Instruments(void *c); -//#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/modplug/load_psm.cpp b/src/modplug/load_psm.cpp deleted file mode 100644 index 19966a68..00000000 --- a/src/modplug/load_psm.cpp +++ /dev/null @@ -1,839 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque -*/ - - -/////////////////////////////////////////////////// -// -// PSM module loader -// -/////////////////////////////////////////////////// -#include "stdafx.h" -#include "sndfile.h" - -//#define PSM_LOG - -#define PSM_ID_NEW 0x204d5350 -#define PSM_ID_OLD 0xfe4d5350 -#define IFFID_FILE 0x454c4946 -#define IFFID_TITL 0x4c544954 -#define IFFID_SDFT 0x54464453 -#define IFFID_PBOD 0x444f4250 -#define IFFID_SONG 0x474e4f53 -#define IFFID_PATT 0x54544150 -#define IFFID_DSMP 0x504d5344 -#define IFFID_OPLH 0x484c504f - -#pragma pack(1) - -typedef struct _PSMCHUNK -{ - DWORD id; - DWORD len; - DWORD listid; -} PSMCHUNK; - -typedef struct _PSMSONGHDR -{ - CHAR songname[8]; // "MAINSONG" - BYTE reserved1; - BYTE reserved2; - BYTE channels; -} PSMSONGHDR; - -typedef struct _PSMPATTERN -{ - DWORD size; - DWORD name; - WORD rows; - WORD reserved1; - BYTE data[4]; -} PSMPATTERN; - -typedef struct _PSMSAMPLE -{ - BYTE flags; - CHAR songname[8]; - DWORD smpid; - CHAR samplename[34]; - DWORD reserved1; - BYTE reserved2; - BYTE insno; - BYTE reserved3; - DWORD length; - DWORD loopstart; - DWORD loopend; - WORD reserved4; - BYTE defvol; - DWORD reserved5; - DWORD samplerate; - BYTE reserved6[19]; -} PSMSAMPLE; - -#pragma pack() - - -BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) -//----------------------------------------------------------- -{ - PSMCHUNK *pfh = (PSMCHUNK *)lpStream; - DWORD dwMemPos, dwSongPos; - DWORD smpnames[MAX_SAMPLES]; - DWORD patptrs[MAX_PATTERNS]; - BYTE samplemap[MAX_SAMPLES]; - UINT nPatterns; - - // Chunk0: "PSM ",filesize,"FILE" - if (dwMemLength < 256) return FALSE; - if (pfh->id == PSM_ID_OLD) - { - #ifdef PSM_LOG - Log("Old PSM format not supported\n"); - #endif - return FALSE; - } - if ((pfh->id != PSM_ID_NEW) || (pfh->len+12 > dwMemLength) || (pfh->listid != IFFID_FILE)) return FALSE; - m_nType = MOD_TYPE_PSM; - m_nChannels = 16; - m_nSamples = 0; - nPatterns = 0; - dwMemPos = 12; - dwSongPos = 0; - for (UINT iChPan=0; iChPan<16; iChPan++) - { - UINT pan = (((iChPan & 3) == 1) || ((iChPan&3)==2)) ? 0xC0 : 0x40; - ChnSettings[iChPan].nPan = pan; - } - while (dwMemPos+8 < dwMemLength) - { - PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); - if ((pchunk->len >= dwMemLength - 8) || (dwMemPos + pchunk->len + 8 > dwMemLength)) break; - dwMemPos += 8; - PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); - ULONG len = pchunk->len; - if (len) switch(pchunk->id) - { - // "TITL": Song title - case IFFID_TITL: - if (!pdata[0]) { pdata++; len--; } - memcpy(m_szNames[0], pdata, (len>31) ? 31 : len); - m_szNames[0][31] = 0; - break; - // "PBOD": Pattern - case IFFID_PBOD: - if ((len >= 12) && (nPatterns < MAX_PATTERNS)) - { - patptrs[nPatterns++] = dwMemPos-8; - } - break; - // "SONG": Song description - case IFFID_SONG: - if ((len >= sizeof(PSMSONGHDR)+8) && (!dwSongPos)) - { - dwSongPos = dwMemPos - 8; - } - break; - // "DSMP": Sample Data - case IFFID_DSMP: - if ((len >= sizeof(PSMSAMPLE)) && (m_nSamples+1 < MAX_SAMPLES)) - { - m_nSamples++; - MODINSTRUMENT *pins = &Ins[m_nSamples]; - PSMSAMPLE *psmp = (PSMSAMPLE *)pdata; - smpnames[m_nSamples] = psmp->smpid; - memcpy(m_szNames[m_nSamples], psmp->samplename, 31); - m_szNames[m_nSamples][31] = 0; - samplemap[m_nSamples-1] = (BYTE)m_nSamples; - // Init sample - pins->nGlobalVol = 0x40; - pins->nC4Speed = psmp->samplerate; - pins->nLength = psmp->length; - pins->nLoopStart = psmp->loopstart; - pins->nLoopEnd = psmp->loopend; - pins->nPan = 128; - pins->nVolume = (psmp->defvol+1) * 2; - pins->uFlags = (psmp->flags & 0x80) ? CHN_LOOP : 0; - if (pins->nLoopStart > 0) pins->nLoopStart--; - // Point to sample data - pdata += 0x60; - len -= 0x60; - // Load sample data - if ((pins->nLength > 3) && (len > 3)) - { - ReadSample(pins, RS_PCM8D, (LPCSTR)pdata, len); - } else - { - pins->nLength = 0; - } - } - break; - #if 0 - default: - { - CHAR s[8], s2[64]; - *(DWORD *)s = pchunk->id; - s[4] = 0; - wsprintf(s2, "%s: %4d bytes @ %4d\n", s, pchunk->len, dwMemPos); - OutputDebugString(s2); - } - #endif - } - dwMemPos += pchunk->len; - } - // Step #1: convert song structure - PSMSONGHDR *pSong = (PSMSONGHDR *)(lpStream+dwSongPos+8); - if ((!dwSongPos) || (pSong->channels < 2) || (pSong->channels > 32)) return TRUE; - m_nChannels = pSong->channels; - // Valid song header -> convert attached chunks - { - DWORD dwSongEnd = dwSongPos + 8 + *(DWORD *)(lpStream+dwSongPos+4); - dwMemPos = dwSongPos + 8 + 11; // sizeof(PSMCHUNK)+sizeof(PSMSONGHDR) - while (dwMemPos + 8 < dwSongEnd) - { - PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); - dwMemPos += 8; - if ((pchunk->len > dwSongEnd) || (dwMemPos + pchunk->len > dwSongEnd)) break; - PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); - ULONG len = pchunk->len; - switch(pchunk->id) - { - case IFFID_OPLH: - if (len >= 0x20) - { - UINT pos = len - 3; - while (pos > 5) - { - BOOL bFound = FALSE; - pos -= 5; - DWORD dwName = *(DWORD *)(pdata+pos); - for (UINT i=0; iname; - if (dwName == dwPatName) - { - bFound = TRUE; - break; - } - } - if ((!bFound) && (pdata[pos+1] > 0) && (pdata[pos+1] <= 0x10) - && (pdata[pos+3] > 0x40) && (pdata[pos+3] < 0xC0)) - { - m_nDefaultSpeed = pdata[pos+1]; - m_nDefaultTempo = pdata[pos+3]; - break; - } - } - UINT iOrd = 0; - while ((pos+5name; - if (dwName == dwPatName) - { - Order[iOrd++] = i; - break; - } - } - pos += 5; - } - } - break; - } - dwMemPos += pchunk->len; - } - } - - // Step #2: convert patterns - for (UINT nPat=0; nPatrows; - if (len > pPsmPat->size) len = pPsmPat->size; - if ((nRows < 64) || (nRows > 256)) nRows = 64; - PatternSize[nPat] = nRows; - if ((Patterns[nPat] = AllocatePattern(nRows, m_nChannels)) == NULL) break; - MODCOMMAND *m = Patterns[nPat]; - BYTE *p = pPsmPat->data; - UINT pos = 0; - UINT row = 0; - UINT oldch = 0; - BOOL bNewRow = FALSE; - #ifdef PSM_LOG - Log("Pattern %d at offset 0x%04X\n", nPat, (DWORD)(p - (BYTE *)lpStream)); - #endif - while ((row < nRows) && (pos+1 < len)) - { - UINT flags = p[pos++]; - UINT ch = p[pos++]; - - #ifdef PSM_LOG - //Log("flags+ch: %02X.%02X\n", flags, ch); - #endif - if (((flags & 0xf0) == 0x10) && (ch <= oldch) /*&& (!bNewRow)*/) - { - if ((pos+1= len) || (row >= nRows)) break; - if (!(flags & 0xf0)) - { - #ifdef PSM_LOG - //if (!nPat) Log("EOR(%d): %02X.%02X\n", row, p[pos], p[pos+1]); - #endif - row++; - m += m_nChannels; - bNewRow = TRUE; - oldch = ch; - continue; - } - bNewRow = FALSE; - if (ch >= m_nChannels) - { - #ifdef PSM_LOG - if (!nPat) Log("Invalid channel row=%d (0x%02X.0x%02X)\n", row, flags, ch); - #endif - ch = 0; - } - // Note + Instr - if ((flags & 0x40) && (pos+1 < len)) - { - UINT note = p[pos++]; - UINT nins = p[pos++]; - #ifdef PSM_LOG - //if (!nPat) Log("note+ins: %02X.%02X\n", note, nins); - if ((!nPat) && (nins >= m_nSamples)) Log("WARNING: invalid instrument number (%d)\n", nins); - #endif - if ((note) && (note < 0x80)) note = (note>>4)*12+(note&0x0f)+12+1; - m[ch].instr = samplemap[nins]; - m[ch].note = note; - } - // Volume - if ((flags & 0x20) && (pos < len)) - { - m[ch].volcmd = VOLCMD_VOLUME; - m[ch].vol = p[pos++] / 2; - } - // Effect - if ((flags & 0x10) && (pos+1 < len)) - { - UINT command = p[pos++]; - UINT param = p[pos++]; - // Convert effects - switch(command) - { - // 01: fine volslide up - case 0x01: command = CMD_VOLUMESLIDE; param |= 0x0f; break; - // 04: fine volslide down - case 0x04: command = CMD_VOLUMESLIDE; param>>=4; param |= 0xf0; break; - // 0C: portamento up - case 0x0C: command = CMD_PORTAMENTOUP; param = (param+1)/2; break; - // 0E: portamento down - case 0x0E: command = CMD_PORTAMENTODOWN; param = (param+1)/2; break; - // 33: Position Jump - case 0x33: command = CMD_POSITIONJUMP; break; - // 34: Pattern break - case 0x34: command = CMD_PATTERNBREAK; break; - // 3D: speed - case 0x3D: command = CMD_SPEED; break; - // 3E: tempo - case 0x3E: command = CMD_TEMPO; break; - // Unknown - default: - #ifdef PSM_LOG - Log("Unknown PSM effect pat=%d row=%d ch=%d: %02X.%02X\n", nPat, row, ch, command, param); - #endif - command = param = 0; - } - m[ch].command = (BYTE)command; - m[ch].param = (BYTE)param; - } - oldch = ch; - } - #ifdef PSM_LOG - if (pos < len) - { - Log("Pattern %d: %d/%d[%d] rows (%d bytes) -> %d bytes left\n", nPat, row, nRows, pPsmPat->rows, pPsmPat->size, len-pos); - } - #endif - } - - // Done (finally!) - return TRUE; -} - - -////////////////////////////////////////////////////////////// -// -// PSM Old Format -// - -/* - -CONST - c_PSM_MaxOrder = $FF; - c_PSM_MaxSample = $FF; - c_PSM_MaxChannel = $0F; - - TYPE - PPSM_Header = ^TPSM_Header; - TPSM_Header = RECORD - PSM_Sign : ARRAY[01..04] OF CHAR; { PSM + #254 } - PSM_SongName : ARRAY[01..58] OF CHAR; - PSM_Byte00 : BYTE; - PSM_Byte1A : BYTE; - PSM_Unknown00 : BYTE; - PSM_Unknown01 : BYTE; - PSM_Unknown02 : BYTE; - PSM_Speed : BYTE; - PSM_Tempo : BYTE; - PSM_Unknown03 : BYTE; - PSM_Unknown04 : WORD; - PSM_OrderLength : WORD; - PSM_PatternNumber : WORD; - PSM_SampleNumber : WORD; - PSM_ChannelNumber : WORD; - PSM_ChannelUsed : WORD; - PSM_OrderPosition : LONGINT; - PSM_ChannelSettingPosition : LONGINT; - PSM_PatternPosition : LONGINT; - PSM_SamplePosition : LONGINT; - { *** perhaps there are some more infos in a larger header, - but i have not decoded it and so it apears here NOT } - END; - - PPSM_Sample = ^TPSM_Sample; - TPSM_Sample = RECORD - PSM_SampleFileName : ARRAY[01..12] OF CHAR; - PSM_SampleByte00 : BYTE; - PSM_SampleName : ARRAY[01..22] OF CHAR; - PSM_SampleUnknown00 : ARRAY[01..02] OF BYTE; - PSM_SamplePosition : LONGINT; - PSM_SampleUnknown01 : ARRAY[01..04] OF BYTE; - PSM_SampleNumber : BYTE; - PSM_SampleFlags : WORD; - PSM_SampleLength : LONGINT; - PSM_SampleLoopBegin : LONGINT; - PSM_SampleLoopEnd : LONGINT; - PSM_Unknown03 : BYTE; - PSM_SampleVolume : BYTE; - PSM_SampleC5Speed : WORD; - END; - - PPSM_SampleList = ^TPSM_SampleList; - TPSM_SampleList = ARRAY[01..c_PSM_MaxSample] OF TPSM_Sample; - - PPSM_Order = ^TPSM_Order; - TPSM_Order = ARRAY[00..c_PSM_MaxOrder] OF BYTE; - - PPSM_ChannelSettings = ^TPSM_ChannelSettings; - TPSM_ChannelSettings = ARRAY[00..c_PSM_MaxChannel] OF BYTE; - - CONST - PSM_NotesInPattern : BYTE = $00; - PSM_ChannelInPattern : BYTE = $00; - - CONST - c_PSM_SetSpeed = 60; - - FUNCTION PSM_Size(FileName : STRING;FilePosition : LONGINT) : LONGINT; - BEGIN - END; - - PROCEDURE PSM_UnpackPattern(VAR Source,Destination;PatternLength : WORD); - VAR - Witz : ARRAY[00..04] OF WORD; - I1,I2 : WORD; - I3,I4 : WORD; - TopicalByte : ^BYTE; - Pattern : PUnpackedPattern; - ChannelP : BYTE; - NoteP : BYTE; - InfoByte : BYTE; - CodeByte : BYTE; - InfoWord : WORD; - Effect : BYTE; - Opperand : BYTE; - Panning : BYTE; - Volume : BYTE; - PrevInfo : BYTE; - InfoIndex : BYTE; - BEGIN - Pattern := @Destination; - TopicalByte := @Source; - { *** Initialize patttern } - FOR I2 := 0 TO c_Maximum_NoteIndex DO - FOR I3 := 0 TO c_Maximum_ChannelIndex DO - BEGIN - Pattern^[I2,I3,c_Pattern_NoteIndex] := $FF; - Pattern^[I2,I3,c_Pattern_SampleIndex] := $00; - Pattern^[I2,I3,c_Pattern_VolumeIndex] := $FF; - Pattern^[I2,I3,c_Pattern_PanningIndex] := $FF; - Pattern^[I2,I3,c_Pattern_EffectIndex] := $00; - Pattern^[I2,I3,c_Pattern_OpperandIndex] := $00; - END; - { *** Byte-pointer on first pattern-entry } - ChannelP := $00; - NoteP := $00; - InfoByte := $00; - PrevInfo := $00; - InfoIndex := $02; - { *** read notes in pattern } - PSM_NotesInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); - PSM_ChannelInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); - { *** unpack pattern } - WHILE (INTEGER(PatternLength) > 0) AND (NoteP < c_Maximum_NoteIndex) DO - BEGIN - { *** Read info-byte } - InfoByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); - IF InfoByte <> $00 THEN - BEGIN - ChannelP := InfoByte AND $0F; - IF InfoByte AND 128 = 128 THEN { note and sample } - BEGIN - { *** read note } - CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); - DEC(CodeByte); - CodeByte := CodeByte MOD 12 * 16 + CodeByte DIV 12 + 2; - Pattern^[NoteP,ChannelP,c_Pattern_NoteIndex] := CodeByte; - { *** read sample } - CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); - Pattern^[NoteP,ChannelP,c_Pattern_SampleIndex] := CodeByte; - END; - IF InfoByte AND 64 = 64 THEN { Volume } - BEGIN - CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); - Pattern^[NoteP,ChannelP,c_Pattern_VolumeIndex] := CodeByte; - END; - IF InfoByte AND 32 = 32 THEN { effect AND opperand } - BEGIN - Effect := TopicalByte^; INC(TopicalByte); DEC(PatternLength); - Opperand := TopicalByte^; INC(TopicalByte); DEC(PatternLength); - CASE Effect OF - c_PSM_SetSpeed: - BEGIN - Effect := c_I_Set_Speed; - END; - ELSE - BEGIN - Effect := c_I_NoEffect; - Opperand := $00; - END; - END; - Pattern^[NoteP,ChannelP,c_Pattern_EffectIndex] := Effect; - Pattern^[NoteP,ChannelP,c_Pattern_OpperandIndex] := Opperand; - END; - END ELSE INC(NoteP); - END; - END; - - PROCEDURE PSM_Load(FileName : STRING;FilePosition : LONGINT;VAR Module : PModule;VAR ErrorCode : WORD); - { *** caution : Module has to be inited before!!!! } - VAR - Header : PPSM_Header; - Sample : PPSM_SampleList; - Order : PPSM_Order; - ChannelSettings : PPSM_ChannelSettings; - MultiPurposeBuffer : PByteArray; - PatternBuffer : PUnpackedPattern; - TopicalParaPointer : WORD; - - InFile : FILE; - I1,I2 : WORD; - I3,I4 : WORD; - TempW : WORD; - TempB : BYTE; - TempP : PByteArray; - TempI : INTEGER; - { *** copy-vars for loop-extension } - CopySource : LONGINT; - CopyDestination : LONGINT; - CopyLength : LONGINT; - BEGIN - { *** try to open file } - ASSIGN(InFile,FileName); -{$I-} - RESET(InFile,1); -{$I+} - IF IORESULT <> $00 THEN - BEGIN - EXIT; - END; -{$I-} - { *** seek start of module } - IF FILESIZE(InFile) < FilePosition THEN - BEGIN - EXIT; - END; - SEEK(InFile,FilePosition); - { *** look for enough memory for temporary variables } - IF MEMAVAIL < SIZEOF(TPSM_Header) + SIZEOF(TPSM_SampleList) + - SIZEOF(TPSM_Order) + SIZEOF(TPSM_ChannelSettings) + - SIZEOF(TByteArray) + SIZEOF(TUnpackedPattern) - THEN - BEGIN - EXIT; - END; - { *** init dynamic variables } - NEW(Header); - NEW(Sample); - NEW(Order); - NEW(ChannelSettings); - NEW(MultiPurposeBuffer); - NEW(PatternBuffer); - { *** read header } - BLOCKREAD(InFile,Header^,SIZEOF(TPSM_Header)); - { *** test if this is a DSM-file } - IF NOT ((Header^.PSM_Sign[1] = 'P') AND (Header^.PSM_Sign[2] = 'S') AND - (Header^.PSM_Sign[3] = 'M') AND (Header^.PSM_Sign[4] = #254)) THEN - BEGIN - ErrorCode := c_NoValidFileFormat; - CLOSE(InFile); - EXIT; - END; - { *** read order } - SEEK(InFile,FilePosition + Header^.PSM_OrderPosition); - BLOCKREAD(InFile,Order^,Header^.PSM_OrderLength); - { *** read channelsettings } - SEEK(InFile,FilePosition + Header^.PSM_ChannelSettingPosition); - BLOCKREAD(InFile,ChannelSettings^,SIZEOF(TPSM_ChannelSettings)); - { *** read samplelist } - SEEK(InFile,FilePosition + Header^.PSM_SamplePosition); - BLOCKREAD(InFile,Sample^,Header^.PSM_SampleNumber * SIZEOF(TPSM_Sample)); - { *** copy header to intern NTMIK-structure } - Module^.Module_Sign := 'MF'; - Module^.Module_FileFormatVersion := $0100; - Module^.Module_SampleNumber := Header^.PSM_SampleNumber; - Module^.Module_PatternNumber := Header^.PSM_PatternNumber; - Module^.Module_OrderLength := Header^.PSM_OrderLength; - Module^.Module_ChannelNumber := Header^.PSM_ChannelNumber+1; - Module^.Module_Initial_GlobalVolume := 64; - Module^.Module_Initial_MasterVolume := $C0; - Module^.Module_Initial_Speed := Header^.PSM_Speed; - Module^.Module_Initial_Tempo := Header^.PSM_Tempo; -{ *** paragraph 01 start } - Module^.Module_Flags := c_Module_Flags_ZeroVolume * BYTE(1) + - c_Module_Flags_Stereo * BYTE(1) + - c_Module_Flags_ForceAmigaLimits * BYTE(0) + - c_Module_Flags_Panning * BYTE(1) + - c_Module_Flags_Surround * BYTE(1) + - c_Module_Flags_QualityMixing * BYTE(1) + - c_Module_Flags_FastVolumeSlides * BYTE(0) + - c_Module_Flags_SpecialCustomData * BYTE(0) + - c_Module_Flags_SongName * BYTE(1); - I1 := $01; - WHILE (Header^.PSM_SongName[I1] > #00) AND (I1 < c_Module_SongNameLength) DO - BEGIN - Module^.Module_Name[I1] := Header^.PSM_SongName[I1]; - INC(I1); - END; - Module^.Module_Name[c_Module_SongNameLength] := #00; - { *** Init channelsettings } - FOR I1 := 0 TO c_Maximum_ChannelIndex DO - BEGIN - IF I1 < Header^.PSM_ChannelUsed THEN - BEGIN - { *** channel enabled } - Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := 64; - Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := (ChannelSettings^[I1]) * $08; - Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := I1 + $10 * BYTE(ChannelSettings^[I1] > $08) + - c_ChannelSettings_Code_ChannelEnabled * BYTE(1) + - c_ChannelSettings_Code_ChannelDigital * BYTE(1); - Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := - c_ChannelSettings_Controls_EnhancedMode * BYTE(1) + - c_ChannelSettings_Controls_SurroundMode * BYTE(0); - END - ELSE - BEGIN - { *** channel disabled } - Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := $00; - Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := $00; - Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := $00; - Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := $00; - END; - END; - { *** init and copy order } - FILLCHAR(Module^.Module_OrderPointer^,c_Maximum_OrderIndex+1,$FF); - MOVE(Order^,Module^.Module_OrderPointer^,Header^.PSM_OrderLength); - { *** read pattern } - SEEK(InFile,FilePosition + Header^.PSM_PatternPosition); - NTMIK_LoaderPatternNumber := Header^.PSM_PatternNumber-1; - FOR I1 := 0 TO Header^.PSM_PatternNumber-1 DO - BEGIN - NTMIK_LoadPatternProcedure; - { *** read length } - BLOCKREAD(InFile,TempW,2); - { *** read pattern } - BLOCKREAD(InFile,MultiPurposeBuffer^,TempW-2); - { *** unpack pattern and set notes per channel to 64 } - PSM_UnpackPattern(MultiPurposeBuffer^,PatternBuffer^,TempW); - NTMIK_PackPattern(MultiPurposeBuffer^,PatternBuffer^,PSM_NotesInPattern); - TempW := WORD(256) * MultiPurposeBuffer^[01] + MultiPurposeBuffer^[00]; - GETMEM(Module^.Module_PatternPointer^[I1],TempW); - MOVE(MultiPurposeBuffer^,Module^.Module_PatternPointer^[I1]^,TempW); - { *** next pattern } - END; - { *** read samples } - NTMIK_LoaderSampleNumber := Header^.PSM_SampleNumber; - FOR I1 := 1 TO Header^.PSM_SampleNumber DO - BEGIN - NTMIK_LoadSampleProcedure; - { *** get index for sample } - I3 := Sample^[I1].PSM_SampleNumber; - { *** clip PSM-sample } - IF Sample^[I1].PSM_SampleLoopEnd > Sample^[I1].PSM_SampleLength - THEN Sample^[I1].PSM_SampleLoopEnd := Sample^[I1].PSM_SampleLength; - { *** init intern sample } - NEW(Module^.Module_SamplePointer^[I3]); - FILLCHAR(Module^.Module_SamplePointer^[I3]^,SIZEOF(TSample),$00); - FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_SampleName,c_Sample_SampleNameLength,#32); - FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_FileName,c_Sample_FileNameLength,#32); - { *** copy informations to intern sample } - I2 := $01; - WHILE (Sample^[I1].PSM_SampleName[I2] > #00) AND (I2 < c_Sample_SampleNameLength) DO - BEGIN - Module^.Module_SamplePointer^[I3]^.Sample_SampleName[I2] := Sample^[I1].PSM_SampleName[I2]; - INC(I2); - END; - Module^.Module_SamplePointer^[I3]^.Sample_Sign := 'DF'; - Module^.Module_SamplePointer^[I3]^.Sample_FileFormatVersion := $00100; - Module^.Module_SamplePointer^[I3]^.Sample_Position := $00000000; - Module^.Module_SamplePointer^[I3]^.Sample_Selector := $0000; - Module^.Module_SamplePointer^[I3]^.Sample_Volume := Sample^[I1].PSM_SampleVolume; - Module^.Module_SamplePointer^[I3]^.Sample_LoopCounter := $00; - Module^.Module_SamplePointer^[I3]^.Sample_C5Speed := Sample^[I1].PSM_SampleC5Speed; - Module^.Module_SamplePointer^[I3]^.Sample_Length := Sample^[I1].PSM_SampleLength; - Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin := Sample^[I1].PSM_SampleLoopBegin; - Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd := Sample^[I1].PSM_SampleLoopEnd; - { *** now it's time for the flags } - Module^.Module_SamplePointer^[I3]^.Sample_Flags := - c_Sample_Flags_DigitalSample * BYTE(1) + - c_Sample_Flags_8BitSample * BYTE(1) + - c_Sample_Flags_UnsignedSampleData * BYTE(1) + - c_Sample_Flags_Packed * BYTE(0) + - c_Sample_Flags_LoopCounter * BYTE(0) + - c_Sample_Flags_SampleName * BYTE(1) + - c_Sample_Flags_LoopActive * - BYTE(Sample^[I1].PSM_SampleFlags AND (LONGINT(1) SHL 15) = (LONGINT(1) SHL 15)); - { *** alloc memory for sample-data } - E_Getmem(Module^.Module_SamplePointer^[I3]^.Sample_Selector, - Module^.Module_SamplePointer^[I3]^.Sample_Position, - Module^.Module_SamplePointer^[I3]^.Sample_Length + c_LoopExtensionSize); - { *** read out data } - EPT(TempP).p_Selector := Module^.Module_SamplePointer^[I3]^.Sample_Selector; - EPT(TempP).p_Offset := $0000; - SEEK(InFile,Sample^[I1].PSM_SamplePosition); - E_BLOCKREAD(InFile,TempP^,Module^.Module_SamplePointer^[I3]^.Sample_Length); - { *** 'coz the samples are signed in a DSM-file -> PC-fy them } - IF Module^.Module_SamplePointer^[I3]^.Sample_Length > 4 THEN - BEGIN - CopyLength := Module^.Module_SamplePointer^[I3]^.Sample_Length; - { *** decode sample } - ASM - DB 066h; MOV CX,WORD PTR CopyLength - { *** load sample selector } - MOV ES,WORD PTR TempP[00002h] - DB 066h; XOR SI,SI - DB 066h; XOR DI,DI - XOR AH,AH - { *** conert all bytes } - @@MainLoop: - DB 026h; DB 067h; LODSB - ADD AL,AH - MOV AH,AL - DB 067h; STOSB - DB 066h; LOOP @@MainLoop - END; - { *** make samples unsigned } - ASM - DB 066h; MOV CX,WORD PTR CopyLength - { *** load sample selector } - MOV ES,WORD PTR TempP[00002h] - DB 066h; XOR SI,SI - DB 066h; XOR DI,DI - { *** conert all bytes } - @@MainLoop: - DB 026h; DB 067h; LODSB - SUB AL,080h - DB 067h; STOSB - DB 066h; LOOP @@MainLoop - END; - { *** Create Loop-Extension } - IF Module^.Module_SamplePointer^[I3]^.Sample_Flags AND c_Sample_Flags_LoopActive = c_Sample_Flags_LoopActive THEN - BEGIN - CopySource := Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin; - CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd; - CopyLength := CopyDestination - CopySource; - ASM - { *** load sample-selector } - MOV ES,WORD PTR TempP[00002h] - DB 066h; MOV DI,WORD PTR CopyDestination - { *** calculate number of full sample-loops to copy } - XOR DX,DX - MOV AX,c_LoopExtensionSize - MOV BX,WORD PTR CopyLength - DIV BX - OR AX,AX - JE @@NoFullLoop - { *** copy some full-loops (size=bx) } - MOV CX,AX - @@InnerLoop: - PUSH CX - DB 066h; MOV SI,WORD PTR CopySource - MOV CX,BX - DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } - POP CX - LOOP @@InnerLoop - @@NoFullLoop: - { *** calculate number of rest-bytes to copy } - DB 066h; MOV SI,WORD PTR CopySource - MOV CX,DX - DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } - END; - END - ELSE - BEGIN - CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_Length; - ASM - { *** load sample-selector } - MOV ES,WORD PTR TempP[00002h] - DB 066h; MOV DI,WORD PTR CopyDestination - { *** clear extension } - MOV CX,c_LoopExtensionSize - MOV AL,080h - DB 0F3h; DB 067h,0AAh { REP STOS BYTE PTR ES:[EDI] } - END; - END; - END; - { *** next sample } - END; - { *** init period-ranges } - NTMIK_MaximumPeriod := $0000D600 SHR 1; - NTMIK_MinimumPeriod := $0000D600 SHR 8; - { *** close file } - CLOSE(InFile); - { *** dispose all dynamic variables } - DISPOSE(Header); - DISPOSE(Sample); - DISPOSE(Order); - DISPOSE(ChannelSettings); - DISPOSE(MultiPurposeBuffer); - DISPOSE(PatternBuffer); - { *** set errorcode to noerror } - ErrorCode := c_NoError; - END; - -*/ - diff --git a/src/modplug/load_ptm.cpp b/src/modplug/load_ptm.cpp deleted file mode 100644 index 28039153..00000000 --- a/src/modplug/load_ptm.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque , - * Adam Goode (endian and char fixes for PPC) -*/ - -////////////////////////////////////////////// -// PTM PolyTracker module loader // -////////////////////////////////////////////// -#include "stdafx.h" -#include "sndfile.h" - -//#pragma warning(disable:4244) - -#pragma pack(1) - -typedef struct PTMFILEHEADER -{ - CHAR songname[28]; // name of song, asciiz string - CHAR eof; // 26 - BYTE version_lo; // 03 version of file, currently 0203h - BYTE version_hi; // 02 - BYTE reserved1; // reserved, set to 0 - WORD norders; // number of orders (0..256) - WORD nsamples; // number of instruments (1..255) - WORD npatterns; // number of patterns (1..128) - WORD nchannels; // number of channels (voices) used (1..32) - WORD fileflags; // set to 0 - WORD reserved2; // reserved, set to 0 - DWORD ptmf_id; // song identification, 'PTMF' or 0x464d5450 - BYTE reserved3[16]; // reserved, set to 0 - BYTE chnpan[32]; // channel panning settings, 0..15, 0 = left, 7 = middle, 15 = right - BYTE orders[256]; // order list, valid entries 0..nOrders-1 - WORD patseg[128]; // pattern offsets (*16) -} PTMFILEHEADER, *LPPTMFILEHEADER; - -#define SIZEOF_PTMFILEHEADER 608 - - -typedef struct PTMSAMPLE -{ - BYTE sampletype; // sample type (bit array) - CHAR filename[12]; // name of external sample file - BYTE volume; // default volume - WORD nC4Spd; // C4 speed - WORD sampleseg; // sample segment (used internally) - WORD fileofs[2]; // offset of sample data - WORD length[2]; // sample size (in bytes) - WORD loopbeg[2]; // start of loop - WORD loopend[2]; // end of loop - WORD gusdata[8]; - char samplename[28]; // name of sample, asciiz // changed from CHAR - DWORD ptms_id; // sample identification, 'PTMS' or 0x534d5450 -} PTMSAMPLE; - -#define SIZEOF_PTMSAMPLE 80 - -#pragma pack() - - -BOOL CSoundFile::ReadPTM(const BYTE *lpStream, DWORD dwMemLength) -//--------------------------------------------------------------- -{ - PTMFILEHEADER pfh = *(LPPTMFILEHEADER)lpStream; - DWORD dwMemPos; - UINT nOrders; - - pfh.norders = bswapLE16(pfh.norders); - pfh.nsamples = bswapLE16(pfh.nsamples); - pfh.npatterns = bswapLE16(pfh.npatterns); - pfh.nchannels = bswapLE16(pfh.nchannels); - pfh.fileflags = bswapLE16(pfh.fileflags); - pfh.reserved2 = bswapLE16(pfh.reserved2); - pfh.ptmf_id = bswapLE32(pfh.ptmf_id); - for (UINT j=0; j<128; j++) - { - pfh.patseg[j] = bswapLE16(pfh.patseg[j]); - } - - if ((!lpStream) || (dwMemLength < 1024)) return FALSE; - if ((pfh.ptmf_id != 0x464d5450) || (!pfh.nchannels) - || (pfh.nchannels > 32) - || (pfh.norders > 256) || (!pfh.norders) - || (!pfh.nsamples) || (pfh.nsamples > 255) - || (!pfh.npatterns) || (pfh.npatterns > 128) - || (SIZEOF_PTMFILEHEADER+pfh.nsamples*SIZEOF_PTMSAMPLE >= (int)dwMemLength)) return FALSE; - memcpy(m_szNames[0], pfh.songname, 28); - m_szNames[0][28] = 0; - m_nType = MOD_TYPE_PTM; - m_nChannels = pfh.nchannels; - m_nSamples = (pfh.nsamples < MAX_SAMPLES) ? pfh.nsamples : MAX_SAMPLES-1; - dwMemPos = SIZEOF_PTMFILEHEADER; - nOrders = (pfh.norders < MAX_ORDERS) ? pfh.norders : MAX_ORDERS-1; - memcpy(Order, pfh.orders, nOrders); - for (UINT ipan=0; ipansamplename, 28); - memcpy(pins->name, psmp->filename, 12); - pins->name[12] = 0; - pins->nGlobalVol = 64; - pins->nPan = 128; - pins->nVolume = psmp->volume << 2; - pins->nC4Speed = bswapLE16(psmp->nC4Spd) << 1; - pins->uFlags = 0; - if ((psmp->sampletype & 3) == 1) - { - UINT smpflg = RS_PCM8D; - DWORD samplepos; - pins->nLength = bswapLE32(*(LPDWORD)(psmp->length)); - pins->nLoopStart = bswapLE32(*(LPDWORD)(psmp->loopbeg)); - pins->nLoopEnd = bswapLE32(*(LPDWORD)(psmp->loopend)); - samplepos = bswapLE32(*(LPDWORD)(&psmp->fileofs)); - if (psmp->sampletype & 4) pins->uFlags |= CHN_LOOP; - if (psmp->sampletype & 8) pins->uFlags |= CHN_PINGPONGLOOP; - if (psmp->sampletype & 16) - { - pins->uFlags |= CHN_16BIT; - pins->nLength >>= 1; - pins->nLoopStart >>= 1; - pins->nLoopEnd >>= 1; - smpflg = RS_PTM8DTO16; - } - if ((pins->nLength) && (samplepos) && (samplepos < dwMemLength)) - { - ReadSample(pins, smpflg, (LPSTR)(lpStream+samplepos), dwMemLength-samplepos); - } - } - } - // Reading Patterns - for (UINT ipat=0; ipat= dwMemLength)) continue; - PatternSize[ipat] = 64; - if ((Patterns[ipat] = AllocatePattern(64, m_nChannels)) == NULL) break; - // - MODCOMMAND *m = Patterns[ipat]; - for (UINT row=0; ((row < 64) && (dwMemPos < dwMemLength)); ) - { - UINT b = lpStream[dwMemPos++]; - - if (dwMemPos >= dwMemLength) break; - if (b) - { - UINT nChn = b & 0x1F; - - if (b & 0x20) - { - if (dwMemPos + 2 > dwMemLength) break; - m[nChn].note = lpStream[dwMemPos++]; - m[nChn].instr = lpStream[dwMemPos++]; - } - if (b & 0x40) - { - if (dwMemPos + 2 > dwMemLength) break; - m[nChn].command = lpStream[dwMemPos++]; - m[nChn].param = lpStream[dwMemPos++]; - if ((m[nChn].command == 0x0E) && ((m[nChn].param & 0xF0) == 0x80)) - { - m[nChn].command = CMD_S3MCMDEX; - } else - if (m[nChn].command < 0x10) - { - ConvertModCommand(&m[nChn]); - } else - { - switch(m[nChn].command) - { - case 16: - m[nChn].command = CMD_GLOBALVOLUME; - break; - case 17: - m[nChn].command = CMD_RETRIG; - break; - case 18: - m[nChn].command = CMD_FINEVIBRATO; - break; - default: - m[nChn].command = 0; - } - } - } - if (b & 0x80) - { - if (dwMemPos >= dwMemLength) break; - m[nChn].volcmd = VOLCMD_VOLUME; - m[nChn].vol = lpStream[dwMemPos++]; - } - } else - { - row++; - m += m_nChannels; - } - } - } - return TRUE; -} - diff --git a/src/modplug/load_ult.cpp b/src/modplug/load_ult.cpp deleted file mode 100644 index 91e1b304..00000000 --- a/src/modplug/load_ult.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque -*/ - -#include "stdafx.h" -#include "sndfile.h" - -//#pragma warning(disable:4244) - -#define ULT_16BIT 0x04 -#define ULT_LOOP 0x08 -#define ULT_BIDI 0x10 - -#pragma pack(1) - -// Raw ULT header struct: -typedef struct tagULTHEADER -{ - char id[15]; // changed from CHAR - char songtitle[32]; // changed from CHAR - BYTE reserved; -} ULTHEADER; - - -// Raw ULT sampleinfo struct: -typedef struct tagULTSAMPLE -{ - CHAR samplename[32]; - CHAR dosname[12]; - LONG loopstart; - LONG loopend; - LONG sizestart; - LONG sizeend; - BYTE volume; - BYTE flags; - WORD finetune; -} ULTSAMPLE; - -#pragma pack() - - -BOOL CSoundFile::ReadUlt(const BYTE *lpStream, DWORD dwMemLength) -//--------------------------------------------------------------- -{ - ULTHEADER *pmh = (ULTHEADER *)lpStream; - ULTSAMPLE *pus; - UINT nos, nop; - DWORD dwMemPos = 0; - - // try to read module header - if ((!lpStream) || (dwMemLength < 0x100)) return FALSE; - if (strncmp(pmh->id,"MAS_UTrack_V00",14)) return FALSE; - // Warning! Not supported ULT format, trying anyway - // if ((pmh->id[14] < '1') || (pmh->id[14] > '4')) return FALSE; - m_nType = MOD_TYPE_ULT; - m_nDefaultSpeed = 6; - m_nDefaultTempo = 125; - memcpy(m_szNames[0], pmh->songtitle, 32); - // read songtext - dwMemPos = sizeof(ULTHEADER); - if ((pmh->reserved) && (dwMemPos + pmh->reserved * 32 < dwMemLength)) - { - UINT len = pmh->reserved * 32; - m_lpszSongComments = new char[len + 1 + pmh->reserved]; - if (m_lpszSongComments) - { - for (UINT l=0; lreserved; l++) - { - memcpy(m_lpszSongComments+l*33, lpStream+dwMemPos+l*32, 32); - m_lpszSongComments[l*33+32] = 0x0D; - } - m_lpszSongComments[len] = 0; - } - dwMemPos += len; - } - if (dwMemPos >= dwMemLength) return TRUE; - nos = lpStream[dwMemPos++]; - m_nSamples = nos; - if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1; - UINT smpsize = 64; - if (pmh->id[14] >= '4') smpsize += 2; - if (dwMemPos + nos*smpsize + 256 + 2 > dwMemLength) return TRUE; - for (UINT ins=1; ins<=nos; ins++, dwMemPos+=smpsize) if (ins<=m_nSamples) - { - pus = (ULTSAMPLE *)(lpStream+dwMemPos); - MODINSTRUMENT *pins = &Ins[ins]; - memcpy(m_szNames[ins], pus->samplename, 32); - memcpy(pins->name, pus->dosname, 12); - pins->nLoopStart = pus->loopstart; - pins->nLoopEnd = pus->loopend; - pins->nLength = pus->sizeend - pus->sizestart; - pins->nVolume = pus->volume; - pins->nGlobalVol = 64; - pins->nC4Speed = 8363; - if (pmh->id[14] >= '4') - { - pins->nC4Speed = pus->finetune; - } - if (pus->flags & ULT_LOOP) pins->uFlags |= CHN_LOOP; - if (pus->flags & ULT_BIDI) pins->uFlags |= CHN_PINGPONGLOOP; - if (pus->flags & ULT_16BIT) - { - pins->uFlags |= CHN_16BIT; - pins->nLoopStart >>= 1; - pins->nLoopEnd >>= 1; - } - } - memcpy(Order, lpStream+dwMemPos, 256); - dwMemPos += 256; - m_nChannels = lpStream[dwMemPos] + 1; - nop = lpStream[dwMemPos+1] + 1; - dwMemPos += 2; - if (m_nChannels > 32) m_nChannels = 32; - // Default channel settings - for (UINT nSet=0; nSetid[14]>='3') - { - if (dwMemPos + m_nChannels > dwMemLength) return TRUE; - for(UINT t=0; t 256) ChnSettings[t].nPan = 256; - } - } - // Allocating Patterns - for (UINT nAllocPat=0; nAllocPat dwMemLength) return TRUE; - UINT rep = 1; - UINT note = lpStream[dwMemPos++]; - if (note == 0xFC) - { - rep = lpStream[dwMemPos]; - note = lpStream[dwMemPos+1]; - dwMemPos += 2; - } - UINT instr = lpStream[dwMemPos++]; - UINT eff = lpStream[dwMemPos++]; - UINT dat1 = lpStream[dwMemPos++]; - UINT dat2 = lpStream[dwMemPos++]; - UINT cmd1 = eff & 0x0F; - UINT cmd2 = eff >> 4; - if (cmd1 == 0x0C) dat1 >>= 2; else - if (cmd1 == 0x0B) { cmd1 = dat1 = 0; } - if (cmd2 == 0x0C) dat2 >>= 2; else - if (cmd2 == 0x0B) { cmd2 = dat2 = 0; } - while ((rep != 0) && (row < 64)) - { - if (pat) - { - pat->instr = instr; - if (note) pat->note = note + 36; - if (cmd1 | dat1) - { - if (cmd1 == 0x0C) - { - pat->volcmd = VOLCMD_VOLUME; - pat->vol = dat1; - } else - { - pat->command = cmd1; - pat->param = dat1; - ConvertModCommand(pat); - } - } - if (cmd2 == 0x0C) - { - pat->volcmd = VOLCMD_VOLUME; - pat->vol = dat2; - } else - if ((cmd2 | dat2) && (!pat->command)) - { - pat->command = cmd2; - pat->param = dat2; - ConvertModCommand(pat); - } - pat += m_nChannels; - } - row++; - rep--; - } - } - } - } - // Reading Instruments - for (UINT smp=1; smp<=m_nSamples; smp++) if (Ins[smp].nLength) - { - if (dwMemPos >= dwMemLength) return TRUE; - UINT flags = (Ins[smp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S; - dwMemPos += ReadSample(&Ins[smp], flags, (LPSTR)(lpStream+dwMemPos), dwMemLength - dwMemPos); - } - return TRUE; -} - diff --git a/src/modplug/load_wav.cpp b/src/modplug/load_wav.cpp deleted file mode 100644 index 3bcd1328..00000000 --- a/src/modplug/load_wav.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* - * This source code is public domain. - * - * Authors: Olivier Lapicque -*/ - -#include "stdafx.h" -#include "sndfile.h" - -#ifndef WAVE_FORMAT_EXTENSIBLE -#define WAVE_FORMAT_EXTENSIBLE 0xFFFE -#endif - -///////////////////////////////////////////////////////////// -// WAV file support - -BOOL CSoundFile::ReadWav(const BYTE *lpStream, DWORD dwMemLength) -//--------------------------------------------------------------- -{ - DWORD dwMemPos = 0; - WAVEFILEHEADER *phdr = (WAVEFILEHEADER *)lpStream; - WAVEFORMATHEADER *pfmt = (WAVEFORMATHEADER *)(lpStream + sizeof(WAVEFILEHEADER)); - if ((!lpStream) || (dwMemLength < (DWORD)sizeof(WAVEFILEHEADER))) return FALSE; - if ((phdr->id_RIFF != IFFID_RIFF) || (phdr->id_WAVE != IFFID_WAVE) - || (pfmt->id_fmt != IFFID_fmt)) return FALSE; - dwMemPos = sizeof(WAVEFILEHEADER) + 8 + pfmt->hdrlen; - if ((dwMemPos + 8 >= dwMemLength) - || ((pfmt->format != WAVE_FORMAT_PCM) && (pfmt->format != WAVE_FORMAT_EXTENSIBLE)) - || (pfmt->channels > 4) - || (!pfmt->channels) - || (!pfmt->freqHz) - || (pfmt->bitspersample & 7) - || (pfmt->bitspersample < 8) - || (pfmt->bitspersample > 32)) return FALSE; - WAVEDATAHEADER *pdata; - for (;;) - { - pdata = (WAVEDATAHEADER *)(lpStream + dwMemPos); - if (pdata->id_data == IFFID_data) break; - dwMemPos += pdata->length + 8; - if (dwMemPos + 8 >= dwMemLength) return FALSE; - } - m_nType = MOD_TYPE_WAV; - m_nSamples = 0; - m_nInstruments = 0; - m_nChannels = 4; - m_nDefaultSpeed = 8; - m_nDefaultTempo = 125; - m_dwSongFlags |= SONG_LINEARSLIDES; // For no resampling - Order[0] = 0; - Order[1] = 0xFF; - PatternSize[0] = PatternSize[1] = 64; - if ((Patterns[0] = AllocatePattern(64, 4)) == NULL) return TRUE; - if ((Patterns[1] = AllocatePattern(64, 4)) == NULL) return TRUE; - UINT samplesize = (pfmt->channels * pfmt->bitspersample) >> 3; - UINT len = pdata->length, bytelen; - if (dwMemPos + len > dwMemLength - 8) len = dwMemLength - dwMemPos - 8; - len /= samplesize; - bytelen = len; - if (pfmt->bitspersample >= 16) bytelen *= 2; - if (len > MAX_SAMPLE_LENGTH) len = MAX_SAMPLE_LENGTH; - if (!len) return TRUE; - // Setting up module length - DWORD dwTime = ((len * 50) / pfmt->freqHz) + 1; - DWORD framesperrow = (dwTime + 63) / 63; - if (framesperrow < 4) framesperrow = 4; - UINT norders = 1; - while (framesperrow >= 0x20) - { - Order[norders++] = 1; - Order[norders] = 0xFF; - framesperrow = (dwTime + (64 * norders - 1)) / (64 * norders); - if (norders >= MAX_ORDERS-1) break; - } - m_nDefaultSpeed = framesperrow; - for (UINT iChn=0; iChn<4; iChn++) - { - ChnSettings[iChn].nPan = (iChn & 1) ? 256 : 0; - ChnSettings[iChn].nVolume = 64; - ChnSettings[iChn].dwFlags = 0; - } - // Setting up speed command - MODCOMMAND *pcmd = Patterns[0]; - pcmd[0].command = CMD_SPEED; - pcmd[0].param = (BYTE)m_nDefaultSpeed; - pcmd[0].note = 5*12+1; - pcmd[0].instr = 1; - pcmd[1].note = pcmd[0].note; - pcmd[1].instr = pcmd[0].instr; - m_nSamples = pfmt->channels; - // Support for Multichannel Wave - for (UINT nChn=0; nChnnLength = len; - pins->nC4Speed = pfmt->freqHz; - pins->nVolume = 256; - pins->nPan = 128; - pins->nGlobalVol = 64; - pins->uFlags = (WORD)((pfmt->bitspersample >= 16) ? CHN_16BIT : 0); - pins->uFlags |= CHN_PANNING; - if (m_nSamples > 1) - { - switch(nChn) - { - case 0: pins->nPan = 0; break; - case 1: pins->nPan = 256; break; - case 2: pins->nPan = (WORD)((m_nSamples == 3) ? 128 : 64); pcmd[nChn].command = CMD_S3MCMDEX; pcmd[nChn].param = 0x91; break; - case 3: pins->nPan = 192; pcmd[nChn].command = CMD_S3MCMDEX; pcmd[nChn].param = 0x91; break; - default: pins->nPan = 128; break; - } - } - if ((pins->pSample = AllocateSample(bytelen+8)) == NULL) return TRUE; - if (pfmt->bitspersample >= 16) - { - int slsize = pfmt->bitspersample >> 3; - signed short *p = (signed short *)pins->pSample; - signed char *psrc = (signed char *)(lpStream+dwMemPos+8+nChn*slsize+slsize-2); - for (UINT i=0; ipSample; - signed char *psrc = (signed char *)(lpStream+dwMemPos+8+nChn); - for (UINT i=0; i dwBytes)) return FALSE; - nPos = 0; - while ((nPos < nLen) && (dwBytes > 4)) - { - int nIndex; - value = *((short int *)psrc); - nIndex = psrc[2]; - psrc += 4; - dwBytes -= 4; - pdest[nPos++] = (short int)value; - for (UINT i=0; ((i<(pkBlkAlign-4)*2) && (nPos < nLen) && (dwBytes)); i++) - { - BYTE delta; - if (i & 1) - { - delta = (BYTE)(((*(psrc++)) >> 4) & 0x0F); - dwBytes--; - } else - { - delta = (BYTE)((*psrc) & 0x0F); - } - int v = gIMAUnpackTable[nIndex] >> 3; - if (delta & 1) v += gIMAUnpackTable[nIndex] >> 2; - if (delta & 2) v += gIMAUnpackTable[nIndex] >> 1; - if (delta & 4) v += gIMAUnpackTable[nIndex]; - if (delta & 8) value -= v; else value += v; - nIndex += gIMAIndexTab[delta & 7]; - if (nIndex < 0) nIndex = 0; else - if (nIndex > 88) nIndex = 88; - if (value > 32767) value = 32767; else - if (value < -32768) value = -32768; - pdest[nPos++] = (short int)value; - } - } - return TRUE; -} - - - diff --git a/src/modplug/sndfile.cpp b/src/modplug/sndfile.cpp index b3b70899..12ecd559 100644 --- a/src/modplug/sndfile.cpp +++ b/src/modplug/sndfile.cpp @@ -9,16 +9,7 @@ #include "stdafx.h" #include "sndfile.h" -#define MMCMP_SUPPORT - -#ifdef MMCMP_SUPPORT -extern BOOL MMCMP_Unpack(LPCBYTE *ppMemFile, LPDWORD pdwMemLength); -#endif - // External decompressors -extern void AMSUnpack(const char *psrc, UINT inputlen, char *pdest, UINT dmax, char packcharacter); -extern WORD MDLReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, CHAR n); -extern int DMFUnpack(LPBYTE psample, LPBYTE ibuf, LPBYTE ibufmax, UINT maxlen); extern DWORD ITReadBits(DWORD &bitbuf, UINT &bitnum, LPBYTE &ibuf, CHAR n); extern void ITUnpack8Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dwMemLength, BOOL b215); extern void ITUnpack16Bit(signed char *pSample, DWORD dwLen, LPBYTE lpMemFile, DWORD dwMemLength, BOOL b215); @@ -133,44 +124,12 @@ BOOL CSoundFile::Create(LPCBYTE lpStream, DWORD dwMemLength) } if (lpStream) { -#ifdef MMCMP_SUPPORT - BOOL bMMCmp = MMCMP_Unpack(&lpStream, &dwMemLength); -#endif if ((!ReadXM(lpStream, dwMemLength)) && (!ReadS3M(lpStream, dwMemLength)) && (!ReadIT(lpStream, dwMemLength)) - && (!ReadWav(lpStream, dwMemLength)) -#ifndef MODPLUG_BASIC_SUPPORT -/* Sequencer File Format Support */ - //&& (!ReadABC(lpStream, dwMemLength)) // these 3 don't compile on Windows - //&& (!ReadMID(lpStream, dwMemLength)) - //&& (!ReadPAT(lpStream, dwMemLength)) - && (!ReadSTM(lpStream, dwMemLength)) - && (!ReadMed(lpStream, dwMemLength)) - && (!ReadMTM(lpStream, dwMemLength)) - && (!ReadMDL(lpStream, dwMemLength)) - && (!ReadDBM(lpStream, dwMemLength)) - && (!Read669(lpStream, dwMemLength)) - && (!ReadFAR(lpStream, dwMemLength)) - && (!ReadAMS(lpStream, dwMemLength)) - && (!ReadOKT(lpStream, dwMemLength)) - && (!ReadPTM(lpStream, dwMemLength)) - && (!ReadUlt(lpStream, dwMemLength)) - && (!ReadDMF(lpStream, dwMemLength)) - && (!ReadDSM(lpStream, dwMemLength)) && (!ReadUMX(lpStream, dwMemLength)) - && (!ReadAMF(lpStream, dwMemLength)) - && (!ReadPSM(lpStream, dwMemLength)) - && (!ReadMT2(lpStream, dwMemLength)) -#endif // MODPLUG_BASIC_SUPPORT + && (!ReadSTM(lpStream, dwMemLength)) && (!ReadMod(lpStream, dwMemLength))) m_nType = MOD_TYPE_NONE; -#ifdef MMCMP_SUPPORT - if (bMMCmp) - { - GlobalFreePtr(lpStream); - lpStream = NULL; - } -#endif } // Adjust song names for (i=0; ipSample, pIns->nLength, (LPBYTE)lpMemFile, dwMemLength, (nFlags == RS_IT21516)); break; -#ifndef MODPLUG_BASIC_SUPPORT -#ifndef MODPLUG_FASTSOUNDLIB - // 8-bit interleaved stereo samples - case RS_STIPCM8S: - case RS_STIPCM8U: - { - int iadd = 0; - if (nFlags == RS_STIPCM8U) { iadd = -0x80; } - len = pIns->nLength; - if (len*2 > dwMemLength) len = dwMemLength >> 1; - LPBYTE psrc = (LPBYTE)lpMemFile; - LPBYTE pSample = (LPBYTE)pIns->pSample; - for (UINT j=0; jnLength; - if (len*4 > dwMemLength) len = dwMemLength >> 2; - short int *psrc = (short int *)lpMemFile; - short int *pSample = (short int *)pIns->pSample; - for (UINT j=0; j 9) - { - const char *psrc = lpMemFile; - char packcharacter = lpMemFile[8], *pdest = (char *)pIns->pSample; - len += bswapLE32(*((LPDWORD)(lpMemFile+4))); - if (len > dwMemLength) len = dwMemLength; - UINT dmax = pIns->nLength; - if (pIns->uFlags & CHN_16BIT) dmax <<= 1; - AMSUnpack(psrc+9, len-9, pdest, dmax, packcharacter); - } - break; - - // PTM 8bit delta to 16-bit sample - case RS_PTM8DTO16: - { - len = pIns->nLength * 2; - if (len > dwMemLength) break; - signed char *pSample = (signed char *)pIns->pSample; - signed char delta8 = 0; - for (UINT j=0; jpSample; - for (UINT j=0; j= 4) - { - LPBYTE pSample = (LPBYTE)pIns->pSample; - LPBYTE ibuf = (LPBYTE)lpMemFile; - DWORD bitbuf = bswapLE32(*((DWORD *)ibuf)); - UINT bitnum = 32; - BYTE dlt = 0, lowbyte = 0; - ibuf += 4; - for (UINT j=0; jnLength; j++) - { - BYTE hibyte; - BYTE sign; - if (nFlags == RS_MDL16) lowbyte = (BYTE)MDLReadBits(bitbuf, bitnum, ibuf, 8); - sign = (BYTE)MDLReadBits(bitbuf, bitnum, ibuf, 1); - if (MDLReadBits(bitbuf, bitnum, ibuf, 1)) - { - hibyte = (BYTE)MDLReadBits(bitbuf, bitnum, ibuf, 3); - } else - { - hibyte = 8; - while (!MDLReadBits(bitbuf, bitnum, ibuf, 1)) hibyte += 0x10; - hibyte += MDLReadBits(bitbuf, bitnum, ibuf, 4); - } - if (sign) hibyte = ~hibyte; - dlt += hibyte; - if (nFlags != RS_MDL16) - pSample[j] = dlt; - else - { - pSample[j<<1] = lowbyte; - pSample[(j<<1)+1] = dlt; - } - } - } - break; - - case RS_DMF8: - case RS_DMF16: - len = dwMemLength; - if (len >= 4) - { - UINT maxlen = pIns->nLength; - if (pIns->uFlags & CHN_16BIT) maxlen <<= 1; - LPBYTE ibuf = (LPBYTE)lpMemFile, ibufmax = (LPBYTE)(lpMemFile+dwMemLength); - len = DMFUnpack((LPBYTE)pIns->pSample, ibuf, ibufmax, maxlen); - } - break; - -#ifdef MODPLUG_TRACKER - // PCM 24-bit signed -> load sample, and normalize it to 16-bit - case RS_PCM24S: - case RS_PCM32S: - len = pIns->nLength * 3; - if (nFlags == RS_PCM32S) len += pIns->nLength; - if (len > dwMemLength) break; - if (len > 4*8) - { - UINT slsize = (nFlags == RS_PCM32S) ? 4 : 3; - LPBYTE pSrc = (LPBYTE)lpMemFile; - LONG max = 255; - if (nFlags == RS_PCM32S) pSrc++; - for (UINT j=0; j max) max = l; - if (-l > max) max = -l; - } - max = (max / 128) + 1; - signed short *pDest = (signed short *)pIns->pSample; - for (UINT k=0; k load sample, and normalize it to 16-bit - case RS_STIPCM24S: - case RS_STIPCM32S: - len = pIns->nLength * 6; - if (nFlags == RS_STIPCM32S) len += pIns->nLength * 2; - if (len > dwMemLength) break; - if (len > 8*8) - { - UINT slsize = (nFlags == RS_STIPCM32S) ? 4 : 3; - LPBYTE pSrc = (LPBYTE)lpMemFile; - LONG max = 255; - if (nFlags == RS_STIPCM32S) pSrc++; - for (UINT j=0; j max) max = l; - if (-l > max) max = -l; - } - max = (max / 128) + 1; - signed short *pDest = (signed short *)pIns->pSample; - for (UINT k=0; knLength; - if (len*4 > dwMemLength) len = dwMemLength >> 2; - LPCBYTE psrc = (LPCBYTE)lpMemFile; - short int *pSample = (short int *)pIns->pSample; - for (UINT j=0; j Activator; line_t *Line; bool LineSide; bool bCeiling; @@ -1530,7 +1530,7 @@ void FBehavior::StartTypedScripts (WORD type, AActor *activator, bool always, in if (ptr->Type == type) { DLevelScript *runningScript = P_GetScriptGoing (activator, NULL, ptr->Number, - ptr, this, 0, arg1, 0, 0, always, true); + ptr, this, 0, arg1, 0, 0, always); if (runNow) { runningScript->RunScript (); @@ -1557,7 +1557,10 @@ void FBehavior::StaticStopMyScripts (AActor *actor) //---- The ACS Interpreter ----// -IMPLEMENT_CLASS (DACSThinker) +IMPLEMENT_POINTY_CLASS (DACSThinker) + DECLARE_POINTER(LastScript) + DECLARE_POINTER(Scripts) +END_POINTERS DACSThinker *DACSThinker::ActiveThinker = NULL; @@ -1579,13 +1582,6 @@ DACSThinker::DACSThinker () DACSThinker::~DACSThinker () { - DLevelScript *script = Scripts; - while (script) - { - DLevelScript *next = script->next; - script->Destroy (); - script = next; - } Scripts = NULL; ActiveThinker = NULL; } @@ -1647,7 +1643,9 @@ void DACSThinker::StopScriptsFor (AActor *actor) } IMPLEMENT_POINTY_CLASS (DLevelScript) - DECLARE_POINTER (activator) + DECLARE_POINTER(next) + DECLARE_POINTER(prev) + DECLARE_POINTER(activator) END_POINTERS void DLevelScript::Serialize (FArchive &arc) @@ -1713,13 +1711,25 @@ void DLevelScript::Unlink () DACSThinker *controller = DACSThinker::ActiveThinker; if (controller->LastScript == this) + { controller->LastScript = prev; + GC::WriteBarrier(controller, prev); + } if (controller->Scripts == this) + { controller->Scripts = next; + GC::WriteBarrier(controller, next); + } if (prev) + { prev->next = next; + GC::WriteBarrier(prev, next); + } if (next) + { next->prev = prev; + GC::WriteBarrier(next, prev); + } } void DLevelScript::Link () @@ -1727,12 +1737,19 @@ void DLevelScript::Link () DACSThinker *controller = DACSThinker::ActiveThinker; next = controller->Scripts; + GC::WriteBarrier(this, next); if (controller->Scripts) + { controller->Scripts->prev = this; + GC::WriteBarrier(controller->Scripts, this); + } prev = NULL; controller->Scripts = this; + GC::WriteBarrier(controller, this); if (controller->LastScript == NULL) + { controller->LastScript = this; + } } void DLevelScript::PutLast () @@ -4852,9 +4869,9 @@ int DLevelScript::RunScript () } else { - if (activator->IsKindOf (RUNTIME_CLASS(AScriptedMarine))) + if (activator != NULL && activator->IsKindOf (RUNTIME_CLASS(AScriptedMarine))) { - static_cast(activator)->SetWeapon ( + barrier_cast(activator)->SetWeapon ( (AScriptedMarine::EMarineWeapon)STACK(1)); } } @@ -4879,9 +4896,9 @@ int DLevelScript::RunScript () } else { - if (activator->IsKindOf (RUNTIME_CLASS(AScriptedMarine))) + if (activator != NULL && activator->IsKindOf (RUNTIME_CLASS(AScriptedMarine))) { - static_cast(activator)->SetSprite (type); + barrier_cast(activator)->SetSprite (type); } } } @@ -5114,7 +5131,7 @@ int DLevelScript::RunScript () if (STACK(3) == 0) { - state = RUNTIME_TYPE(activator)->ActorInfo->FindState (statelist.Size(), &statelist[0], !!STACK(1)); + state = activator->GetClass()->ActorInfo->FindState (statelist.Size(), &statelist[0], !!STACK(1)); if (state != NULL) { activator->SetState (state); @@ -5133,7 +5150,7 @@ int DLevelScript::RunScript () while ( (actor = iterator.Next ()) ) { - state = RUNTIME_TYPE(actor)->ActorInfo->FindState (statelist.Size(), &statelist[0], !!STACK(1)); + state = actor->GetClass()->ActorInfo->FindState (statelist.Size(), &statelist[0], !!STACK(1)); if (state != NULL) { actor->SetState (state); @@ -5309,7 +5326,6 @@ int DLevelScript::RunScript () Unlink (); if (controller->RunningScripts[script] == this) controller->RunningScripts[script] = NULL; - this->Destroy (); } else { @@ -5326,7 +5342,7 @@ int DLevelScript::RunScript () #undef PushtoStack static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module, - bool backSide, int arg0, int arg1, int arg2, int always, bool delay) + bool backSide, int arg0, int arg1, int arg2, int always) { DACSThinker *controller = DACSThinker::ActiveThinker; @@ -5340,11 +5356,11 @@ static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, cons return NULL; } - return new DLevelScript (who, where, num, code, module, backSide, arg0, arg1, arg2, always, delay); + return new DLevelScript (who, where, num, code, module, backSide, arg0, arg1, arg2, always); } DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module, - bool backside, int arg0, int arg1, int arg2, int always, bool delay) + bool backside, int arg0, int arg1, int arg2, int always) : activeBehavior (module) { if (DACSThinker::ActiveThinker == NULL) @@ -5363,17 +5379,12 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr backSide = backside; activefont = SmallFont; hudwidth = hudheight = 0; - if (delay) - { - // From Hexen: Give the world some time to set itself up before running open scripts. - //script->state = SCRIPT_Delayed; - //script->statedata = TICRATE; - state = SCRIPT_Running; - } - else - { - state = SCRIPT_Running; - } + state = SCRIPT_Running; + // Hexen waited one second before executing any open scripts. I didn't realize + // this when I wrote my ACS implementation. Now that I know, it's still best to + // run them right away because there are several map properties that can't be + // set in an editor. If an open script sets them, it looks dumb if a second + // goes by while they're in their default state. if (!always) DACSThinker::ActiveThinker->RunningScripts[num] = this; @@ -5414,7 +5425,7 @@ void P_DoDeferedScripts () NULL, def->script, scriptdata, module, 0, def->arg0, def->arg1, def->arg2, - def->type == acsdefered_t::defexealways, true); + def->type == acsdefered_t::defexealways); } else { @@ -5487,7 +5498,7 @@ int P_StartScript (AActor *who, line_t *where, int script, char *map, bool backS } } DLevelScript *runningScript = P_GetScriptGoing (who, where, script, - scriptdata, module, backSide, arg0, arg1, arg2, always, false); + scriptdata, module, backSide, arg0, arg1, arg2, always); if (runningScript != NULL) { if (wantResultCode) diff --git a/src/p_acs.h b/src/p_acs.h index de0b0ea9..9da00145 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -638,7 +638,7 @@ public: DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module, - bool backSide, int arg0, int arg1, int arg2, int always, bool delay); + bool backSide, int arg0, int arg1, int arg2, int always); ~DLevelScript (); void Serialize (FArchive &arc); @@ -655,7 +655,7 @@ protected: int *pc; EScriptState state; int statedata; - AActor *activator; + TObjPtr activator; line_t *activationline; bool backSide; FFont *activefont; @@ -701,6 +701,7 @@ inline FArchive &operator<< (FArchive &arc, DLevelScript::EScriptState &state) class DACSThinker : public DThinker { DECLARE_CLASS (DACSThinker, DThinker) + HAS_OBJECT_POINTERS public: DACSThinker (); ~DACSThinker (); diff --git a/src/p_doors.cpp b/src/p_doors.cpp index 2fce22ba..c4b7d486 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -69,7 +69,7 @@ void DDoor::Tick () if (m_Sector->floorplane.d != m_OldFloorDist) { if (!m_Sector->floordata || !m_Sector->floordata->IsKindOf(RUNTIME_CLASS(DPlat)) || - !((DPlat*)m_Sector->floordata)->IsLift()) + !(barrier_cast(m_Sector->floordata))->IsLift()) { m_OldFloorDist = m_Sector->floorplane.d; m_BotDist = m_Sector->ceilingplane.PointToDist (m_BotSpot, @@ -340,7 +340,7 @@ DDoor::DDoor (sector_t *sec, EVlDoor type, fixed_t speed, int delay, int lightTa } if (!m_Sector->floordata || !m_Sector->floordata->IsKindOf(RUNTIME_CLASS(DPlat)) || - !((DPlat*)m_Sector->floordata)->IsLift()) + !(barrier_cast(m_Sector->floordata))->IsLift()) { height = sec->FindHighestFloorPoint (&m_BotSpot); m_BotDist = sec->ceilingplane.PointToDist (m_BotSpot, height); @@ -384,7 +384,7 @@ bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing, { if (sec->ceilingdata->IsKindOf (RUNTIME_CLASS(DDoor))) { - DDoor *door = static_cast(sec->ceilingdata); + DDoor *door = barrier_cast(sec->ceilingdata); // ONLY FOR "RAISE" DOORS, NOT "OPEN"s if (door->m_Type == DDoor::doorRaise && type == DDoor::doorRaise) @@ -761,7 +761,7 @@ bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay) if (sec->ceilingdata->IsA (RUNTIME_CLASS(DAnimatedDoor))) { - DAnimatedDoor *door = static_cast (sec->ceilingdata); + DAnimatedDoor *door = barrier_cast(sec->ceilingdata); if (door->m_Status == DAnimatedDoor::Waiting) { return door->StartClosing(); diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 214c4463..ce509b4a 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -1133,22 +1133,22 @@ bool P_LookForTID (AActor *actor, INTBOOL allaround) actor->reactiontime = 0; actor->target = other; - actor->LastLook.Actor = other; + actor->LastLookActor = other; return true; } // The actor's TID could change because of death or because of // Thing_ChangeTID. If it's not what we expect, then don't use // it as a base for the iterator. - if (actor->LastLook.Actor != NULL && - actor->LastLook.Actor->tid != actor->TIDtoHate) + if (actor->LastLookActor != NULL && + actor->LastLookActor->tid != actor->TIDtoHate) { - actor->LastLook.Actor = NULL; + actor->LastLookActor = NULL; } - FActorIterator iterator (actor->TIDtoHate, actor->LastLook.Actor); + FActorIterator iterator (actor->TIDtoHate, actor->LastLookActor); int c = (pr_look3() & 31) + 7; // Look for between 7 and 38 hatees at a time - while ((other = iterator.Next()) != actor->LastLook.Actor) + while ((other = iterator.Next()) != actor->LastLookActor) { if (other == NULL) { @@ -1206,10 +1206,10 @@ bool P_LookForTID (AActor *actor, INTBOOL allaround) actor->reactiontime = 0; actor->target = other; - actor->LastLook.Actor = other; + actor->LastLookActor = other; return true; } - actor->LastLook.Actor = other; + actor->LastLookActor = other; if (actor->target == NULL) { // [RH] use goal as target @@ -1436,7 +1436,7 @@ bool P_LookForPlayers (AActor *actor, INTBOOL allaround) } else { - pnum = actor->LastLook.PlayerNumber; + pnum = actor->LastLookPlayerNumber; } stop = (pnum - 1) & (MAXPLAYERS-1); @@ -1448,7 +1448,7 @@ bool P_LookForPlayers (AActor *actor, INTBOOL allaround) if (actor->TIDtoHate == 0) { - actor->LastLook.PlayerNumber = pnum; + actor->LastLookPlayerNumber = pnum; } if (++c == MAXPLAYERS-1 || pnum == stop) diff --git a/src/p_enemy_a_lookex.cpp b/src/p_enemy_a_lookex.cpp index ff679c77..3a5e4b6a 100644 --- a/src/p_enemy_a_lookex.cpp +++ b/src/p_enemy_a_lookex.cpp @@ -346,22 +346,22 @@ bool P_NewLookTID (AActor *actor, angle_t fov, fixed_t mindist, fixed_t maxdist, actor->reactiontime = 0; actor->target = other; - actor->LastLook.Actor = other; + actor->LastLookActor = other; return true; } // The actor's TID could change because of death or because of // Thing_ChangeTID. If it's not what we expect, then don't use // it as a base for the iterator. - if (actor->LastLook.Actor != NULL && - actor->LastLook.Actor->tid != actor->TIDtoHate) + if (actor->LastLookActor != NULL && + actor->LastLookActor->tid != actor->TIDtoHate) { - actor->LastLook.Actor = NULL; + actor->LastLookActor = NULL; } - FActorIterator iterator (actor->TIDtoHate, actor->LastLook.Actor); + FActorIterator iterator (actor->TIDtoHate, actor->LastLookActor); int c = (pr_look3() & 31) + 7; // Look for between 7 and 38 hatees at a time - while ((other = iterator.Next()) != actor->LastLook.Actor) + while ((other = iterator.Next()) != actor->LastLookActor) { if (other == NULL) { @@ -448,10 +448,10 @@ bool P_NewLookTID (AActor *actor, angle_t fov, fixed_t mindist, fixed_t maxdist, actor->reactiontime = 0; actor->target = other; - actor->LastLook.Actor = other; + actor->LastLookActor = other; return true; } - actor->LastLook.Actor = other; + actor->LastLookActor = other; if (actor->target == NULL) { // [RH] use goal as target @@ -562,7 +562,7 @@ bool P_NewLookPlayers (AActor *actor, angle_t fov, fixed_t mindist, fixed_t maxd } else { - pnum = actor->LastLook.PlayerNumber; + pnum = actor->LastLookPlayerNumber; } stop = (pnum - 1) & (MAXPLAYERS-1); @@ -574,7 +574,7 @@ bool P_NewLookPlayers (AActor *actor, angle_t fov, fixed_t mindist, fixed_t maxd if (actor->TIDtoHate == 0) { - actor->LastLook.PlayerNumber = pnum; + actor->LastLookPlayerNumber = pnum; } if (++c == MAXPLAYERS-1 || pnum == stop) diff --git a/src/p_floor.cpp b/src/p_floor.cpp index 33d1d221..469cfab6 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -600,7 +600,7 @@ bool EV_FloorCrushStop (int tag) sector_t *sec = sectors + secnum; if (sec->floordata && sec->floordata->IsKindOf (RUNTIME_CLASS(DFloor)) && - static_cast(sec->floordata)->m_Type == DFloor::floorRaiseAndCrush) + barrier_cast(sec->floordata)->m_Type == DFloor::floorRaiseAndCrush) { SN_StopSequence (sec); sec->floordata->Destroy (); diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 36b4e20c..555b542a 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -932,7 +932,7 @@ FUNC(LS_Thing_ChangeTID) { if (arg0 == 0) { - if (it != NULL && !(it->ObjectFlags & OF_MassDestruction)) + if (it != NULL && !(it->ObjectFlags & OF_EuthanizeMe)) { it->RemoveFromHash (); it->tid = arg1; @@ -950,7 +950,7 @@ FUNC(LS_Thing_ChangeTID) actor = next; next = iterator.Next (); - if (!(actor->ObjectFlags & OF_MassDestruction)) + if (!(actor->ObjectFlags & OF_EuthanizeMe)) { actor->RemoveFromHash (); actor->tid = arg1; @@ -1227,7 +1227,7 @@ FUNC(LS_Thing_Hate) if (arg2 != 0) { hater->TIDtoHate = arg1; - hater->LastLook.Actor = NULL; + hater->LastLookActor = NULL; // If the TID to hate is 0, then don't forget the target and // lastenemy fields. diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 75d28360..6502fbe9 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -173,7 +173,7 @@ IMPLEMENT_POINTY_CLASS (AActor) DECLARE_POINTER (lastenemy) DECLARE_POINTER (tracer) DECLARE_POINTER (goal) - DECLARE_POINTER (LastLook) // This is actually a union + DECLARE_POINTER (LastLookActor) DECLARE_POINTER (Inventory) DECLARE_POINTER (LastHeard) DECLARE_POINTER (master) @@ -242,11 +242,13 @@ void AActor::Serialize (FArchive &arc) arc << TIDtoHate; if (TIDtoHate == 0) { - arc << LastLook.PlayerNumber; + arc << LastLookPlayerNumber; + LastLookActor = NULL; } else { - arc << LastLook.Actor; + arc << LastLookActor; + LastLookPlayerNumber = -1; } arc << effects << alpha @@ -324,10 +326,8 @@ void AActor::Serialize (FArchive &arc) << meleerange << DamageType << gravity - << FastChaseStrafeCount; - - if (SaveVersion >=778) - arc << master; + << FastChaseStrafeCount + << master; if (arc.IsStoring ()) { @@ -569,7 +569,8 @@ bool AActor::SetState (FState *newstate) newstate->GetAction() (this); // Check whether the called action function resulted in destroying the actor - if (ObjectFlags & OF_MassDestruction) return false; + if (ObjectFlags & OF_EuthanizeMe) + return false; } newstate = newstate->GetNextState(); } while (tics == 0); @@ -716,7 +717,7 @@ void AActor::DestroyAllInventory () // //============================================================================ -AInventory *AActor::FirstInv () const +AInventory *AActor::FirstInv () { if (Inventory == NULL) { @@ -805,7 +806,7 @@ AInventory *AActor::DropInventory (AInventory *item) // //============================================================================ -AInventory *AActor::FindInventory (const PClass *type) const +AInventory *AActor::FindInventory (const PClass *type) { AInventory *item; @@ -822,7 +823,7 @@ AInventory *AActor::FindInventory (const PClass *type) const return item; } -AInventory *AActor::FindInventory (FName type) const +AInventory *AActor::FindInventory (FName type) { return FindInventory(PClass::FindClass(type)); } @@ -882,11 +883,12 @@ bool AActor::GiveAmmo (const PClass *type, int amount) // //============================================================================ -void AActor::CopyFriendliness (const AActor *other, bool changeTarget) +void AActor::CopyFriendliness (AActor *other, bool changeTarget) { level.total_monsters -= CountsAsKill(); TIDtoHate = other->TIDtoHate; - LastLook = other->LastLook; + LastLookActor = other->LastLookActor; + LastLookPlayerNumber = other->LastLookPlayerNumber; flags = (flags & ~MF_FRIENDLY) | (other->flags & MF_FRIENDLY); flags3 = (flags3 & ~(MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)) | (other->flags3 & (MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS)); flags4 = (flags4 & ~MF4_NOHATEPLAYERS) | (other->flags4 & MF4_NOHATEPLAYERS); @@ -1060,7 +1062,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) if (nextstate == NULL) nextstate = mo->FindState(NAME_Death); mo->SetState (nextstate); - if (mo->ObjectFlags & OF_MassDestruction) + if (mo->ObjectFlags & OF_EuthanizeMe) { return; } @@ -2963,7 +2965,7 @@ void AActor::Tick () // Handle X and Y momemtums BlockingMobj = NULL; P_XYMovement (this, cummx, cummy); - if (ObjectFlags & OF_MassDestruction) + if (ObjectFlags & OF_EuthanizeMe) { // actor was destroyed return; } @@ -3021,7 +3023,7 @@ void AActor::Tick () P_ZMovement (this); } - if (ObjectFlags & OF_MassDestruction) + if (ObjectFlags & OF_EuthanizeMe) return; // actor was destroyed } else if (z <= floorz) @@ -3293,7 +3295,7 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t if (actor->flags3 & MF3_ISMONSTER) { - actor->LastLook.PlayerNumber = rng() % MAXPLAYERS; + actor->LastLookPlayerNumber = rng() % MAXPLAYERS; actor->TIDtoHate = 0; } @@ -3381,7 +3383,7 @@ AActor *AActor::StaticSpawn (const PClass *type, fixed_t ix, fixed_t iy, fixed_t if (!SpawningMapThing) { actor->BeginPlay (); - if (actor->ObjectFlags & OF_MassDestruction) + if (actor->ObjectFlags & OF_EuthanizeMe) { return NULL; } @@ -3502,6 +3504,15 @@ void AActor::Deactivate (AActor *activator) } } +size_t AActor::PropagateMark() +{ + for (unsigned i=0; iangle = (DWORD)((mthing->angle * UCONST64(0x100000000)) / 360); mobj->BeginPlay (); - if (mobj->ObjectFlags & OF_MassDestruction) + if (mobj->ObjectFlags & OF_EuthanizeMe) { return; } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 6c5af2b3..f9fe0317 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1525,8 +1525,122 @@ enum THING_CopyCeilingPlane = 9511, THING_VavoomFloor=1500, THING_VavoomCeiling=1501, + THING_VertexFloorZ=1505, + THING_VertexCeilingZ=1505, }; +//========================================================================== +// +// P_SetSlopesFromVertexHeights +// +//========================================================================== + +static void P_SetSlopesFromVertexHeights(mapthing2_t *firstmt, mapthing2_t *lastmt) +{ + TMap vt_heights[2]; + mapthing2_t *mt; + bool vt_found = false; + + for (mt = firstmt; mt < lastmt; ++mt) + { + if (mt->type == THING_VertexFloorZ || mt->type == THING_VertexCeilingZ) + { + for(int i=0; ix << FRACBITS && vertexes[i].y == mt->y << FRACBITS) + { + if (mt->type == THING_VertexFloorZ) + { + vt_heights[0][i] = mt->z << FRACBITS; + } + else + { + vt_heights[1][i] = mt->z << FRACBITS; + } + vt_found = true; + } + } + mt->type = 0; + } + } + if (vt_found) + { + for (int i = 0; i < numsectors; i++) + { + sector_t *sec = §ors[i]; + if (sec->linecount != 3) continue; // only works with triangular sectors + + FVector3 vt1, vt2, vt3, cross; + FVector3 vec1, vec2; + int vi1, vi2, vi3; + + vi1 = sec->lines[0]->v1 - vertexes; + vi2 = sec->lines[0]->v2 - vertexes; + vi3 = (sec->lines[1]->v1 == sec->lines[0]->v1 || sec->lines[1]->v1 == sec->lines[0]->v2)? + sec->lines[1]->v2 - vertexes : sec->lines[1]->v1 - vertexes; + + vt1.X = FIXED2FLOAT(vertexes[vi1].x); + vt1.Y = FIXED2FLOAT(vertexes[vi1].y); + vt2.X = FIXED2FLOAT(vertexes[vi2].x); + vt2.Y = FIXED2FLOAT(vertexes[vi2].y); + vt3.X = FIXED2FLOAT(vertexes[vi3].x); + vt3.Y = FIXED2FLOAT(vertexes[vi3].y); + + for(int j=0; j<2; j++) + { + fixed_t *h1 = vt_heights[j].CheckKey(vi1); + fixed_t *h2 = vt_heights[j].CheckKey(vi2); + fixed_t *h3 = vt_heights[j].CheckKey(vi3); + fixed_t z3; + if (h1==NULL && h2==NULL && h3==NULL) continue; + + vt1.Z = FIXED2FLOAT(h1? *h1 : j==0? sec->floortexz : sec->ceilingtexz); + vt2.Z = FIXED2FLOAT(h2? *h2 : j==0? sec->floortexz : sec->ceilingtexz); + z3 = h3? *h3 : j==0? sec->floortexz : sec->ceilingtexz; + vt3.Z = FIXED2FLOAT(z3); + + if (P_PointOnLineSide(vertexes[vi3].x, vertexes[vi3].y, sec->lines[0]) == 0) + { + vec1 = vt2 - vt3; + vec2 = vt1 - vt3; + } + else + { + vec1 = vt1 - vt3; + vec2 = vt2 - vt3; + } + + FVector3 cross = vec1 ^ vec2; + + double len = cross.Length(); + if (len == 0) + { + // Only happens when all vertices in this sector are on the same line. + // Let's just ignore this case. + continue; + } + cross /= len; + + // Fix backward normals + if ((cross.Z < 0 && j == 0) || (cross.Z > 0 && j == 1)) + { + cross = -cross; + } + + secplane_t *srcplane = j==0? &sec->floorplane : &sec->ceilingplane; + + srcplane->a = FLOAT2FIXED (cross[0]); + srcplane->b = FLOAT2FIXED (cross[1]); + srcplane->c = FLOAT2FIXED (cross[2]); + srcplane->ic = DivScale32 (1, srcplane->c); + srcplane->d = -TMulScale16 (srcplane->a, vertexes[vi3].x, + srcplane->b, vertexes[vi3].y, + srcplane->c, z3); + } + } + } +} + //=========================================================================== // // P_SpawnSlopeMakers @@ -1584,6 +1698,8 @@ static void P_SpawnSlopeMakers (mapthing2_t *firstmt, mapthing2_t *lastmt) mt->type = 0; } } + + P_SetSlopesFromVertexHeights(firstmt, lastmt); } //=========================================================================== @@ -2751,7 +2867,7 @@ void P_LoadBlockMap (MapData * map) if (ForceNodeBuild || genblockmap || count/2 >= 0x10000 || count == 0 || - Args.CheckParm("-blockmap") + Args->CheckParm("-blockmap") ) { DPrintf ("Generating BLOCKMAP\n"); @@ -2819,7 +2935,7 @@ static void P_GroupLines (bool buildmap) int totallights; line_t* li; sector_t* sector; - DBoundingBox bbox; + FBoundingBox bbox; bool flaggedNoFronts = false; unsigned int ii, jj; @@ -3853,7 +3969,7 @@ static void P_Shutdown () P_FreeExtraLevelData (); if (StatusBar != NULL) { - delete StatusBar; + StatusBar->Destroy(); StatusBar = NULL; } } diff --git a/src/p_spec.h b/src/p_spec.h index ec9004a3..739f1a35 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -128,7 +128,7 @@ public: protected: EPusher m_Type; - AActor *m_Source; // Point source if point pusher + TObjPtr m_Source;// Point source if point pusher int m_Xmag; // X Strength int m_Ymag; // Y Strength int m_Magnitude; // Vector strength for point pusher diff --git a/src/p_user.cpp b/src/p_user.cpp index d5263624..7f713434 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -322,6 +322,26 @@ void player_s::FixPointers (const DObject *old, DObject *rep) if (PendingWeapon == old) PendingWeapon = static_cast(rep); } +size_t player_s::PropagateMark() +{ + GC::Mark(mo); + GC::Mark(poisoner); + GC::Mark(attacker); + GC::Mark(camera); + GC::Mark(dest); + GC::Mark(prev); + GC::Mark(enemy); + GC::Mark(missile); + GC::Mark(mate); + GC::Mark(last_mate); + GC::Mark(ReadyWeapon); + if (PendingWeapon != WP_NOCHANGE) + { + GC::Mark(PendingWeapon); + } + return sizeof(*this); +} + void player_s::SetLogNumber (int num) { char lumpname[16]; diff --git a/src/r_defs.h b/src/r_defs.h index f0b01848..69fec815 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -310,7 +310,7 @@ struct sector_t int floorpic, ceilingpic; BYTE lightlevel; - AActor * SoundTarget; + TObjPtr SoundTarget; BYTE soundtraversed; // 0 = untraversed, 1,2 = sndlines -1 short special; @@ -330,9 +330,9 @@ struct sector_t fixed_t friction, movefactor; // thinker_t for reversable actions - DSectorEffect *floordata; // jff 2/22/98 make thinkers on - DSectorEffect *ceilingdata; // floors, ceilings, lighting, - DSectorEffect *lightingdata; // independent of one another + TObjPtr floordata; // jff 2/22/98 make thinkers on + TObjPtr ceilingdata; // floors, ceilings, lighting, + TObjPtr lightingdata; // independent of one another // jff 2/26/98 lockout machinery for stairbuilding SBYTE stairlock; // -2 on first locked -1 after thinker done 0 normally @@ -364,11 +364,11 @@ struct sector_t // flexible in a Bloody way. SecActTarget forms a list of actors // joined by their tracer fields. When a potential sector action // occurs, SecActTarget's TriggerAction method is called. - ASectorAction *SecActTarget; + TObjPtr SecActTarget; // [RH] The sky box to render for this sector. NULL means use a // regular sky. - ASkyViewpoint *FloorSkyBox, *CeilingSkyBox; + TObjPtr FloorSkyBox, CeilingSkyBox; // Planes that partition this sector into different light zones. FExtraLight *ExtraLights; diff --git a/src/r_things.cpp b/src/r_things.cpp index b39ecde2..b3a85b08 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -2226,7 +2226,7 @@ void R_InitParticles () { char *i; - if ((i = Args.CheckValue ("-numparticles"))) + if ((i = Args->CheckValue ("-numparticles"))) NumParticles = atoi (i); // [BC] Use r_maxparticles now. else diff --git a/src/s_sndseq.cpp b/src/s_sndseq.cpp index 8a1c8e60..412abba6 100644 --- a/src/s_sndseq.cpp +++ b/src/s_sndseq.cpp @@ -100,7 +100,7 @@ class DSeqActorNode : public DSeqNode HAS_OBJECT_POINTERS public: DSeqActorNode (AActor *actor, int sequence, int modenum); - ~DSeqActorNode (); + void Destroy (); void Serialize (FArchive &arc); void MakeSound () { S_SoundID (m_Actor, CHAN_BODY, m_CurrentSoundID, clamp(m_Volume, 0.f, 1.f), m_Atten); } void MakeLoopedSound () { S_LoopedSoundID (m_Actor, CHAN_BODY, m_CurrentSoundID, clamp(m_Volume, 0.f, 1.f), m_Atten); } @@ -109,7 +109,7 @@ public: DSeqNode *SpawnChild (int seqnum) { return SN_StartSequence (m_Actor, seqnum, SEQ_NOTRANS, m_ModeNum, true); } private: DSeqActorNode () {} - AActor *m_Actor; + TObjPtr m_Actor; }; class DSeqPolyNode : public DSeqNode @@ -117,7 +117,7 @@ class DSeqPolyNode : public DSeqNode DECLARE_CLASS (DSeqPolyNode, DSeqNode) public: DSeqPolyNode (polyobj_t *poly, int sequence, int modenum); - ~DSeqPolyNode (); + void Destroy (); void Serialize (FArchive &arc); void MakeSound () { S_SoundID (&m_Poly->startSpot[0], CHAN_BODY, m_CurrentSoundID, clamp(m_Volume, 0.f, 1.f), m_Atten); } void MakeLoopedSound () { S_LoopedSoundID (&m_Poly->startSpot[0], CHAN_BODY, m_CurrentSoundID, clamp(m_Volume, 0.f, 1.f), m_Atten); } @@ -134,7 +134,7 @@ class DSeqSectorNode : public DSeqNode DECLARE_CLASS (DSeqSectorNode, DSeqNode) public: DSeqSectorNode (sector_t *sec, int sequence, int modenum); - ~DSeqSectorNode (); + void Destroy (); void Serialize (FArchive &arc); void MakeSound () { S_SoundID (&m_Sector->soundorg[0], CHAN_BODY, m_CurrentSoundID, clamp(m_Volume, 0.f, 1.f), m_Atten); Looping = false; } void MakeLoopedSound () { S_LoopedSoundID (&m_Sector->soundorg[0], CHAN_BODY, m_CurrentSoundID, clamp(m_Volume, 0.f, 1.f), m_Atten); Looping = true; } @@ -242,7 +242,12 @@ void DSeqNode::SerializeSequences (FArchive &arc) arc << SequenceListHead; } -IMPLEMENT_CLASS (DSeqNode) +IMPLEMENT_POINTY_CLASS (DSeqNode) + DECLARE_POINTER(m_ChildSeqNode) + DECLARE_POINTER(m_ParentSeqNode) + DECLARE_POINTER(m_Next) + DECLARE_POINTER(m_Prev) +END_POINTERS DSeqNode::DSeqNode () : m_SequenceChoices(0) @@ -331,6 +336,19 @@ void DSeqNode::Destroy() m_ParentSeqNode->m_ChildSeqNode = NULL; m_ParentSeqNode = NULL; } + if (SequenceListHead == this) + SequenceListHead = m_Next; + if (m_Prev) + { + m_Prev->m_Next = m_Next; + GC::WriteBarrier(m_Prev, m_Next); + } + if (m_Next) + { + m_Next->m_Prev = m_Prev; + GC::WriteBarrier(m_Next, m_Prev); + } + ActiveSequences--; Super::Destroy(); } @@ -676,17 +694,6 @@ static void AddSequence (int curseq, FName seqname, FName slot, int stopsound, c Sequences[curseq]->Script[ScriptTemp.Size()] = MakeCommand(SS_CMD_END, 0); } -DSeqNode::~DSeqNode () -{ - if (SequenceListHead == this) - SequenceListHead = m_Next; - if (m_Prev) - m_Prev->m_Next = m_Next; - if (m_Next) - m_Next->m_Prev = m_Prev; - ActiveSequences--; -} - DSeqNode::DSeqNode (int sequence, int modenum) : m_ModeNum(modenum), m_SequenceChoices(0) { @@ -912,28 +919,31 @@ void SN_DoStop (void *source) } } -DSeqActorNode::~DSeqActorNode () +void DSeqActorNode::Destroy () { if (m_StopSound >= 0) S_StopSound (m_Actor, CHAN_BODY); if (m_StopSound >= 1) S_SoundID (m_Actor, CHAN_BODY, m_StopSound, m_Volume, m_Atten); + Super::Destroy(); } -DSeqSectorNode::~DSeqSectorNode () +void DSeqSectorNode::Destroy () { if (m_StopSound >= 0) S_StopSound (m_Sector->soundorg, CHAN_BODY); if (m_StopSound >= 1) S_SoundID (m_Sector->soundorg, CHAN_BODY, m_StopSound, m_Volume, m_Atten); + Super::Destroy(); } -DSeqPolyNode::~DSeqPolyNode () +void DSeqPolyNode::Destroy () { if (m_StopSound >= 0) S_StopSound (m_Poly->startSpot, CHAN_BODY); if (m_StopSound >= 1) S_SoundID (m_Poly->startSpot, CHAN_BODY, m_StopSound, m_Volume, m_Atten); + Super::Destroy(); } //========================================================================== @@ -1026,6 +1036,7 @@ void DSeqNode::Tick () { int choice = pr_sndseq() % m_SequenceChoices.Size(); m_ChildSeqNode = SpawnChild (m_SequenceChoices[choice]); + GC::WriteBarrier(this, m_ChildSeqNode); if (m_ChildSeqNode == NULL) { // Failed, so skip to next instruction. m_SequencePtr++; diff --git a/src/s_sndseq.h b/src/s_sndseq.h index e8cb4422..46ca4642 100644 --- a/src/s_sndseq.h +++ b/src/s_sndseq.h @@ -20,8 +20,8 @@ struct sector_t; class DSeqNode : public DObject { DECLARE_CLASS (DSeqNode, DObject) + HAS_OBJECT_POINTERS public: - virtual ~DSeqNode (); void Serialize (FArchive &arc); void StopAndDestroy (); void Destroy (); @@ -56,8 +56,8 @@ protected: int m_ModeNum; TArray m_SequenceChoices; - DSeqNode *m_ChildSeqNode; - DSeqNode *m_ParentSeqNode; + TObjPtr m_ChildSeqNode; + TObjPtr m_ParentSeqNode; private: static DSeqNode *SequenceListHead; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index a47b8115..4b6aee25 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -74,10 +74,9 @@ #define NORM_PITCH 128 #define NORM_PRIORITY 64 #define NORM_SEP 0 -#define NONE_SEP -2 #define S_PITCH_PERTURB 1 -#define S_STEREO_SWING 0.25f +#define S_STEREO_SWING 0.75 /* Sound curve parameters for Doom */ @@ -606,7 +605,6 @@ static void S_StartSound (fixed_t *pt, AActor *mover, int channel, float sep; int org_id; fixed_t x, y, z; - angle_t angle; static int sndcount = 0; int chan; @@ -653,14 +651,9 @@ static void S_StartSound (fixed_t *pt, AActor *mover, int channel, if (volume > 1) volume = 1; - if (attenuation == 0) + if (attenuation <= 0) { - sep = NONE_SEP; - dist = 0; - } - else if (attenuation < 0) - { - sep = NONE_SEP; + sep = NORM_SEP; dist = 0; } else @@ -836,22 +829,21 @@ static void S_StartSound (fixed_t *pt, AActor *mover, int channel, if (sep == -3) { AActor *listener = players[consoleplayer].camera; - if (listener == NULL) - { - sep = NONE_SEP; - } - else if (dist == 0) + if (listener == NULL || dist == 0) { sep = NORM_SEP; } else { - angle = R_PointToAngle2 (listener->x, listener->y, x, y); - if (angle > listener->angle) - angle = angle - listener->angle; - else - angle = angle + (ANGLE_MAX - listener->angle); - sep = NORM_SEP - S_STEREO_SWING * sin((angle >> 1) * M_PI / 2147483648.0); + double angle = atan2(double(y - listener->y), double(x - listener->x)); + double listener_angle = (listener->angle >> 1) * (M_PI / 1073741824.0); + + if (angle <= listener_angle) + { + angle += 2*M_PI; + } + angle -= listener_angle; + sep = -S_STEREO_SWING * sin(angle); if (snd_flipstereo) { sep = -sep; @@ -1303,7 +1295,6 @@ void S_UpdateSounds (void *listener_p) fixed_t *listener; fixed_t x, y; int i, dist; - angle_t angle; float vol, sep; I_UpdateMusic(); @@ -1363,12 +1354,15 @@ void S_UpdateSounds (void *listener_p) vol = SoundCurve[dist] * Channel[i].volume; if (dist > 0) { - angle = R_PointToAngle2(listener[0], listener[1], x, y); - if (angle > players[consoleplayer].camera->angle) - angle = angle - players[consoleplayer].camera->angle; - else - angle = angle + (ANGLE_MAX - players[consoleplayer].camera->angle); - sep = NORM_SEP - S_STEREO_SWING * sin((angle >> 1) * M_PI / 2147483648.0); + double angle = atan2(double(y - listener[1]), double(x - listener[0])); + double listener_angle = (players[consoleplayer].camera->angle >> 1) * (M_PI / 1073741824.0); + + if (angle <= listener_angle) + { + angle += 2*M_PI; + } + angle -= listener_angle; + sep = -S_STEREO_SWING * sin(angle); if (snd_flipstereo) { sep = -sep; diff --git a/src/sdl/hardware.cpp b/src/sdl/hardware.cpp index 548d709d..59b4ad6d 100644 --- a/src/sdl/hardware.cpp +++ b/src/sdl/hardware.cpp @@ -129,7 +129,11 @@ void I_CheckRestartRenderer() void I_ShutdownGraphics () { if (screen) - delete screen, screen = NULL; + { + screen->ObjectFlags |= OF_YesReallyDelete; + delete screen; + screen = NULL; + } if (Video) delete Video, Video = NULL; } diff --git a/src/sdl/i_main.cpp b/src/sdl/i_main.cpp index 4d7ac911..e208bae0 100644 --- a/src/sdl/i_main.cpp +++ b/src/sdl/i_main.cpp @@ -75,7 +75,7 @@ extern "C" int cc_install_handlers(int, int*, const char*, int(*)(char*, char*)) bool GtkAvailable; // The command line arguments. -DArgs Args; +DArgs *Args; // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -137,9 +137,9 @@ static int DoomSpecificInfo (char *buffer, char *end) p = 0; p += snprintf (buffer+p, size-p, GAMENAME" version " DOTVERSIONSTR " (" __DATE__ ")\n"); p += snprintf (buffer+p, size-p, "\nCommand line:"); - for (i = 0; i < Args.NumArgs(); ++i) + for (i = 0; i < Args->NumArgs(); ++i) { - p += snprintf (buffer+p, size-p, " %s", Args.GetArg(i)); + p += snprintf (buffer+p, size-p, " %s", Args->GetArg(i)); } p += snprintf (buffer+p, size-p, "\n"); @@ -208,7 +208,7 @@ int main (int argc, char **argv) try { - Args.SetArgs (argc, argv); + Args = new DArgs(argc, argv); /* killough 1/98: diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 90292e7e..a239eca2 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -3,7 +3,7 @@ ** System interface for sound; uses fmod.dll ** **--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit +** Copyright 1998-2008 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -277,8 +277,15 @@ public: } Channel->setChannelGroup(Owner->MusicGroup); Channel->setVolume(volume); - Channel->setPaused(false); - + if (Owner->Sound3D) + { // Ensure reverb is disabled when using 3D sound. + FMOD_REVERB_CHANNELPROPERTIES reverb; + if (FMOD_OK == Channel->getReverbProperties(&reverb)) + { + reverb.Room = -10000; + Channel->setReverbProperties(&reverb); + } + } if (normalize) { // Attach a normalizer DSP unit to the channel. result = Owner->Sys->createDSPByType(FMOD_DSP_TYPE_NORMALIZE, &DSP); @@ -287,6 +294,7 @@ public: Channel->addDSP(DSP); } } + Channel->setPaused(false); return true; } @@ -891,16 +899,19 @@ long FMODSoundRenderer::StartSound(sfxinfo_t *sfx, float vol, float sep, int pit chan->setFrequency(freq); chan->setVolume(vol); chan->setPan(sep); - chan->setPaused(false); if (Sound3D) - { + { // Make 2D sounds head relative. FMOD_MODE mode; if (FMOD_OK == chan->getMode(&mode)) { mode = (mode & ~FMOD_3D_WORLDRELATIVE) | (FMOD_3D_HEADRELATIVE); + chan->setMode(mode); } + FMOD_VECTOR zero = { 0, 0, 0 }; + chan->set3DAttributes(&zero, &zero); } + chan->setPaused(false); ChannelMap[channel].channelID = chan; ChannelMap[channel].soundID = id; ChannelMap[channel].bIsLooping = looping; @@ -952,7 +963,7 @@ void FMODSoundRenderer::StopSound(long handle) { ChannelMap[handle].channelID->stop(); UncheckSound(&S_sfx[ChannelMap[handle].soundID], ChannelMap[handle].bIsLooping); - ChannelMap[handle].soundID = 0; + ChannelMap[handle].soundID = -1; } } diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index cfa20b27..dc3d6ac7 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -137,7 +137,7 @@ void I_InitMusic (void) snd_musicvolume.Callback (); - nomusic = !!Args.CheckParm("-nomusic") || !!Args.CheckParm("-nosound"); + nomusic = !!Args->CheckParm("-nomusic") || !!Args->CheckParm("-nosound"); #ifdef _WIN32 I_InitMusicWin32 (); @@ -463,7 +463,6 @@ void *I_RegisterSong (const char *filename, char * musiccache, int offset, int l } if (info == NULL) { - // First try loading it as MOD, then as a stream info = new ModPlugSong (file, musiccache, len); if (file != NULL) fclose (file); @@ -472,6 +471,11 @@ void *I_RegisterSong (const char *filename, char * musiccache, int offset, int l } else { + if (file != NULL) + { + fclose (file); + file = NULL; + } info = new StreamSong (offset >=0 ? filename : musiccache, offset, len); } } diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index b259c617..a6d8dfcf 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -251,17 +251,10 @@ protected: int m_LastPos; }; -#ifdef _WIN32 // SPC file, rendered with SNESAPU.DLL and streamed through FMOD ------------ -typedef void (__stdcall *SNESAPUInfo_TYPE) (DWORD*, DWORD*, DWORD*); -typedef void (__stdcall *GetAPUData_TYPE) (void**, BYTE**, BYTE**, DWORD**, void**, void**, DWORD**, DWORD**); -typedef void (__stdcall *LoadSPCFile_TYPE) (void*); -typedef void (__stdcall *ResetAPU_TYPE) (DWORD); -typedef void (__stdcall *SetDSPAmp_TYPE) (DWORD); -typedef void (__stdcall *FixAPU_TYPE) (WORD, BYTE, BYTE, BYTE, BYTE, BYTE); -typedef void (__stdcall *SetAPUOpt_TYPE) (DWORD, DWORD, DWORD, DWORD, DWORD, DWORD); -typedef void *(__stdcall *EmuAPU_TYPE) (void *, DWORD, BYTE); +struct SNES_SPC; +struct SPC_Filter; class SPCSong : public StreamSong { @@ -273,33 +266,12 @@ public: bool IsValid () const; protected: - bool LoadEmu (); - void CloseEmu (); - static bool FillStream (SoundStream *stream, void *buff, int len, void *userdata); -#ifdef _WIN32 - HINSTANCE HandleAPU; -#else - void *HandleAPU; -#endif - int APUVersion; - - bool Stereo; - bool Is8Bit; - - SNESAPUInfo_TYPE SNESAPUInfo; - GetAPUData_TYPE GetAPUData; - LoadSPCFile_TYPE LoadSPCFile; - ResetAPU_TYPE ResetAPU; - SetDSPAmp_TYPE SetDSPAmp; - FixAPU_TYPE FixAPU; - SetAPUOpt_TYPE SetAPUOpt; - EmuAPU_TYPE EmuAPU; + SNES_SPC *SPC; + SPC_Filter *Filter; }; -#endif - // MIDI file played with Timidity and possibly streamed through FMOD -------- class TimiditySong : public StreamSong diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 4cf4a495..6e836a1b 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -151,7 +151,7 @@ CUSTOM_CVAR (Float, snd_sfxvolume, 0.5f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOI void I_InitSound () { /* Get command line options: */ - bool nosound = !!Args.CheckParm ("-nosfx") || !!Args.CheckParm ("-nosound"); + bool nosound = !!Args->CheckParm ("-nosfx") || !!Args->CheckParm ("-nosound"); if (nosound) { diff --git a/src/sound/music_spc.cpp b/src/sound/music_spc.cpp index 485df2df..00614213 100644 --- a/src/sound/music_spc.cpp +++ b/src/sound/music_spc.cpp @@ -4,6 +4,8 @@ #include "templates.h" #include "c_cvars.h" #include "doomdef.h" +#include "SNES_SPC.h" +#include "SPC_Filter.h" struct XID6Tag { @@ -14,25 +16,7 @@ struct XID6Tag EXTERN_CVAR (Int, snd_samplerate) -CVAR (Int, spc_amp, 30, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, spc_8bit, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, spc_stereo, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, spc_lowpass, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, spc_surround, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, spc_oldsamples, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, spc_noecho, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - -CUSTOM_CVAR (Int, spc_quality, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (spc_quality < 0) - { - spc_quality = 0; - } - else if (spc_quality > 3) - { - spc_quality = 3; - } -} +CVAR (Float, spc_amp, 1.875f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CUSTOM_CVAR (Int, spc_frequency, 32000, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { @@ -40,68 +24,46 @@ CUSTOM_CVAR (Int, spc_frequency, 32000, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { spc_frequency = 8000; } - else if (spc_frequency > 65535) + else if (spc_frequency > 32000) { - spc_frequency = 65535; + spc_frequency = 32000; } } -SPCSong::SPCSong (FILE *iofile, char * musiccache, int len) +SPCSong::SPCSong (FILE *iofile, char *musiccache, int len) { + FileReader *file; - if (!LoadEmu ()) + if (iofile != NULL) { - return; + file = new FileReader(iofile, len); + } + else + { + file = new MemoryReader(musiccache, len); } - - FileReader * file; - - if (iofile != NULL) file = new FileReader(iofile, len); - else file = new MemoryReader(musiccache, len); // No sense in using a higher frequency than the final output int freq = MIN (*spc_frequency, *snd_samplerate); - Is8Bit = spc_8bit; - Stereo = spc_stereo; - - m_Stream = GSnd->CreateStream (FillStream, 16384, - (Stereo ? 0 : SoundStream::Mono) | - (Is8Bit ? SoundStream::Bits8 : 0), - freq, this); - if (m_Stream == NULL) - { - Printf (PRINT_BOLD, "Could not create music stream.\n"); - CloseEmu (); - delete file; - return; - } - - ResetAPU (spc_amp); - SetAPUOpt (~0, Stereo + 1, Is8Bit ? 8 : 16, freq, spc_quality, - (spc_lowpass ? 1 : 0) | (spc_oldsamples ? 2 : 0) | (spc_surround ? 4 : 0) | (spc_noecho ? 16 : 0)); + SPC = new SNES_SPC; + SPC->init(); + Filter = new SPC_Filter; BYTE spcfile[66048]; file->Read (spcfile, 66048); - if (LoadSPCFile != NULL) + SPC->load_spc(spcfile, 66048); + SPC->clear_echo(); + Filter->set_gain(int(SPC_Filter::gain_unit * spc_amp)); + + m_Stream = GSnd->CreateStream (FillStream, 16384, 0, freq, this); + if (m_Stream == NULL) { - LoadSPCFile (spcfile); - } - else - { - void *apuram; - BYTE *extraram; - void *dsp; - - GetAPUData (&apuram, &extraram, NULL, NULL, &dsp, NULL, NULL, NULL); - - memcpy (apuram, spcfile + 0x100, 65536); - memcpy (dsp, spcfile + 0x10100, 128); - memcpy (extraram, spcfile + 0x101c0, 64); - - FixAPU (spcfile[37]+spcfile[38]*256, spcfile[39], spcfile[41], spcfile[40], spcfile[42], spcfile[43]); + Printf (PRINT_BOLD, "Could not create music stream.\n"); + delete file; + return; } // Search for amplification tag in extended ID666 info @@ -134,11 +96,7 @@ SPCSong::SPCSong (FILE *iofile, char * musiccache, int len) { DWORD amp; (*file) >> amp; - if (APUVersion < 98) - { - amp >>= 12; - } - SetDSPAmp (amp); + Filter->set_gain(amp >> 8); break; } } @@ -152,13 +110,14 @@ SPCSong::SPCSong (FILE *iofile, char * musiccache, int len) SPCSong::~SPCSong () { - Stop (); - CloseEmu (); + Stop(); + delete Filter; + delete SPC; } bool SPCSong::IsValid () const { - return HandleAPU != NULL; + return SPC != NULL; } bool SPCSong::IsPlaying () @@ -180,94 +139,9 @@ void SPCSong::Play (bool looping) bool SPCSong::FillStream (SoundStream *stream, void *buff, int len, void *userdata) { SPCSong *song = (SPCSong *)userdata; - song->EmuAPU (buff, len >> (song->Stereo + !song->Is8Bit), 1); - if (song->Is8Bit) - { - BYTE *bytebuff = (BYTE *)buff; - for (int i = 0; i < len; ++i) - { - bytebuff[i] -= 128; - } - } + song->SPC->play(len >> 1, (short *)buff); + song->Filter->run((short *)buff, len >> 1); return true; } -bool SPCSong::LoadEmu () -{ - APUVersion = 0; - - HandleAPU = LoadLibraryA ("snesapu.dll"); - if (HandleAPU == NULL) - { - Printf ("Could not load snesapu.dll\n"); - return false; - } - - SNESAPUInfo = (SNESAPUInfo_TYPE)GetProcAddress (HandleAPU, "SNESAPUInfo"); - if (SNESAPUInfo == NULL) - { - Printf ("This snesapu.dll is too old.\n"); - } - else - { - DWORD ver, min, opt; - - SNESAPUInfo (&ver, &min, &opt); - - if ((min & 0xffff00) >= 0x8500 && (min & 0xffff00) < 0x9800) - { - APUVersion = 85; - } - else if ((min & 0xffff00) == 0x9800) - { - APUVersion = 98; - } - else if ((min & 0xffff00) == 0x11000) - { - APUVersion = 110; - } - else - { - char letters[4]; - letters[0] = (char)ver; letters[1] = 0; - letters[2] = (char)min; letters[3] = 0; - Printf ("This snesapu.dll is too new.\nIt is version %lx.%02lx%s and" - "is backward compatible with DLL version %lx.%02lx%s.\n" - "ZDoom is only known to support DLL versions 0.95 - 2.0\n", - (ver>>16) & 255, (ver>>8) & 255, letters, - (min>>16) & 255, (min>>8) & 255, letters+2); - } - if (APUVersion != 0) - { - if (!(GetAPUData = (GetAPUData_TYPE)GetProcAddress (HandleAPU, "GetAPUData")) || - !(ResetAPU = (ResetAPU_TYPE)GetProcAddress (HandleAPU, "ResetAPU")) || - !(SetDSPAmp = (SetDSPAmp_TYPE)GetProcAddress (HandleAPU, "SetDSPAmp")) || - !(FixAPU = (FixAPU_TYPE)GetProcAddress (HandleAPU, "FixAPU")) || - !(SetAPUOpt = (SetAPUOpt_TYPE)GetProcAddress (HandleAPU, "SetAPUOpt")) || - !(EmuAPU = (EmuAPU_TYPE)GetProcAddress (HandleAPU, "EmuAPU"))) - { - Printf ("Snesapu.dll is missing some functions.\n"); - APUVersion = 0; - } - LoadSPCFile = (LoadSPCFile_TYPE)GetProcAddress (HandleAPU, "LoadSPCFile"); - } - } - if (APUVersion == 0) - { - FreeLibrary (HandleAPU); - HandleAPU = NULL; - return false; - } - return true; -} - -void SPCSong::CloseEmu () -{ - if (HandleAPU != NULL) - { - FreeLibrary (HandleAPU); - HandleAPU = NULL; - } -} - #endif diff --git a/src/stats.cpp b/src/stats.cpp index fe75484b..07a02772 100644 --- a/src/stats.cpp +++ b/src/stats.cpp @@ -40,6 +40,7 @@ #include "st_stuff.h" #include "c_dispatch.h" #include "m_swap.h" +#include "sbar.h" FStat *FStat::FirstStat; @@ -84,7 +85,7 @@ void FStat::ToggleStat (const char *name) void FStat::ToggleStat () { m_Active = !m_Active; - SB_state = screen->GetPageCount (); + SB_state = StatusBar == NULL ? 0 : screen->GetPageCount (); } void FStat::PrintStat () diff --git a/src/svnrevision.h b/src/svnrevision.h index 4f4f8fbe..b6a47002 100644 --- a/src/svnrevision.h +++ b/src/svnrevision.h @@ -1,7 +1,7 @@ -// 748 +// 795 // // This file was automatically generated by the // updaterevision tool. Do not edit by hand. -#define SVN_REVISION_STRING "791" -#define SVN_REVISION_NUMBER 791 +#define ZD_SVN_REVISION_STRING "796" +#define ZD_SVN_REVISION_NUMBER 796 diff --git a/src/v_video.cpp b/src/v_video.cpp index 00d40c1d..cec1edba 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -357,7 +357,7 @@ void DCanvas::Dim (PalEntry color) if (color.a != 0) { float dim[4] = { color.r/255.f, color.g/255.f, color.b/255.f, color.a/255.f }; - FBaseStatusBar::AddBlend (dimmer.r/255.f, dimmer.g/255.f, dimmer.b/255.f, amount, dim); + DBaseStatusBar::AddBlend (dimmer.r/255.f, dimmer.g/255.f, dimmer.b/255.f, amount, dim); dimmer = PalEntry (BYTE(dim[0]*255), BYTE(dim[1]*255), BYTE(dim[2]*255)); amount = dim[3]; } @@ -1390,6 +1390,7 @@ void DFrameBuffer::WriteSavePic (player_t *player, FILE *file, int width, int he GetFlashedPalette (palette); M_CreatePNG (file, pic->GetBuffer(), palette, SS_PAL, width, height, pic->GetPitch()); pic->Unlock (); + pic->ObjectFlags |= OF_YesReallyDelete; delete pic; } @@ -1426,6 +1427,7 @@ bool V_DoModeSetup (int width, int height, int bits) } screen = buff; + GC::WriteBarrier(screen); screen->SetFont (SmallFont); screen->SetGamma (Gamma); @@ -1594,13 +1596,13 @@ void V_Init (void) width = height = bits = 0; - if ( (i = Args.CheckValue ("-width")) ) + if ( (i = Args->CheckValue ("-width")) ) width = atoi (i); - if ( (i = Args.CheckValue ("-height")) ) + if ( (i = Args->CheckValue ("-height")) ) height = atoi (i); - if ( (i = Args.CheckValue ("-bits")) ) + if ( (i = Args->CheckValue ("-bits")) ) bits = atoi (i); if (width == 0) @@ -1638,6 +1640,7 @@ void V_Init2() float gamma = static_cast(screen)->Gamma; FFont *font = screen->Font; + screen->ObjectFlags |= OF_YesReallyDelete; delete screen; screen = NULL; diff --git a/src/version.h b/src/version.h index ee7d60aa..2c00d5b8 100644 --- a/src/version.h +++ b/src/version.h @@ -37,13 +37,15 @@ // The svnrevision.h is automatically updated to grab the revision of // of the current source tree so that it can be included with version numbers. #include "svnrevision.h" +#include "svnrevision_gz.h" /** Lots of different version numbers **/ -#define DOTVERSIONSTR_NOREV "1.1.0 - 2.2.0" +#define DOTVERSIONSTR_NOREV "1.1.0" +#define ZDVER_STRING "2.2.0" // The version string the user actually sees. -#define DOTVERSIONSTR DOTVERSIONSTR_NOREV " (r" SVN_REVISION_STRING ")" +#define DOTVERSIONSTR DOTVERSIONSTR_NOREV " (r" SVN_REVISION_STRING ") / ZDoom" ZDVER_STRING " (r" ZD_SVN_REVISION_STRING ")" // The version as seen in the Windows resource #define RC_FILEVERSION 1,1,0,SVN_REVISION_NUMBER @@ -59,7 +61,7 @@ // Version stored in the ini's [LastRun] section. // Bump it if you made some configuration change that you want to // be able to migrate in FGameConfigFile::DoGlobalSetup(). -#define LASTRUNVERSION "205" +#define LASTRUNVERSION "206" // Protocol version used in demos. // Bump it if you change existing DEM_ commands or add new ones. @@ -75,7 +77,7 @@ // SAVESIG should match SAVEVER. // MINSAVEVER is the minimum level snapshot version that can be loaded. -#define MINSAVEVER 714 +#define MINSAVEVER 795 #if SVN_REVISION_NUMBER == 0 // This can happen if svnrevision is not updated properly (e.g. compiling while offline) @@ -83,8 +85,9 @@ #define MAKESAVESIG(x) "ZDOOMSAVE" #x #define SAVESIG MAKESAVESIG(SAVEVER) #else -#define SAVEVER SVN_REVISION_NUMBER -#define SAVESIG "ZDOOMSAVE"SVN_REVISION_STRING +// savegame versioning is based on ZDoom revisions +#define SAVEVER ZD_SVN_REVISION_NUMBER +#define SAVESIG "ZDOOMSAVE"ZD_SVN_REVISION_STRING #endif // This is so that derivates can use the same savegame versions without worrying about engine compatibility diff --git a/src/w_wad.cpp b/src/w_wad.cpp index 4f431ef4..b2673a86 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -1148,7 +1148,7 @@ bool FWadCollection::IsMarker (const FWadCollection::LumpRecord *lump, const cha void FWadCollection::ScanForFlatHack (int startlump) { - if (Args.CheckParm ("-noflathack")) + if (Args->CheckParm ("-noflathack")) { return; } @@ -1348,7 +1348,7 @@ void FWadCollection::RenameSprites (int startlump) break; } - renameAll = !!Args.CheckParm ("-oldsprites"); + renameAll = !!Args->CheckParm ("-oldsprites"); for (DWORD i = startlump + 1; i < NumLumps && diff --git a/src/win32/hardware.cpp b/src/win32/hardware.cpp index c3f26230..dd23358d 100644 --- a/src/win32/hardware.cpp +++ b/src/win32/hardware.cpp @@ -124,7 +124,11 @@ void I_CheckRestartRenderer() void I_ShutdownGraphics () { if (screen) - delete screen, screen = NULL; + { + screen->ObjectFlags |= OF_YesReallyDelete; + delete screen; + screen = NULL; + } if (Video) delete Video, Video = NULL; } @@ -149,7 +153,7 @@ void I_InitGraphics () // are the active app. Huh? } gl_disabled = gl_nogl; - val.Bool = !!Args.CheckParm ("-devparm"); + val.Bool = !!Args->CheckParm ("-devparm"); ticker.SetGenericRepDefault (val, CVAR_Bool); if (gl_disabled) currentrenderer=0; @@ -295,7 +299,7 @@ static void KeepWindowOnScreen (int &winx, int &winy, int winw, int winh, int sc void I_SaveWindowedPos () { // Don't save if we were run with the -0 option. - if (Args.CheckParm ("-0")) + if (Args->CheckParm ("-0")) { return; } @@ -336,7 +340,7 @@ void I_RestoreWindowedPos () GetCenteredPos (winx, winy, winw, winh, scrwidth, scrheight); // Just move to (0,0) if we were run with the -0 option. - if (Args.CheckParm ("-0")) + if (Args->CheckParm ("-0")) { winx = winy = 0; } diff --git a/src/win32/i_cd.cpp b/src/win32/i_cd.cpp index 3de373cc..c1d5328b 100644 --- a/src/win32/i_cd.cpp +++ b/src/win32/i_cd.cpp @@ -473,7 +473,7 @@ bool CD_Init () bool CD_Init (int device) { - if (!cd_enabled || Args.CheckParm ("-nocdaudio")) + if (!cd_enabled || Args->CheckParm ("-nocdaudio")) return false; if (CDThread == NULL) diff --git a/src/win32/i_input.cpp b/src/win32/i_input.cpp index c8bc89eb..750dc00e 100644 --- a/src/win32/i_input.cpp +++ b/src/win32/i_input.cpp @@ -1105,7 +1105,7 @@ void DI_EnumJoy () JoyActive = 0; JoystickNames.Clear (); - if (g_pdi != NULL && !Args.CheckParm ("-nojoy")) + if (g_pdi != NULL && !Args->CheckParm ("-nojoy")) { g_pdi->EnumDevices (DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, NULL, DIEDFL_ALLDEVICES); } @@ -1403,7 +1403,7 @@ bool I_InitInput (void *hwnd) NativeMouse = -1; GetCursorPos (&UngrabbedPointerPos); - noidle = !!Args.CheckParm ("-noidle"); + noidle = !!Args->CheckParm ("-noidle"); g_pdi = NULL; g_pdi3 = NULL; diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index 60de2bbd..f29ab2c1 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -111,7 +111,7 @@ extern HCURSOR TheArrowCursor, TheInvisibleCursor; // PUBLIC DATA DEFINITIONS ------------------------------------------------- // The command line arguments. -DArgs Args; +DArgs *Args; HINSTANCE g_hInst; DWORD SessionID; @@ -776,7 +776,7 @@ void DoMain (HINSTANCE hInstance) _set_new_handler (NewFailure); #endif - Args.SetArgs (__argc, __argv); + Args = new DArgs(__argc, __argv); // Under XP, get our session ID so we can know when the user changes/locks sessions. // Since we need to remain binary compatible with older versions of Windows, we @@ -827,7 +827,7 @@ void DoMain (HINSTANCE hInstance) x = (displaysettings.dmPelsWidth - width) / 2; y = (displaysettings.dmPelsHeight - height) / 2; - if (Args.CheckParm ("-0")) + if (Args->CheckParm ("-0")) { x = y = 0; } diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 197f3903..2e8b333d 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -404,7 +404,7 @@ void I_Init (void) UINT delay; char *cmdDelay; - cmdDelay = Args.CheckValue ("-timerdelay"); + cmdDelay = Args->CheckValue ("-timerdelay"); delay = 0; if (cmdDelay != 0) { diff --git a/src/win32/win32video.cpp b/src/win32/win32video.cpp index 23ab3852..800f512f 100644 --- a/src/win32/win32video.cpp +++ b/src/win32/win32video.cpp @@ -504,6 +504,7 @@ DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscr return old; } old->GetFlash (flashColor, flashAmount); + old->ObjectFlags |= OF_YesReallyDelete; delete old; } else diff --git a/tools/updaterevision/Makefile b/tools/updaterevision/Makefile new file mode 100644 index 00000000..20f09049 --- /dev/null +++ b/tools/updaterevision/Makefile @@ -0,0 +1,42 @@ +ifeq (Windows_NT,$(OS)) + WIN=1 + WINCMD=1 +endif +ifeq ($(findstring msys,$(shell sh --version 2>nul)),msys) + WIN=1 + WINCMD=0 +endif + +CC = gcc +CFLAGS = -Os -Wall -fomit-frame-pointer +LDFLAGS = -s + +OBJS = updaterevision.o + +ifeq (1,$(WIN)) + EXE = updaterevision.exe + OBJS += trustinfo.o +else + EXE = updaterevision +endif + +CCDV = @../../ccdv + +all: $(EXE) + +$(EXE): $(OBJS) + $(CCDV) $(CC) -o $(EXE) $(OBJS) $(CFLAGS) $(LDFLAGS) + +%.o : %.rc + $(CCDV) windres -o $@ -i $< + +.PHONY: clean + +clean: +ifeq (1,$(WINCMD)) + -del /q /f $(EXE) 2>nul + -del /q /f *.o 2>nul +else + -rm -f $(EXE) + -rm -f *.o +endif diff --git a/tools/updaterevision/trustinfo.rc b/tools/updaterevision/trustinfo.rc new file mode 100644 index 00000000..cda3092c --- /dev/null +++ b/tools/updaterevision/trustinfo.rc @@ -0,0 +1,6 @@ +// This resource script is for compiling with MinGW only. Visual C++ +// compilations use the manifest tool to insert the manifest instead. + +#include + +1 RT_MANIFEST "trustinfo.txt" diff --git a/tools/updaterevision/trustinfo.txt b/tools/updaterevision/trustinfo.txt new file mode 100644 index 00000000..3fa47088 --- /dev/null +++ b/tools/updaterevision/trustinfo.txt @@ -0,0 +1,16 @@ + + + + + Update svnrevision.h for the ZDoom source build process. + + + + + + + + diff --git a/tools/updaterevision/updaterevision.c b/tools/updaterevision/updaterevision.c new file mode 100644 index 00000000..d601c26e --- /dev/null +++ b/tools/updaterevision/updaterevision.c @@ -0,0 +1,118 @@ +/* updaterevision.c + * + * Public domain. This program uses the svnversion command to get the + * repository revision for a particular directory and writes it into + * a header file so that it can be used as a project's build number. + */ + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + char *name; + char currev[64], lastrev[64], run[256], *rev; + unsigned long urev; + FILE *stream = NULL; + int gotrev = 0, needupdate = 1; + + if (argc != 3) + { + fprintf (stderr, "Usage: %s \n", argv[0]); + return 1; + } + + // Use svnversion to get the revision number. If that fails, pretend it's + // revision 0. Note that this requires you have the command-line svn tools installed. + sprintf (run, "svnversion -cn %s", argv[1]); + if ((name = tmpnam(NULL)) != NULL && + (stream = freopen(name, "w+b", stdout)) != NULL && + system(run) == 0 && + errno == 0 && + fseek(stream, 0, SEEK_SET) == 0 && + fgets(currev, sizeof currev, stream) == currev && + (isdigit(currev[0]) || (currev[0] == '-' && currev[1] == '1'))) + { + gotrev = 1; + } + if (stream != NULL) + { + fclose (stream); + remove (name); + } + + if (!gotrev) + { + strcpy (currev, "0"); + rev = currev; + } + else + { + rev = strchr (currev, ':'); + if (rev == NULL) + { + rev = currev; + } + else + { + rev += 1; + } + } + + stream = fopen (argv[2], "r"); + if (stream != NULL) + { + if (!gotrev) + { // If we didn't get a revision but the file does exist, leave it alone. + fclose (stream); + return 0; + } + // Read the revision that's in this file already. If it's the same as + // what we've got, then we don't need to modify it and can avoid rebuilding + // dependant files. + if (fgets(lastrev, sizeof lastrev, stream) == lastrev) + { + if (lastrev[0] != '\0') + { // Strip trailing \n + lastrev[strlen(lastrev) - 1] = '\0'; + } + if (strcmp(rev, lastrev + 3) == 0) + { + needupdate = 0; + } + } + fclose (stream); + } + + if (needupdate) + { + stream = fopen (argv[2], "w"); + if (stream == NULL) + { + return 1; + } + urev = strtoul(rev, NULL, 10); + fprintf (stream, +"// %s\n" +"//\n" +"// This file was automatically generated by the\n" +"// updaterevision tool. Do not edit by hand.\n" +"\n" +"#define SVN_REVISION_STRING \"%s\"\n" +"#define SVN_REVISION_NUMBER %lu\n", + rev, rev, urev); + fclose (stream); + fprintf (stderr, "%s updated to revision %s.\n", argv[2], rev); + } + else + { + fprintf (stderr, "%s is up to date at revision %s.\n", argv[2], rev); + } + + return 0; +} diff --git a/tools/updaterevision/updaterevision.vcproj b/tools/updaterevision/updaterevision.vcproj new file mode 100644 index 00000000..f39914dc --- /dev/null +++ b/tools/updaterevision/updaterevision.vcproj @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wadsrc_bm/brightmaps/doom/BON2B0.png b/wadsrc_bm/brightmaps/doom/BON2B0.png index 0fdf4d6aafb75238f56f573f4a3a1522c898fd77..ef16358e57574ef86907977daf6f182e489f420c 100644 GIT binary patch delta 81 zcmbQtm^?wkn~|A;f#LezyE#CLEx;$lRYpbz$jn>wvKvTAc)B=-RNPAb@qa#}_^z5+ i4?kD7-T(ejiDBvwS>=5_M-~CqFnGH9xvXU%E89K!0@)%Umr+u7I;J!GcfQS24TkI`72U@g7%&+jv*HQ$tej5KlqP` zFiv2clF((+9P8itb}^g7WQJ0vz}VQo_C{{~vNoS4WGuPCFr(L?xnL)Q_b$m-oSY2e Xf-hZ{xIX#@G>XB~)z4*}Q$iB}3$Q50 diff --git a/wadsrc_bm/brightmaps/doom/BON2D0.png b/wadsrc_bm/brightmaps/doom/BON2D0.png index 47a3dcdc2687bc3bf8e31a0c6e59e5318fbbe926..a08679f6ee03d6962cd61f1b60ec2dcc73f78a9a 100644 GIT binary patch delta 80 zcmbQrm^4Aci;Bd%8G=RNPAb@qa#}xK)ir gi`{&7#SI(`eJwK8KeijJ11e$gboFyt=akR{0F;IpS^xk5 delta 131 zcmYeA$~ZxymV=Fff#Gejzdn%SEbxddW?HSe978PplT#8Be()a= zVVuA?C85itIo7}P?P4~E$qc1T7jNA7VQ=KtFKhE@LdKFC3^RHSnhSO^c<*xb^$?ME ga67d`N0XW1=gfDNHrKb^02;~Q>FVdQ&MBb@0L^VK4*&oF diff --git a/wadsrc_bm/brightmaps/doom/BOSSE1.png b/wadsrc_bm/brightmaps/doom/BOSSE1.png index 655e995503247585c264c44492e5fdaee8057599..cc3c407c6e96538047cc8ef8fa46e2881286bac1 100644 GIT binary patch delta 342 zcmV-c0jd7J0p$Xa7=Hu<0001C+~m#x001peOjJb-3=9_+7nGEgq@<)A92_SnCrnIC zTwGi+F)?&>bYx^?jEszYe0&511ONa4`t$%!0002!jp6~QyBN7iRa0L#=YJU}^F3#==^gI5!!PH5 zzZ8C0^An6gHUfnoM~(}C3C{Q477B=P{5h*2DG$$oSHXl-T)qn??;$pTjz9rxL5aHu1};QNm^LmvlSqM?dnE*UmQ?&iM5;=N zbB0U_vKgXBRZqi396LlxtH}sa?tc!1h+8L_HEgGL?Y)@>4CP`{hp2+zhpi3TFu;Uv o=sjEreMr6SH2uk`NX37P7m6xAxaM_5Jpcdz07*qoM6N<$f^u(+{r~^~ delta 174 zcmV;f08#(t0=@x|7=Hl+0001~JZruH000J1OjJcLFE0Q90O(x_kpKVyb4f%&RCwBB zc)tBV7&I_EsfV%t^1xUJj_ilBEboyoL^vd;g6vD|;aWSf7&bnO2N6Py1Z#ySjV zJ+_Cjp7Fz258$#N_rqBK{|_?O3pnc{ob?;d`ix-xgt0!t#V7uJhqL%$tbg`!JL(6c ce+MrM0Mh707T4HN$p8QV07*qoM6N<$f(0#3^#A|> diff --git a/wadsrc_bm/brightmaps/doom/BOSSE2.png b/wadsrc_bm/brightmaps/doom/BOSSE2.png index 984e5704bb78186a09e8855b11fa5f7af2ddb6f5..31837cc6cf46f1adeee16ae88230a38eef5deff7 100644 GIT binary patch delta 180 zcmeBTJi<6ZqMn7Bfq@~B&FUi6=6Ql5m8gj#>sh1iy*b7Xyw zu*UjEL@BKD$n2VAy~<;z^{$hNPg`7_HgwNeJY!83_rI`L7SE%%~OcF>5;cGvSgPyXAkIj+}k`O1BRxJ!fx>KcU2U X%)z(ec#mo-&?p8^S3j3^P67R6)d zRD;aGOq{L_j7-KYIW>J+CvJbH;R}T6mH0xalDyy>l^l4FBp3hy#N0(4 TmrOV+00000NkvXXu0mjfXg*Y{ delta 136 zcmaFKID>J5L_H%j0|SG_$M#=9iY>q=#8qEkAIN+W%Q+E9`FgrIhE&{2{=v`4qfuho z@{jxJ{|W6EoQz{o+fx#$I`+ o`PdP0Okrg0ssI2 diff --git a/wadsrc_bm/brightmaps/doom/BOSSE4.png b/wadsrc_bm/brightmaps/doom/BOSSE4.png index d6107d5f65a7fb06aa1025ff1e1c30d4679c77ac..8ffaed4bd564fedcabbe6c861fc0b5cb6790a388 100644 GIT binary patch delta 279 zcmV+y0qFj_0j2_w7=Hu<0000RCbNkE001XYOjJb#1Oz81CzO`Y0J=#;K~#9!?b1CCfLwqTmUxNo#M;1{8mE=)T zd5mEOINp#EHkUEV2`@g1Vr&jDws*|q+zPEIr dspHo357dPg`VbpukW~Nx002ovPDHLkV1h+ub29({ delta 171 zcmZ3+w3~5)L_H%j0|Ue5V>Um56kC8#h^xN7K9Kn$mUAMID)4l145_$v_WVZP1_K@z z2d!leT*((Uaw%U3OcQZSV0ocAgP~Q|Lt&HPlX~W^9acZ@w{q9Ip9!qlaZ1nem+C?D z4yV==?@bptY-!+F(6q^IPIkf(?Y)wjZ4wNbJ0&^Ucc_SNkTwuhKYdX^Y{ur=pYk>? X|G&=Df5%-1w4A}y)z4*}Q$iB}`szPN diff --git a/wadsrc_bm/brightmaps/doom/BOSSE5.png b/wadsrc_bm/brightmaps/doom/BOSSE5.png index 560c1246a491a1c0aefd986c800187416e3d93c9..e5bca33a300c684cb0a5d897a44e1d7406fbf32c 100644 GIT binary patch delta 276 zcmV+v0qg#u0iyzt7=Hu<0000*LR?h<001XYOjJcDCnqs6F|4etTwGkFq@+wtOmuW~ zjEszAWMl*c1eBDNe0+QW002elKAZpm0Jlj*K~#9!V>p8yWM$6GoHZ?T=1c}03O4PW zvAUzX+7hRN)8^f$s;f??;#9EbOw^fGYmTRD;!<$t#I7?gXMc{TRpL@`V*8RaK4*4q zv&5yK3#k9hnN`)DxD}iRy3}RWR$L0geIWE=TnfV0KzzFcmx7}?Akt-ZGfr>zfC#S& z8=MNj;MC?b_!R8kbG!$if}`!_8KyG$OgI`EbC3t031AYxBS#gCDi~ETs$f*XsDk07 a003JT`FMbKDvU^eTnD5UdAc};RNP9w(aW22{zXQ@0i~F|#aB(%7`a|fi_|%;^?2IO zs?Vp4bd08UibuhGSk@Uv*R0FmZz;LASDV2(?RS>J);o-s=dFr$ zNJ`Tb6R%+Ycy6lZjTl}wZQ+8G35ARjIkPQGnayVEUfjLBJz?@qp?TYO=a`q-CNf^U wk-8{Wvh4i5hQqm=%O6j=X{%)ngI`h^6px3wNQ%Dw0(35er>mdKI;Vst077$RaR2}S delta 140 zcmeBXn!`9jqMnhNfq~)QyR!~JiY>q=#8qEkAIN+W%Q+E91$w$ThE&{2{=v_5URuK8 z%K0>ozm5`K>JBfM`4zq#O6EzZ`ewsn`=7tX?k}TKz5J?o_91`%+iUQO{tstZSTEuH q^O3->^|~SV%QgPiPmxnIVYvC{;p)5(tsui0JYD@*UDi1zGywn#do%_B diff --git a/wadsrc_bm/brightmaps/doom/BOSSE7.png b/wadsrc_bm/brightmaps/doom/BOSSE7.png index 1232c9b7d5efd8e52f94ea1677cace090c776ce1..4a9e0a65341102a6fb3b2c234af9c89e5c690d77 100644 GIT binary patch delta 290 zcmV+-0p0$!0kHy*7=Hu<0002Z-hi|K001XYOjJb#1O%j{q%kouCnqP2jEtkl`3##DE0}Ke^pF07*qoM6N<$f@oui*#H0l delta 165 zcmdnUw3Ts!L_H%j0|NuA9sdy^#TMWb;;OH&4`jZG<(vqlvOHZJLn>}vy|9tD!GMRw z;j$rHv%*HM$qHsCydsJe>dl diff --git a/wadsrc_bm/brightmaps/doom/BOSSE8.png b/wadsrc_bm/brightmaps/doom/BOSSE8.png index c73089392d7ce8520f67d9a8bc531a09ca6baa1f..d8be17d60cc12dd1e6bc63a23ff6539e45067754 100644 GIT binary patch delta 284 zcmV+%0ptFy0jmO#7=Hu<0000%k!uqG001FSOjJdTjEpBICzOZY*WOb6_UF={4E9uMetV z9eEYAekld({BZ94f4yV92V%wt`|mp1$5gT27dh)#@%hEl=HE=-m%P7@z@Dq$za9kJ izeKqA<=puX`xOV`A4U8@eJ7m&0000}1SDg4K&+^Y; zj$ymeInk@m61kiKZoiuU3b=igTp{&aKA^2eaf;!8Mf$=m;xZJ7>%r^FZ-j;~Am(6{KvC!lozopr00f3STL1t6 diff --git a/wadsrc_bm/brightmaps/doom/BOSSF1.png b/wadsrc_bm/brightmaps/doom/BOSSF1.png index 61c4cd706d7676e1bd53af7f0bcb038323ee4fdf..7667a456a1546ef3e26d8010132a8d40fa4ba4f2 100644 GIT binary patch delta 360 zcmV-u0hj*U0rvus7=Hu<0001JH$=1m001gbOjJb#1Oz81CowTGq@<*bjEuazymWMQ zl$4ZAOiZk-tXy1Ne0+RlWMlvU0CO8D)Bpeh2uVaiRCwBBxJwXRU>KxodZ}SfZwFyD zw^r;Wpk~kAV`~YSbK=gK6}NK<*tP9=?zOY{)m$?*UQ8J000J1OjJcLFE0Q90O(x_kpKVykV!;ARCwBB z_zwj?7>H*5#UkbpXSKsvpXFh!PnYoo|-Q5pk-Gs2t|6n)@(L`+jgW)gSwm)d$@)pke z31|I5u>Mv+SsxN%VxKm{!|ywc^%oJQpD|@gildRh0sx8hTufuSZ65#t002ovPDHLk FV1g&+S4RK< diff --git a/wadsrc_bm/brightmaps/doom/BOSSF2.png b/wadsrc_bm/brightmaps/doom/BOSSF2.png index e7c095fa4d7daeba7f1def7070775d9c411eba13..1884c9cb98f55aaa2cb7163345fe26ec649849db 100644 GIT binary patch delta 383 zcmV-_0f7G70geNZ7=Hu<0001DYwG3z001yhOjJb#1Oyx$95FF5#KgoWCnvnTyriV0 ze0+RNOiYxNlyr1-TwGjaWMqttjI6Az0002~xfihj009|EL_t(|+U?Y_N&`U@1<-ti zy`-}j{~&z|*^fw(*x8s6*fs%si%qF$V8BMfglPhiBoIL`h=0+oB5bo$2y14tGuIR$ zAm+VPYO~ut4l~8RdnX6>%l!@C@qEg%#DYC-B*$jVT|VkGNVdXEhLU6>FRQl@o7nD*6R?0!}d_{4xU#diBlH#L}cP~1j*eb-H7SuwgF`UxW zzU`~0m^dVd2Y4aFPkfk?Em?b;g=`cZ&%tshf@Xyo6v+R7-K(-aS+D%31fWT4rP2dhcezR|3v>=9>jRK{{NqRuv5?f ze+cutGK^6SV?4HpG7i^685dzv|6n5TVIsc?hw6xB003^<5bqBNF}(l)002ovPDHLk FV1f*kQd$52 diff --git a/wadsrc_bm/brightmaps/doom/BOSSF3.png b/wadsrc_bm/brightmaps/doom/BOSSF3.png index fcd71e9d42785476f375cb3cfaab6b6827cc4000..f6b72649af2c9bba897226359b74b8a06603e501 100644 GIT binary patch delta 201 zcmV;)05<=G0oehN7=Hu<0002qs1RTP001XYOjJb#1O#McWE>nEl$4aDq@+wtOpJ_- zbaZrle0*G7Trn{*CnqNW001&)6_Wq}0BuP`K~#9!V>p8g9AFrg8j4aaIHgW_;FRjY zY2Yy*oKiVw$dNi>gWK_k=@n2X%WH5+O~<9{*r_=<4Lpg{u0mNWaY`*%ic{*SC{DXh z?}|KgW)F5}oZhv1Hx6BgwD;f+s8gevXjE!cihvXVn)G(U^ck#I00000NkvXXu0mjf D^FB?F delta 115 zcmcb~*upqLB8QQgfq_BHW4-{8VhivIan;w?2Qpv8a!v$NmYyz-Ar-fhfAF*MOR&xP z$Fo%4`Q`6`D!VD`+!rT4djE0uZ{}71jHm3E_1fQl>1W2DZ~s@k`rn@;#c);X?8TX1 SrzQdoVeoYIb6Mw<&;$UXxhwhnxj4A-lHVmO(3N#cie=c0(E3J335U zamaO)7nek|}1|KMktw_;hcX*^XCw zRy989on55YsMh_tqAHoEkV*~m$8 zC;pVWi)Pti8v)(f_@%d(b>p>rYxHm{9Y&=`rH6?$04;)N$1h*t7ytkO07*qoM6N<$ Ef<5|j+W-In delta 154 zcmeBUTE#d)qMnhNfq`Kj-`r0?iY>q=#8qEkAIN+W%Q+E9#e2FqhE&{2{=v`4FL5GK zML=;WLuyT<;lBe*SI1E2i% zEtv$|=U$3ezRWW{IvSt-?bAwI@tNxOYQTb8%QTb7MO636>pvJ+eaNI}$0000q=#8qEkAIN+W%Q+E9rFyzJhE&{2{=v^^FHvCd z?|?-8_rpuloF)JNN>pl3Vn+Xj@Vjc^+YaTWj*`+#2&W}GZr;zCw zP^RWU!ht3C4wuxn82&3eAYr_qXTHJrKTVcj+5PtSOB$U>U|?vJ`Y#<{_-6vpMg~t; KKbLh*2~7a($USlZ diff --git a/wadsrc_bm/brightmaps/doom/BOSSF7.png b/wadsrc_bm/brightmaps/doom/BOSSF7.png index c1e6faf5fb0d285c5c7cd5083c3080d50a65c027..71826d44008ff24d51802be68f38178ea350a459 100644 GIT binary patch delta 322 zcmV-I0logb0nq}G7=Hu<000084ax-o001peOjJb#1O&vy#Js$`tgNhbbaa%Ilw4d~ ze0+S2jEqc7OffMrCnqOlWMrhIqyPW_esG-E0002rNkliOScKnXoCm#cJJQay-|lM{8kS_-$50AVGNNyhZa;ReIU_$=ppzT?4P&T0v4$F9=h=B z>F?g%y<3N&`eiN93@nCBz4x{bo7pce+`}4|FZ!?wKf@mL&z@ryUULpZ_!USv3sd#F zcdw3N5#E_LtvhEGrts@MyUt*9$?JVPu?f!u*^AAAuhwA|e!XsxW~ou(QDI!d0JvIK U+5L%8&Hw-a07*qoM6N<$f|I|MHvj+t delta 172 zcmcb}w1;tmL_H%j0|Uc>x2~l?iY>q=#8qEkAIN+W%Q+E96?(cjhE&{2{=v^AFX6H9 z-&N;|ZwasXTfSeHQaaARu;ul^5a}7e?kDgrlj%8lrICwK;On3NK;p-L7J;uH;~9^% z@g}@_&T5d#9QJcRf6{Nmj!E+cxK=%scX@Z8O~jGot17#bsl${#KO5FOw9A|)*CfJ_ XcBnyKXq(eDpzREvu6{1-oD!Mp8wFboU18Mx(6G?8a!4<5Oh zhYt1Nv@hp`&mkOgC!4ev<8({nisCgfIOJw6OI#x=ie0WES${N&D>Ok6i`;P^vssH4 zMM>{ogGHatOrx}=(Ytr&U<;ShWudEf?@q%SW~Wv~t=_$R2R6CG$IY~Wa=UG?hIL!& z?%l?_w_ue!0hHU^y#uSR1eJIWz#W{G<q&38iQ6htMrh{(*QX?F^+E?7{Q~#`|FU0y~8Mzz-4sA-^9? zzmSKB|FMDS`)8LArvIAB|NsB5hT;ECkj--#{;vSZpJDia6h1^hVfgfA zCnqO#baY%?Trn{*OiWAw001$|>WBaU0NzPNK~#9!V>p8o9AFrg8>VtTc;rsvGjjqB z_Pb2`t=utBx2`}FcimT8?zogEz=$!O_p_nG56 zT~ec+x~96MVKk({Q)iBEO^cqn2YdQlb7nU%K4Zv8*C$5v^r##qascu`L>|92jqv~g N002ovPDHLkV1oYPjI00v delta 169 zcmV;a09OCX0=WT@7=Hl+0001h$E`sC000J1OjJcLFE0Q90O(x_kpKVyZb?KzRCwBB z_zwm@7zPJ@14_Su(jV~CzoGPBsQMoq5c*#Pg#HFL_s9RcVEWhp&k))kBK|FYKbUTN z54Hb0g#P{!Lf8KT(|>Bf{GT6Kj)Uma3=I4r`aFYxJy`yLJR-#XAD^T8=l38ASDZ8e X*jcVa&}+eC00000NkvXXu0mjf_I5~S diff --git a/wadsrc_bm/brightmaps/doom/BOSSG2.png b/wadsrc_bm/brightmaps/doom/BOSSG2.png index b3451000c1b97fbf2f72c0ad836c9df5cd9e4d8a..fc735a9cb93b9f110f6b7dc70694da9e6774b2cc 100644 GIT binary patch delta 297 zcmV+^0oMM)0k{H?7=Hu<00016@M6UP001FSOjJcDCnp311dNP~l$4ZobaXK>F?@V{ zOiWB%TwG*iWB>pFsqU(e0002eNklPf7?mHD#|2gr(!Y|B`4h;OUrs=N z#i~^v_~a{Qt!l+5UmA(m{mTU-($Z$(mJbx|OwPa^J}V`~Re##D@Pyy22$v{O_|L*B z+c|yJ(kO8JRA7~l=$ZwMzvU2t#VA1&wX_N)eyvtnSuID%FXcjt{}r>MRxMkVm6cV6 zJ$)?in6+fpETEDZIMPphN-5M!*yWeEw3~sQh(mtW@`|)_AZ4t9D}7daf^AsTg+qRM vi_7$?#YP&q(<1i@7iE+7(fmCsKhop@5IGKCqm3Hz00000NkvXXu0mjf8~cM) delta 178 zcmV;j08Rh60>S~17=Hl+0001!5bd=9000J1OjJcLFE0Q90O(x_kpKVycS%G+RCwBB z_zwX;7zQb02aNFo#`u8Fcn@b7KpDpipp4rvT{|S8j1LwN#=iv>P?6*x4E10cpr!U8 z#?Sg64Ez598GAv_xSJ1g&-1t85XQspn<0!3^?xCZAN)Tdj4OZ4AE1tB|5p!TG$cSp gelQQ}a3+%h09d5!*dP7ArT_o{07*qoM6N<$f)Rs6oB#j- diff --git a/wadsrc_bm/brightmaps/doom/BOSSG3.png b/wadsrc_bm/brightmaps/doom/BOSSG3.png index 8caeef0bbcccec5b034edef328f81af21889e77b..8d74f0cdfeee11ed36cbd68a34cdffdef5c845a1 100644 GIT binary patch delta 298 zcmV+_0oDGr0l5N@7=Hu<0001RY0-xCfEhjTWM(n$2;9^_yl>95OFY#Nmpc*g7D+=N=ZBvmlwbvRGu^f|X;_ w0~UOXH66UgrXvLsr+4q(9nB@9GIWsv07G(zms6R{(EtDd07*qoM6N<$f>8v6@c;k- delta 164 zcmV;V09*gL0<-~;7=Hl+0002E1YwE*000J1OjJcLFE0Q90O(x_kpKVyX-PyuRCwBB z_zwmR41<7v52f$F$3pM_|NlIQmjD01|2&Yk|NkGz2Gbn%KzjQB|2zLfpF+xRz>0001=NklL7?q);%yOJEvvA3*#A(_JGGu1p zl*s_PU?o-=^YU3(od?vjyu5tXWGO5%YRjgrT2^U=&8LgBR&Gwt!e-hcAXz>w3wwyJ zEGeFaLnbs7dn_z(b(x7>W+{iuEUYq1S9Gnw5vMb{aK&~7&UDa)S7ufh7MT^Rs(@a? vCbP7o6UbPBJ-yAu;y!3foH?2sN4N|CA^FEfx7=^$00000NkvXXu0mjf0$pOA delta 149 zcmZo=TFN*q=#8qEkA0!gXIT1)jdAc};RNPAb!OzCa^JUq6 z=a;_&s_gl0$$6cA-+tlESAnR9HZ5^?_!j;@>3lZ5{Zpceeak<_rS*=zxEzoq2C?4_ zNQPlkV5kO@PYJ~)9|YH*5*UWVdZ2ttTAGQg7#{zdxP}^~2`}RbzhqOPv_v($;hQ*u k(oeE;&}jY{l^-VZ05{jT#jpL!&j0`b07*qoM6N<$f)vn2WdHyG delta 120 zcmX@c*vU9SqL7i9fq`Ma?Z!PoiY>q=#8qEkAIN+W%Q+E9*?GD+hE&{2{=v^^FTp1L z*s`Zk-lzWm!42|iN&LxIt~bByCpqOPu9Q&Z#P(hD0|Ud+JuhwmDYgKg5LbPDeIWBiEayZZ7*cU7`3FCnyad}U zJN~85onQV9*d^z7-8r+8BQ~9X;dh6Z%mE+g|9G>p{Ug7_OHP4b^BG$H1u`tOce-kO khhf?O4g57f1U#7;>Y`fj{4IHr2Q-$!)78&qol`;+09+R{X8-^I diff --git a/wadsrc_bm/brightmaps/doom/BOSSG7.png b/wadsrc_bm/brightmaps/doom/BOSSG7.png index 40c0799ad3db0d5e0dba09a5124b9dc7f0a7f018..90010e2df9339c566d521cd4fe63215337af6472 100644 GIT binary patch delta 212 zcmV;_04x8Q0ptOY7=Hu<0000W)kZM@000|MOjJcDCnt1tbOZzhF)=YrOiWx{Tx4Wq ze0+QW000DkTm1k40E9_IK~#9!W9YyR7)HfM#Sx$ZyLdAbcJWpvhK@FD62S}|?O24J zU9gJ>1I_8Ezz}XpcLs`MYG`%~#_FDSQ4e(SauCrd%z-XmluZUC+CnhGp$tTXi=vCS ztAQmWFvK%J#*|uMm;>@mdkSVagFI92fhGLQI@(jQ#!Gpr$7uQ-vEl$MU*PuJ8vZT- O0000q=#8qEkA0!gXIT1+tdAc};RNPAb!Ov7L(Z=?* z?V)YQFQ$ch4lAsA?mLEXAI=vjWm&X|)#dL`dv=$tCyb1Y0000JxxEnp004?fL_t(|+GE&_2QZ9Ej!NQ|EW<0QjYl#GpXAJ) zWV?1JJp*CqY$#(DHp!X0J;99CtFajy9zMMrm*niyRh8v4xm+*N#v!?TM@L6APRZTIUEa7QojZUbunTAK?OF}ih$C!wts2cp dqmopS1OU}3yyq~{Vmkl;002ovPDHLkV1f>WTG{{r delta 124 zcmey&*vmLUqLh)Dfq{YZ=4)>t#TMWb;;OH&4`jZG<(vqloIG6|Ln>}1|KMljX`Zyr zeevs)CeQg>_Wj>3=Tq#IIqiSKFaKuy7P|xdg84W27s_98e!*{Wi!bY5jK<%J32o9% aYz)rfQ?~l+=Ntu^#^CAd=d#Wzp$Py#)GYr1 diff --git a/wadsrc_bm/brightmaps/doom/CPOSE1.png b/wadsrc_bm/brightmaps/doom/CPOSE1.png index 14afe42f9db2e27587701f9b1c2a1310aeb117f6..437c2a463ca5e6310f87c84b515d487b7b8eb44a 100644 GIT binary patch delta 103 zcmZ3)R5?K+k%^gsfgxn4M=p@!4DbnY-LhrN|NsAiyay*1@&PGrPZ!6Kid)GMtnAtn zD^A93@#=n+c4^99BkqH7QmqN?ZAs$pVfG<^ENX5r_-ZYArS)FqDo`7Pr>mdKI;Vst E08JVtq5uE@ delta 274 zcmbN%-R`^*+}X(g`T6U@EN z@%`U`FK>4JT%)6r?Unjn`sSL5E%G;4glyvnv+5B{5bJ$R*`EH{c0o!9`w!PO`HB57 ze^Yr`Zx;e9)!#H=Aro>vBwyzN?dR>}QuD0jY$9>1X>F@x3 O$l&Sf=d#Wzp$Pym>w-1_ diff --git a/wadsrc_bm/brightmaps/doom/CPOSE2.png b/wadsrc_bm/brightmaps/doom/CPOSE2.png index a0923eaa00faf91c267bef8056ee4829651c0eaf..9597368cf4c1322b5fbf04341dbd1b96bc9fa16a 100644 GIT binary patch delta 120 zcmdnS)X6wOqL7K1fq|i)>5)H>;tcQ!aow_I%m4rXfxHJN7V-fpYfl%)kcwN$5v?p@ z1{Zp^XRxl#U7)V-#oceTE{@LaLap1ixQ!etsx`;?GS}LL@@dZV*%=Hf W-kouHHJck~41=eupUXO@geCwLY%G@m delta 295 zcmV+?0oeYD0k#5=7=H)`0002;Yzo`}000SaNLh0L01m?d01m?e$8V@)0002!NklLJ7L=_7&0|UeQu(fl66n}tEh-*bf#g;8w{{R2)=H>>EalY;rw=bF4tiN z5tg@4Z6>pSt}}a?&f&DHzpZl<<3js+I@XI7_b#1sYtypoYeAdWzT~p?yO%R9CQD`U zbJ_h-+1g7549;n@-V2|u)EA_3d709c4qG$Vfc}(QA@wa%8;>vJcs0>yc2|J%w;JEA zPZ{l<{?GiY^R;f<(rf$qoTvTSQu)_HYie=c-qMYqglF9nx_5u&y^qqTLf-%2sb2mq zweMcT;n}-xJPp5hS6 z^C@GjOWNLXG*T2WE)|Nnm=Z$|&Ot3XQ4)5S5Q;#P75D?88O zhMhbI=X5Pm;6C?cR+m6*+gX-niGqC1&72IL=d&mI@4GnzsDr`N)z4*}Q$iB}glHo( delta 236 zcmXT<%Q!)zo`a2nfuXLBLm5bM7I;J!GcfQS24TkI`72U@g6ln9978gk-%bzYYETei zd3){NJtGbmohHtE%5EQu6KBP)*cRkvo%iZ8qw)PK1?jtIzUBPkY8~dIn-$P}h2iXO zN8TW2llN@@6F0~{e|A}a-y^>AxvVKR4>-0ONODJ{W#|4qH=+4r#DU%MeFszg4lvq? zeOOho&XV^*_x@86%}o0YKJ0y@QF|F%#y#Gw2~rCx%~v>PH9Wh!fNdq?tny~5 gB`}Gfcckwy_8;i_mUi%_4$$okp00i_>zopr0L9H?LjV8( diff --git a/wadsrc_bm/brightmaps/doom/CPOSE7.png b/wadsrc_bm/brightmaps/doom/CPOSE7.png index b31ff849c7a35228e382e07d78e1fe51a53dcd94..587d4f62fffb7439ceb8742d64920b2ee9f4de3a 100644 GIT binary patch delta 121 zcmX@k)WtYKqKJi=fq}tc%CW^jia)?7#C6M-EpBdZ|NsB5sHgyn$<5&}08(b2E{-7; zw~`AEa!Rl*onm}Cq5NUTw4j5ks*e`6xSqOkLqcb%OP`)i+0F--o<6dXieDV0?9auJ Xf49N*Pr@u$pa~3~u6{1-oD!MK)x0Q z9+!_rb2^&~?Zf^Qb<=);0Hu5Ka-qD(HJ#6DEZRaqRy}wRO5RY8F&!|4d_0sBop2~?^5#l$aYmfia ziGTmzC?xwYmjScx!X%bi)Az03dT(wWs}*avAjjS>e_I!Zq`x?n)1%uyZ((u6&)FLq zFBr32IcmVNN8^|MQU?vqu(>=NPb@A@5t3ScX^?%!cca0za@Be0SkdlHt-4 z!>qY;=W+9_vOJ=|7RsDuD|uu=mcv^0W{D8q8D9;L)fd^lU;U}{e=S>of#fOC1*z)< zZmI1zy}@j9>yKPQo8`M(EH76b6-!~u>Dm97w=C`RcZO*5JYMhR*^e2rgXeJ{klr72 zOoHV@8{^klziz`7QUR||Vpz1|Yl=YgD#oqzc>djfz%)rpAZWvpxKN)@CtO@yS65eNW@dnZfKX6SNJvQk|NlKbJpcdz%;T7b0003@ zNkl6rFu3zL5@pMD5Sc^`qFkR zyo2;=v@|VrqP5T4xXw9(Vhtxev1!hjbG*hCOeO?*wCF>^GMBhar+H@VSQPS-Jkqc|5r-@db{uBQ4VM9nLT`Q_m#V>W`t)f>2pBTTlyV~ ub(gQInS2?l!yWg{R{tZ8XNTT$FMa^Nn#WK@eEQD-0000VGd00Oy5L_t(& zL+zTej>0eyM7e*UrbVKmK}bl@QvwYQ4Gj{1!Jqq>oaVAIHeSaYi%3|q$R#Fu8BcaM zK7J;XiF@|ZG{BCfj|Q}HfQhR6{oWl82X{W7P0knLDu>&sNb1 z2?|BAE&Ip80%$gyH66f&=Jv-adpeyQ+Gex)pCu>^P&7l}GS32v4;HsCmy0E!JXbj^ zeF_EXVM8_TUcDrqCgHLo` z+{qdFW9w*V{b&E0eY(THdrKU__>cGkj#@{0J s_=!p#Sg!aK7Blr=Gp9V{@$ta^0=8R2NJvQk|NlKb zJpcdzY&RVR00041NklZz03`OalG%c$oU;qD))_R!`%H0!?mK>MCV?dsBT5cUWgqMo@^=%5)?`ck>H?ht3jL8I5haF5^j-*ddy zik+NaCk;nX#_2VY6daUwsh4GcU2e*`8*|m~#edJI&L5L9PVGMY+1`EuZlp4KfjYp| P00000NkvXXu0mjf67|_k delta 800 zcmV+*1K<4U1FQy+8Gi-<000z4!I%I5010qNS#tmY4#NNd4#NS*Z>VGd00Q1gL_t(& zL+zPMiUKhZhMkLu;!YPTxDwpEbmv9{H-cAi7?=^h7m&hNT>7V zuUF#NXf#qkt=SjgT5ZjObr#vGYUSZ}yIsk}ET`Pza8Q8bEq}A+l!05X*BulA;(9zD zbE{Ye4hrElo6UM4yvJ+1-R6QU2S>{7_xlb8pYfhPXE4!na0CRCBoOky3}EG>WhMxl zS(jA~a4^Hm<)TifQ!Zblar40q`OzDJ9x}C#+5Sovws}9^B2yhl<9lah6MO}y%aSL zm{|gpDiB<`dahb> zwOT0-$9Lnt5fosL$0HBi?RM+oTmeN_)&?iZdj#f)z;flVx*RUVEMxA0YGjghnr0c# z3`h3Q{eO&BR`(65S;m0jUhZqba2yPSKrId!J4xJ+dULbcxO}Jo9p=d496`ukegTTi zP#V#E7Jv$BpT5uM(|aL)#v3lb^sS0-G;@s`^1H5@#~A51s~!)PtbW%QrYKbHGtMnN zbDIBVHOJ~p$0Vo##qyt9N?~|^07XF64HvU8?0??R2q=ND|8U~a9k|k|pHruDwj&GS zqSXmUm62;V?|`ij&Qa-0Ot(HbsKNhlaP8~|)T<9JVR2q3oav7fVKAw;aOMFeLdp7+ zBcG#F=;q)EP;w$>FxxtnYZ?xUN&XcQ9qk1~CP{KF)wfQwY*slA7Eztlo9CL|6(sWc eIvIT~1>84HjU});y!j*m0000Ha5CnqO> zfPgA0Dl{}SD=RBpTwEa`Ay80IS65fGw6sV_NdN!;Jv}`D005+Q3U2@a0S`$;K~#9! z)s@Q@gCGnVI4$SQ26wD6+>;^<=ia z)*LCNk$0|6`h?6l(tZwt)oj~aCi85zqoblFm%49)U>^rbtmp$Ie2GYqI@t@xDD?3o znn@i8Vacp3F%%Nnj`HLN6>G5+X+_txz@C|)T}=O0HsJj8Qn3@#K};{+HWWmdX2g_@ zrg55{Oy9Mo&Us%=l%`k}fPv-#axn&TlqaVnzn0o$<&KfE^sOhw-f6j`)0O0I|X0diV|R40_x e{@;DrNqzthkm;DXC4xo(0000VGd00LD>L_t(& zL+zP8jsh_Zh1olB0V*VDsSxy(P|?uPa1IW|{j%$kyvk;rp9%JxC@hkN5Q5~(dv=WZ zS}YdsC%S(SWbe^E;NVne^0cy_^M9KHUgLB+4R>hR?kVTy7}5fi0Yg<7(&}J&C@@k$yT>Fjf}CnQMK#Ac z`P)e=SeGvUTD43eN#an=T6GC>s)7dAm2=WpGo~zUo>Y>8!D4_pm2&S-I*1rd2_!A_ zdcDM8i{^S&2}b$)76^WXi6SEcf2PEnQth*it3g zuFXUGRW{Zjg!v;i*^*-5iYqJ08_V69QsLowPRfqJe3T21zquS_u6pJ`L3vsBDW&&mKGiRj;DJi8@VHOm8=~!1MX6t!V~_D w2iQ0^CmXV53|vRa#+1|Vcp9moQTu)aY?`*MlMlchr~m)}07*qoM6N<$f`i07k^lez diff --git a/wadsrc_bm/brightmaps/doom/CPOSF4.png b/wadsrc_bm/brightmaps/doom/CPOSF4.png index 0174a3b1da432cdbab29994bba4d2539dc7833be..50c3c135d98bb8250ea01db3e4ad6dadd2757f65 100644 GIT binary patch delta 162 zcmX@iw25(oL_G^L0|Uc0_PG{7ia)?7#5FA~&Bn$?U0vPQ))pw{QM4x=NX2=&IEGZ* zN-j9a!pCDGY;sWTh0m4++!wVrMm~AtG*RK>y(Rsw$ByYpG@ZK<5~RVgrGsU^b&{}% zVpKqDgroYJCcPtENu_%@zp~Da?GRnk`qstzQxs?w NgQu&X%Q~loCIIcXIY|Hj delta 308 zcmV-40n7ff0mA~27=H)`0000)DS!(A000SaNLh0L01m?d01m?e$8V@)0002>Nklpw^aZ*37jB26DTL>x3*Ct0$XrB^RYb;=(Hl?R(~MM+mYRKXypXGwkOeSx7;O;sDS1$M4cOW1=IKU{WDHTGv+cUj&b>)O$_9l#m z^WOJb(^Fr!6$_Wa&~V~;cWtX!H9M_H5Z~QN~5ih~JXOeON0000}1|KMkmmuNUr z&GA*X^;d82uYRwy?E&W%ujGro(&zfhziK^$Q?A28y#&K5g_Yl1R{qwwQYW%XZuWz^ cm~V^>?yBzL<}DXb0L^3YboFyt=akR{0Lm{e-T(jq delta 312 zcmV-80muH20mlN67=H)`0001n!y1eL000SaNLh0L01m?d01m?e$8V@)0002_NkloPqs1)$|FD414?KA_EW>XacCx!2Rd2@MlY=X*mqVdz+`5qyu`FgQ`~(qD}q3-}Mm@PhPZ63{jP0000< KMNUMnLSTY<%7%~t diff --git a/wadsrc_bm/brightmaps/doom/CPOSF6.png b/wadsrc_bm/brightmaps/doom/CPOSF6.png index 26b971b3873940a5d9a8ba6fc32afd417a90b728..574378a254c55897b11b52a4b9d6b960c48a70f1 100644 GIT binary patch delta 198 zcmV;%06G8p0@eYL7=Hu<0001El721#000t3e-004POL_t(|+U?RI4uCKW1<*N;!T}Im06eCdC!w_vw)XnW_X`LW2qs^e z-)?I%aP}0d@>Ut<3ht%?CsRDHK-B=P=ye1u<=eVZW2a>%@;`cxm1uFblTmiH$_5h3 zV4z#8&FJS9?E_2vM*aFwj}EDF4|PSkET6jD1x-#8G}eN~%>V!Z07*qoM6N<$g1mQ7 ARR910 delta 362 zcmV-w0hRvN0r>)u7=H)`0001yyki{z000SaNLh0L01m?d01m?e$8V@)0003iNklH)7x zYqz)_@;D9-32RLnq$nyb&zc(7_6>cI6drS8-MMC1i;BQ*b6xcYLLU- zW}@WsCd)aH1+^ct?@RTN09eO^O<9xbAzMHMR>mZhHl=jU(dbGq2bps&Fe}i5SvkmR z5}qT_#-P;7ZCMbtF3v`Z@5n-W$j(=H|s_(jx%vFN8JRe1> z&{maxyN^&q=pc}jMsT7P;0r6NnvBX>7+-$vSW0#J^4l&CY2`$?-3ZQ3ZX03_LH&ro zn%Xlnb4jmy(7hjVa%(M*BM*m&adK^4aYW1`n%c^1T*7z6rn4+p^|SrPmiP1pT&&o0 zP(dP=Tdv?3IzG*9^#@p0$5~_YVBQS|B?Q#PNnJlXZbu^9Q^BCDs=9NAgH;W2;4|j| X@~**~X8`Ek00000NkvXXu0mjf_PC9U delta 602 zcmV-g0;T=c0_X&g8Gi-<005|lJ01W4010qNS#tmY4#NNd4#NS*Z>VGd00J6GL_t(& zL+zR|u7n^EhJ81(wXm_;$`~6P8ygdk;hnr6`-WsTgn)n!Glob)HoJ=}AOFt)gV&}7+e_z+qPB9vM6}X?0+I&Ku``#W-uH$KEPoZ z)I86+0vwY~`2fL=P1F1=3Ga)sCpqzClD3BmFJn$Q?(sVw zdhWW4SdeiA66hqt9mk?L8wSKKj|GgiG`0gk zjH2dpQ4Ap3+1z`wZq-s1No}9F`;ni6sep;?UqF)MOya0h-t$$Eax^7=OGd%Et`$@U z>&gH!fbl;*V}#<#Y=?kcnYPe17rvLXXz_rUwz!g;Nl;Qo!Ia!h0*qYZ*Vjpghcq$@ z-c%)J{Zo_&%xu>eFvSj++P(_j`@^8TMKiC5vVfWGN&oQWYWR2MMX&yrb)Bq&q2w001peOjJcCCMGX0FIQJrIyyQdBO^{uPBAet zUS3{lX=!0$VMs_wP*70+|NlKbJpcdzHL2#u0002)Nklmt-FX8&DJ+aB2>MrkkVfqG?)oF(b4Z)-~Ly}a% jTPDUXxs+Q}fBuFC@SAnVwTK#f00000NkvXXu0mjfX!ewl delta 604 zcmV-i0;B!l0_g;h7=H)`0000@pUEiz000SaNLh0L01m?d01m?e$8V@)0006TNkl4jiz8%H?*Yg7kKk&JsuB7<9fZWBjd@IwFJNkn19ZvX>#K@?g^j_@a1xG zbPU7rf$RHz{cNY&_Q1E>&0)Y2^m@PF_rjK$bR3e@r1xZpoyy{VTZJm^~{>ey^7v42&X0Zi9Z$A-!_z>=bYC&9r5 zeTEINr1>G6kUe}~Cwgid;8dkUK(Gk+b>eU90Auy6DD(AzQ%~VElTHa*W?v!`uIw`3 z09Myt8i7c+uD+8_s+9J=ufihb6a}MRTFcxOs$+?!6wXYGbrcRYKf*xj0Sf_--5*wd z=?6lxDOFQsnV$*-tBPiTc|d}79JfV%4>tqMe{R_EgHC;gQ+_|iqzidiXpMRb1JK_~ qKJ7AJxzw+x@J{d6YcG|cEAShB5Jw+uY8C+i0000FE;_6L)uatgNg% zJ3D`Wf0vh+zrVkuqoY(*R2dl=9v&WabaY2YM{aIzXlQ6+Vq!QrI2#)qA0HpSzP?II zN{5Gsetv#%aBypDYj$>aR#sLgCnw3t$v!?lBqSu&)zyuSjejpMFF83mA|fJLSy@R* zNjEn)DJdyXP*8b!d2erTadC04udh>6Q<<5WF)=ZQhK5&HS6*ITU0q#5LPBI@WIsPY zXJ=V zy=StUlO&J?0)NC{@O@%i(v0Sw|No|Rk7OgsvUS+X)>fqpglrf6)YnY+xMiD{)-T41 z8Mn84KDc}M8FoCJgyCgg2SEVW$>!zc1Pt$=48-Tw_ubt9FIU;mYcPDzDSVCy0(={S zo5mYETc?f(GJ=4FRtXWk)3x36+Kjh*KM4b}y+7}E&wuMP4ng#LRZ%BZN!*b=Y{AsG44 z1L0eQR+cN&9x*s2)|eW3#WK&vJ#S+UAguDi4v&W5tB^uBQHQZXFtLq|2*$ytzXFaI zcuoJq$RCtR$!)TcwhoLBwFGl zWq%C)zh=Xa*AW)4^acq$VYC*lRK3r;Kj~a)9!t`(|h|40{YC zA^PIe@9=vuc)yY|4^e^f#tjDGj3yd-E|QFv#d=!>={zC_ZddVBe5xWE`N{n zs%4i@mGM@`aevw}Y!RW7ubvGV#^&sn%5e<4VMK=L_xAREo*^fiallK&`cmSY;1`FZ zEQ3ewwDzE9)0Dsu6(R_DXimq#W)9j?&6On@S`R^~>#$9}9O)})c@>NwKFk~39Fk+= z0X?q*fGL(LjGVzGv-%z8l(IvlMt@DMw`z$=Z0>t0t#U?eAll7kDILB&pxzl$$Y4&r zcEcGEluoPE*7q}e-wvkOSIl_|PcI13I_iB=3?VX4p4~{w!~=#`H(2Uy$bEQA0Ye-x z#FUMBHhp7+!Fd)KX>ZkFa3OX|L`Mi87+5T3e?&^)E1fn(DwhCQl7B>I%kDWS z_@Y&e5Ddp*sZxeWDYXj_++q`-t?gv%P{e@g4;T^$@{GIm^YOr*<#1CV1q>CU84_c= z=ge5|W0dZF2nNF;$bn;y!`om1L;H+LL`bst=v$*Dsy+k$S)${$+Rs3Ukvq*{X+}f< zHG~{A7cuGHuz<11Rg$MV1%I)2j~O|P#j^aCm`XNNp0RwfiG{Yh5~TDQ^0fbA`|p}G z^W8+R-`_qZ(Ty8+)Xgnqh60E&cN^+z%ON@f9QEy8AS4!2ioVsgMB=GJU~YC zcKmdY4-b!DPS5yzxn8etM(v7-^1gD2Uatof$S#K{=PK=1&XSCOmw&yRo15OchoO0R zENzBT`ys6?VmhDq`}29vLCPR>yfE*Fw6crUdVck(?I65eDyJd_Z0cng3y|e>{S|IM z%>lyN6~YaWQg(d11T(%~xk3R*yHbMca`E_f8Jl5e%lVw!Q^^hk&FBDww;N(?%;@lW zzw!)M&R5fj(Yasnd4Dza8oNg99?LM$RkPW$lBe+d7DccTBZc2VE~nGw8qMg;Sv51D zIv6&Tz#v78^#T^e*?osq%v@gDX;@-aQ6B8{U7%;W=|FVU9Eb-%hI9nT~OeFb_EvRurr>M#y;TSSpR7fD1xRCr$Poym&TOc;j!uR#z*P!K@`K?K1G6hsuog`zktbRmL* z-~ghy(Vebbi)&p9-hr;Y058I;$nO(;o43nKQkCJP(tQquoX%MFKJ^V%shs|A$BrG< zpJn|iL;+x|mohY1gD9vmu3@ZUw77+X2M<;(Q;Kxpz=7(>kt37dqeuC-w`CLtqzo8$ z@7^8MN*_FUP-*cN`Sj`2&g-Te)1#Gl-Zyg1`C$O!O z4`Ym06~d6qI(znPB_O9xovN-~yH+nv_uRjKf4z_Oo;`cU5IE-=tB0|7@7|mO_C6K} zKmkY!aKs0KKx_*b2(wBchYuf4f{_Aj;$v2C8537%DZ9(YmNA^;|hneJQ#prq2CaSd+gY;dK(}B6Fa70 zAj^Y+m~A4H%Cdi9AnGTsIRR!{9*g^<1Ic>g7_>|n>RnIWm;^$@E8JIuk`sVg9t?p% zpebPB-VmC&q}W4-#=8GUTRMKYaLb!}@X?OULnLyD2ZcT$Kk?_In4=#m^0}N{Jl?fB*{s9!v=Y zW#Rg%!}E#I%7lR+xB$eEzzze;&_g110tH6Lh4G{7Z|>zFa7DVe+!BBLbpSG=tC|l+ z({t)rE0RA~>y$!%5P~gXfx@B-JrM0ZRzoG3)0=5*4E)d$E5@R&BM5`ts zcg>qpPaMn8Fo?1#o$K3uM7OQs04Skhm1P-Se&0cOItmzx6+*%ddav9wu_l2uMuph7 zZr!S_;^oVi_4>(^C)J}zkLvI2V{un2;d@v}IWTf8BJ3CNw-0q7zKelaScY2&t|*LG z2#e5cM|2i}c>DHk#e!wLc=2N6`h4L$z{Wla79p!C2S!Gr8%vUQ&>Tpr5FrQ$;5}}x4WuprE_?_|78I$+ za$s}?NDq>2EftrBh3IqW(4qP(uM6OwTztN>T?@y$RkWH!WM>PAt@8}Lsr7Za5A7ES z7hd0Y@7`TchGs2rxeyXu)YgTOBNmpjZ{NP^#EBEt`Sa)Nu`m728YmYpUfjrd~)WT~mx5YN@-RG02bGa~-c1nCz=;+a- zH5}ep4J)a*(MK@3AoVL0FVvst>!Q4j9@iEVz&-y*1TVN?Xx-H6`hal|%9vysxUV6f z1nVf*GQ98(BnCrPqIUpYw5HBjlduw>{pw-MI&xry_gy>;L5RW7I1+0C3SY?VlVx#^ z(pSOAf#8`_VivWG9=IUv+_`hZRk?c#tt5ouaV6Dl#X5AUoxBi4f;E&sgsw^mhKn4t z5=2*^cL4tOs=JL`*Cm(LWg(g3hY3izF(VAzs;G+{vyw2&3bU~=yXg`*W&c13<-4gS z7`!R#qqebt?vUDG^s$gIIT#5<`91Jl>u3t}u{1a}hO}A6h#<;`(Nx*c6~(Lr>tPs! zYY=nvJ$B6$R}?#D4WnTQ$K8>h_UQqV9xl0fK62X^Mn{i22PaSrm&_xWL{4E_eHwh4&Ggsw8T ziGmgQwUmAN)X)@B$Fh!~2VV{h1@JDh+xE9Tr`GU?TU7`nXNV*$M+(G_tZIH3{=)kZ z5bATpfJkInhVG*`YIEa8(4uRNb7M!OuJgih5PBmud{Hrs4_&|_UBkApY zF#P3thv9RkhP-3l1RxYq;J8=rrwd5f`rW-11A~g|R}MlDVSvqbQ#k^KmN5m4Fut^u zZyw9gYhNBGfK^Ol_eVmB?xceSu6>9=q-^7|)+SuP$A>)CeQ2IDe=7%)|;R zo&|749P%!-^ zftVr2^@rgg6gyZu6wk6^AWC&n{%~rA*U9)-||&%bfzS80e7Kna16 z`YMK$=9-#d_;c~_!Pi7qk_H3ueZf6p6~K7^{{5zG0VsmO8Ve!qZOgJIs$6OovuR?R z%HmG|i-JJ`A3uIvy?OJdhN27#5LiVhrYuHKN(Rx^5CmjJK|IYe97dZ}aNM_V-)dL_ zk^6rv=C+a`JiXcZqmeAooTW5tY>Om>6x00aoiux75{U=S>(Ur=2|H7?Av*R_>abysAKjh+_KE(h2{d@KE=g%6#@87@cHQSeH6@^%6 zyJaX2XpQ)>MIb(Z{yfN46e7x8b!dj6fj`_4BGKX*f#q*pR%j{o&e@kTIbkWHhuM|S z3!b1?-W@Ys+z^PMU^!%0TMdRcnib5qN{0~x0St{ZNMOaulpGJdYYBwpDgQO0s=r}8Kk{y82NgxVHEV6 zDZOEYRw2uv!{<|S1_rV%mjop0G03d8t7uczdkrJ8)lLOq^7` zK|w)pZ*N#wSSKeZXJ=<*WMoE0Mp02wLPA1JOiW!}T|YlRIyyQuG&C$MEdT%i00024 z1awaT00xgqM1Mh4c-rlnXLs8=42FGY_ioxWdpq%pt>N!G9imDnG8 z&V_s8REZx!FF+7r+l%&Uz5nsQ4c-!l{k?kcf5gkf^-l0s_2uPqY5DuZi)YvYeqGPU zyTA`Qe_H*+qoX7KcKCwOt|3)Ezl`y& zmKC7=*g$>cBHxA*0heMQUamc3l(=AoMFw(x!?49e?E)X9Hvj{AhR0+V;snL@ip;=; zECn>#1wKic3~#sr#x>}!cq$BTLDQ9rd^d<1=zq@%Ex$qX7{TUoi0e4Js+a*r2=g7+ zi##vc1RTfgf)A@bcU{V|!VuYJtc+_qCX&U6VT0?4^I71+Qo>L+gZQ zxXL&f+lVypwc(btVL}gEB4BP}Y2O63_!w58C&vGFb#-+Y+v}_Mwx-$kMOOGl9WzaW zP=75x%EOBI@IB9SAZpqPTQ1p&ma=`(ePdB4Nps$p@HzTWoeqb?6B@(WL`$_#>K86; zzK!@WPRAVn+P;3JuYk|ts=6r!M$#^OzTJv1NF8SGUuzd2imIx^A>cE3WEw`ifT(F= zPaYo@yoV@WyoSK`-%)F@)FWNpG-L^#!hfdNHvjYrnG$aBoPGg8h1lxWbgApQ7U#N& zNvHlk?|x2qz-x3gT3B>Jtr$;v$3Z$(r(Q8xGSjrtXr!u>3GfB})jL8U6$#yZ_DTFn zzI4QoMiZ4A@E0o!@TwLuLRjvzPvX_c@0F81rHA^2^C$XZ1!n0Z{r$V*M<2uu)_;xs z-d46Jc%8#_odSDEIew(|OXK=ev`+ag-f!_i3{_tYR=g}&fS)WDW!L$*4(z8RndxN9 ze-MSU)ycvde6uEnPYu?e0K9&Z9)7mfpF+Wb2UyT=tJT2L^Ukja@ai&KmD$(RDT8cT zfb`aM3i>cTza^50@I8yS3~oEVB7ZM=BgO`+e3L0mQk+e_snDM-=wdL)u+hU)=b}&(TQIuXusNR4=bi-C=u_%)pe7uy zd7iVlB(rB`PBlKAbP~EtCB5G+$W?si#ojXEb+KWSNz95c7rO75&)7qJlYe6UY!zp7 zqbS;@^Jci465+;m6V?&r#+=g~Cn z^Z10%f=Yfb*V9M2%&g$2^nWbdX_8OBV)t1CzF2$mzcQbv<+7|YEAv^~@UxAdD?W!; zDf%nnExxerQy;H(@GN*Q=&5}+B>zRp!$J3<|MR^vlRPdAB+nEKLw`}Uly1zTZq|l3 z6wOd>Z)2SvHR)q)DusVkV)E_dEyw>Ydt_oJ{1*HsQOuq~Yba37WdWT97@4MNy z(rcE+S5CXu87fzE{yG=*@mcf?e9(gT-@fhleP#TmD?J_+NfUlHQ;v^6-1Wzoe~vpJ z01G^B!hels%7^32-sP7so#{v%TcajCtkK!yhl8&N$H(7qyMMq57*re=p%(3DkB{T8 z-wPY9gzLOZ2iFm9%beMna`(rb-;ItjG@_AfX6J3a>iyL?_2=9=M%4`hArK+W-p`Ej z!R5aP-}_zUNsa>YM*?-?oPze3<9k1PnKgwL@Lkm5lPDpScENYz2U~{@b=q@rjzOHc z+Qt1q68|it!7iThqp(!r(QbG+_PS|LyHR+GTjXPCUeD00000NkvXX Hu0mjfA70m> literal 3241 zcmV;a3|8}rP)pRTS-JgRCr$Po!!b0UmM3gCtyb@DM}&&cq@&Dbmn)%JFSu^vwuJv8lTC-;E z|7_T>q4}Lwe-jV@{3?ylU_4wVpcQ?Lt_Uzf_`t|EmLd`k% za>0ir0_gAFy=ySQ&Ye4(9Xob3Teoh_fiY&D>A~Z*wR9HnN}Oj#HR10DZw;!NP|L`L zCa^*^U6Q@s~UgPN@iRxp_3db%Nyf2CY`o)VEvt_CR(A;w`7rcc=n*elDN&Db} zr=38el=_slxTt0dFFbbaSPPqmnws?T<;ynRzJ0rwAtU$lcX)hXV*#7+RNz^!41YP` z8RQ~$8E--N@m`zQruN{-kt2mrEt!4LpejbJI@2M-=>_U+r(rkEVLwSnKtui7%O*DoJ@45&4R;IhqnyJ`^)EQ#_k zrVIaH2j10X3mW+lL{~~&gHR5d&C>~mH!SchflgU!A1Z@(Yv^*oTj{Xt))0zb zVm(ncr~*&kk)hA)VRxzb+WY{w28h)PH(%(yYNQp;_PIXKvCHxJoEw*PC@x1v+-I@+ zt(ZSlE;T~XD?CQ53%>dNmjkRc4<4>?QR6W=WhgEubOdH7YN2fQ1D<7tr&_~;2Xrmi zG2M3I6Fw?x;W%}OSlUs{5K!sAWGTm}g?Z|Mml4J?hK3AW02*`AV3^>Ozhayouj$r1 z`v9_#xpCiLp+^iVQId_+HBcKENbA~9NR|I*e+*b|AzsR!jc$1l9z1B2o0`icy2syAqpyU zWzcE8RKPmhls5+39oQ0VJ6dV)M=|`!vg4P9T<|5(nTmbp%$ar;FzZSH#TY>K0SEfY zlPBB#F$Yk`C^VvIM;yC6x6M_6%zSC^z9>c8O29L}QIkwBU%uQNKYqN~zI}UZ0KvB& z4sZ-bO5Kb`7-O&65VvaxcyjQ$;OvdNhKQ8`Y&;cdu=*^ZEamxly};)J=ep2nfe&?b zC|g?Y>r|x-JNW)w@IKf%?SpLyfv<*Ajq1I7_nPzP&$m*;O9^}S>}las8z`S;$m88v zUoq&EZyyaRwjUg5{_~AIsR@OhF~hmg<@NG>+sFbK@aVt5vyJ3xyN$~=n_%RIF)4i+ z2F(Xws_T*&Km#3sz&?8PXqyfnKHS1)8|(7=5P0^x(=ZkYeEl@-64}YkE3X%9meOw` z6_SO|x(l9$7&%hdXTw6arG|p#eH%A!{Nt&1-M2r(WBYpj|A!26VV2|*Z~)r|)dwH1 zC&~f7WJVe*em-;}$kX;DzHZ=jd@v zd{{UIH)p?<5*Ic`5n#pfyLRnrb)M~AZVbZ$miJ{XVC(5!${qNaF!&s(IeCc9?f49^ z<^2jHfi2kqoTR4I+kq?aS`G}XlQVd(!4EzMXlkfHz==L=lp@%+Ayr=02*B&O5^$PH z#xV*tZS=!%uMIy3o&j89l=Y2kU+6mCoz=x}={vDH>jJKLUOLZ`#x}>}?RoUb&n~r} zC7><%+&(~w@iX(*jpMBmfemPSw6-?5Jy~fy;7dO0U;(@9nJ`K`wr}45g7^LDVuz{$ zDhKF*k>s$%`$;iC7AEg@~ z@l=DU552i@xNkVYojF{3?t$~yiE2{v)b}Q0qmm)$Zo}I58hRdIHdZr4k5)!0y>Ni? zFe5ZL#&+MVvaYkvO0Tale_1#gYj8^&`2Sz^3+FnMp~GZ`yS!_|h@^ilE~!Cge7X zv3xo3t$}?v3v^AR*3vfwkOHMfc!nLSe2veM8ja`s;HMy2Y4Cwp!&>6SdfQJrZ$rvd zIjQ}*_Y=sMKV;Aayziv0aoju_X91e~=5{Fz>*<27filTftjyB$6&NEAyjx(dBo$o9 z9}TnvTz$dY^ULeqccx^VkONeLzrJyG8(94=f!_tV3rV1)@v(+gd1L4GftUCD;L8EX zQvrTDoGb<37rNDYtIe^Fle9c_Zj2X;miF%5yC(XuwQpefaqzJl$%$-IAhiF&g$oVI zSKUKW;MS0wbA`O#dVUK2KnCdkD$Y>g0eki)67L>vOI@31n)D2Z{2DJwAu3$}aAHEAd;C;gnrS})f-w%9W_}8vo3wnE{ zM(vLg0PA+F^n2FeL!G4(uaLyJ`ko^-99wcOUtZw*Qr~hpKJfx`e~;HiraDHxo8b)& zU!xkk@f$a8v@ase=pU`Da{zt3DDo29aNvjVl2jQ$fAQi)D^Hg$UFz-esq=N+&y428 zn}gGW*DExDB*k7Z6LgN5GxSVjO$Q!;g01yeuU<8zkSyK5e?R&5U*?Thtt}ak8K(gc zK%j>pvzh~6fle$Qg7WCmqgi*3+!<*abaTHDGAr-cyyYCc7Oc%u}!aEziz*<2Yi5k|Ng!C@#9Ck-qp+Ia-99BsJ*6)Pj3wi z{@b^2Elj}&I`D!0>({S#iKmn10KBJ4k|NQxLgHc$j zCVcqtAvi4D6~-9H+0P}iZYS94|Bj3f-rl&t10PudJg~bwXB`7*&Ntx6b@R1mFoksQN&!6o&azli(G|BNze$xGhCPqcz z*8$H7@p$xI8V`I7K`(Zxax-AftOwq0TrK$0NlXLYa3{&KL%&v9G&bMi`lbWFMEGcX z$tM>_mm0j?l^WuAD>Z=6`CxMgZ|daLsq@&m^&JPs>~+=i_|%OoZNGh+WCcF|Bg(yN zfxvfp-`3+~E052qc~)lBTg`aN6f^b;dP+va+4qTs31O5=09ymFqE$5#gI bm79M6-1DXtZYrr&00000NkvXXu0mjf;AwXi diff --git a/wadsrc_bm/brightmaps/doom/CYBRF3.png b/wadsrc_bm/brightmaps/doom/CYBRF3.png index 33cf9755c0bb58bffea9718edb4834c490a8b3cf..ea6ccde8e46c7e13ee5696d2fd1639a0d01a4f47 100644 GIT binary patch delta 1826 zcmV+-2i^F{7^V)88Gi!+001=iOjrN_0Hsh&R7H-Cj*^m+b#-;h%F0()S9f=JUteEY zSy`^GuD`#(q@<)_VPT%0o=QqeYHDf@4i1->m#wX>M@L6(ZEbRLa%gC1BqSs+FE0@h z5pZyDF)=ZOgoG0l6E`J1*?%nAedjn>YdliSUTG)Sm~D>`Eu(Cejla&s1=?9T>o6WY!-%J)lkI?2Q|r_vC6L6n)jAtZRzg!u1bj8Frdc+QETs?|xD*VQ8I6$J!pImAD-(=yNsG?% zcKbycDSrtZA}Uu<$aZp{4ErjL6(xC2l!84CDF!lAedO(zXB$%m#RFU+7=hhII>I0F z_G>{CoGYa)%oXEg4_2y8-kQnYe#IsnAyz5s2x3j~Pj+kEPS0i`G>!XZ5hF@j8Qlql ziH4{rdWSZ`Gxcglm>kq&RF+Gg^OJ<&UFB`fL4U-ao}OUpRQw+D{&lZTW({;T+x2wC zf@+&oTDgdpm%f%8dK9UQzhG0KhV_LeBn7>d+(YA${s4FbZvmM6an#FM$ZcYp* zWP~&8np3g-1dTLf{b#!X4Lgt_*foqL#7xEF7GlY`=8l74*UEQGF-rIH zr_jEi_(1!H4m(!Q4?2yDHFOIE;*C@4Qg;Dz9x&lxagfCaRjYuH>;ve>hW5j><0)B< z2w@106k!;rsiq4mez{hLEN#gqaEtpSeSfFl!j7k8Iq+f?2~~s;u=!T^>hkpR;1+wNGZGB!zy{Q!pNR6aLyAO^ui63gX&Pb(k7=XI)BSL z%I^yeth-vR7A{wwXUd;@t3uuFRTa#6YPPhZMizZ?8>ai=!GsN)+PlB^lO*9TD~ef? zrX31YwUf*hDa!=wQAuDttXxqfZ*Nvj=4p}KE|)5O>r@!+HAF#$$th#3ZC7pgN3am; z?JZfZRhW35k;6FYT8}xLO-yl+bAQ@a!?s7`Ut2lh>XUk&KcA<4o^Xw93XalkuQiin z7^YhS3ofv8-E(lEdfm78(rhK4mMRRB-sku^8ZSwAQ>b&Y{mlc+` zOfWO$K=Zara@L#N!ArY+FMkp?(ah&)md|?`JEnEhRqXiP^P+UG7uO5PbJH=Xd^pv6-OlCZ;CtQeOtfU!*>co)8y--OYJxV$ml{;o zFW+lmxz~OSv8zt~V=SXYqP;81NIxG&rOdA)Gf+VmbU69HnvhWjGJo9shmxbZDq>_} z;DO|5+A!O>qWe4gg7V$8ew|G}KTstJ^mNmhW2*gp?jNhE9@A#H)6Jmoc-~C!?(V*| z!9&FS@c8o~xO=&|xpCcEmJcC6KmPOc@vm>wY4H3Ko!Zcl)aKFT;F!F?Y(M}0{oM3p zGdWzhbIC6+LweQATz?>h@;pD$WSuQ}00009a7bBm000id z000id0mpBsWB>pQ`bk7VRCr$Pox92uO&3P}Z$J=15WzqM5d;wg!9*}n5ECO66cGeP z5JeC%Ff%qVF*7pNTQK*g=KEIjt@q!SeW*T8&rH40Oi#aksIFDdu3fth^S@)qj&1(k z%HN*n0FcYJ-+52~A3z~5kPGDfHe-iUO&y++PoF-01o!;;^BcCaXU}#5|M20%hVA+D z=SSzp{S{k;!-EVAKsUk80ls3Ob%pgXAqxr6H*emoHjdr9cW?9T*|W{#$B#FkK7Fb_ zcMNEOTn7xmj~+ex12kqC$b4SAc5QRv!iCMbbLVyy_+cwKLI<`c97VeU`SRtY-H)KDr-2S^>g(vO=M7LQ7FodpaG0&j-c&-2K0>^ zH;y(<_KNzVOPyR-j{=!ZuNsI3KsXkU3pn0~V?bUB=mI%~Y&|SYz8lKSjdXD>pjYCE z703|;Krzc~>;p^495d>*Lvia83`qe;$0|a`bXzY+HKSHMYzu}a+JaG(ydsATC;)o> z`gOGdl4Eu;YI@_KKh1eG%j^T3d#wm`G1sURD2}U+knykqp4P8%3>dI!VyRlb3%V$I z1xf};3fXors;z^px~-oz>nUhG@D+e=R`TY*zZE2MHZK*$0U1z2i;#_U@ku@yAc^PJ zL0(y&wVUgG2MsI$VvYf54-@;gl?{%h&R0}2Dz+f33~;k|4crwbcJee?-wN8mhQJJn zSnuj#+r;Ag$^mynR>}Ja-MSU^s@hb&deGF({12dpJBI*{_g_#%f12wEGXmZ>*g93V zZ==%`Ge3nt)+FheJb$I#-(K6mqKQsZ_^v^ytSHm>y~R zKjbFmrpJ5Sd~h8KR5F3V>**%wp@1Fb(G;3=fPf}?>h}fmT=#BBYk$ad)z7Mj(ti8) z?V~N4IL>i)VHf0~qbuizteO`DU2m>)R6RurZv*T9fLb?gv%}Pw>d<=#It_~syD3G5iL|#Gnmiq-l&@g-wNE%zF*F*23N7-4D5E%{9xJYn1CI%sY4P zRI~gAA7=*EdA^o@y<<8R037>&>%3M6$h)x15XkAQ#givb4t6Xjf|vXJ`5T+=|3s0obhGaeoETen09}(4!*SKFIp@3W04R5H-Wa3BLaDh0?6YUk5#KsywSC!@$5FV$$3)*15zH@{095NfM>h&r6V3T#HfGvSKu5^b!Sw5D zo;9 z69Gr{rkMy-uH_mabB_oblMQSj#{un4FlP2ps{*K+0n2`!N(D_Jh-qa_5sod9&Loez zhyQe$?w>z@R;HEu5p$gbm_+chKm{^@0S$B$=s3?K*SJvDAF~ z?p>B+!v9YL*%6F&^>3QkI2(a|zydpk%ykZk%NZ1Bn{&d}-#UjA|CP9S}n@VhPYHnDj;O8O1h%jze{G3ha<~ zZ@Ff?l7Sn+qDr8<3D0IrTo5ffRa{3?3g=1(C3Y)L{NkgUH4!FtZ8X)knH_DSMbawe z#Lg#v*Kyvp3a4KF`t@tIrK*l?X8_}Tuu<}b$R~vW(17k9AiU`RCK!qt&-wK|}%)jkcnqiJS{j4|KGoVzc!2+hwPwC0lOZhptl$i8l>tJavG@d`_>nd=>xIrTunCK zV+3nd2)GgKiWeNshA!y7;nkUyn-{xoN<p4JwJmk4EX(uO8 zU(Td3020Sv7swN|jZx@QkZmHl)DxeA7Vw1grw&`mI9xWBa_Y+|&e0SD)}AXnAL0<7 z31?9pHe>(;TBnOozR%SzP8Hk)88sIrPhg%^WdyCVJ;wKU@7`5vk4_gJ_Yf~TMzhkD zf#RgN0(nBffUbi)g;`GT5r^aQKbaj!!a}H$y0000kaw zcPcy2)l&Jeu5?ao9k%~UIZK@AO1A|2tgE#X2#0pHG)mym;+H~*aIQ0eF6G&UbaMfK zD%M8Hxd@hL&C%7CO0rbkxiGGja1hrvVCV_rO2!O#;C~l-Hltb2j35`R4d^n?8RU#| ztHB0yiM0SG&yAOQfrv-CiZh28Qy4KI9L#0RQj;gwn+4!;uC7?CFl-uOke`PXv!bgV zji_~3x8A`vu3pb|G`8MaBjqRCxUinUOF{^AGfs;f-&@^{eqX!`IK> zBu$!DsTkbhB*BlxF#;L zf`4wPWM$RswOK*K3AE2nSi{9k7vmMq6a{9Qc8yToBec5Eor#Mz9Q(!uwwLEOS0qW6 z=GVKST5p~^F2i7_?eaG5aLa#+JUOMbkWh}&G>w`;jf&!aqqM?KuN${`!6oStGR`ki z6kQ5@=!^97pf*_Abj{meHOmOuQ;}s(pMRi!PI2-0>a`w)!wmPjeBqd6QFOjs&Sx+X zMPFPa(S|@A4hz(cq)5}k+BAaP4R#@e{_pTZsl&9o(Jqr!ZZ#DO-R+GJTt}`JhY4=k zadE0>hFhmWQ@-yCU%Ai2J=ga>|6T^}yjE(;9m}TI20y8(zT>TIb?7dFEIq zaYk3sPjNd{`pb2Dn^$;rq5~?z&MPsgFZAItKWrOVDmBx6iuKZJ$9V-i5Gt7Ka5q%7 zLR?TFpW1dj%JVo^VMDTt5sXKR1%KR;*TPD%+I{-iKRoY`R?+1Iiz?u`?(Y`tgMce| za1XKE{qy~R4Z=)@bqOZYDizlC#>of0d0p3AX=}r>JA2I)T`U~b4ju3R#kwxNKDw6o ztA<(Gtpgjbq_yLHq(A^S$Bz~6fk(iva)-^=1aKKw7%*cdtc$G6A-u~K%YSJN+pu}Y z=3wf(7YOQN|0VEx9bHc}^ZwV`E`~c`A;3D~stWfRb`e*x{%VFGbwkOg*=1bQuhv0a zft&YE$N^l(n80v(yHbrw^Bh+@H1WZ3+wZ82VrRIT31WHommFXmJI5uKb0Kd#tM>b) ztGQqqs>nj~E-e=4|Nh;5tak2#m5ebdJ=}T%y6?X}R&_zjURuHb{Qky&tsF1CA#F@| zJb!-Q@s;xol}f)H)OzQA_xEn?c*%vqetEasy}y5atmlG}X5OzRZI%l6>m%pPVM#W2oJ zioAF4Uc(k4vhVio+pB)Y359aqX8GyUrTA+bFIVbG z*8x|+&!0a(c*X+b^VY3fe*u5?>{;{T#fw9_n>TOPsM_^6h1yh!nPz+Z`0-(*sl;~o z?%jju{PN|?=HthYTWB8Dquil&-;*a#4oXGgd`HDYk6l^1?ZU+g`~3NHtzhdk%QfFi|t2L_AECY~c5gS0=a;mOue;sgv$+l65U4Fy5;i}LbY%9lg!d0P| z-47o=9L%qPD>JQAy!rLP)hVKVien^P>(>aEeQq0szCO4>1S~;_2sf>7RrL;cCBtoq z2LsE|710}xqb&?E5@sa*235PRb}q^kmT>uvphP;1#1l5i5ng8R$9F*SGj@(sBra+BV2oT?Csg0Lf03>04QLE3q%|$ zK}U#o((iyTiFKLEO~L9k0oYL#fVMBz00!xHz!yxH377V(ipATF!0fJz3I=)vyqo4f zvo}jC>9eEze7})Q!x8?xM!%F1Y^uhzb0OVt+qpvX{0A<7eoh4pSb(}YT|>C`#~Rq@x?QHYw19P)S^Ii5)v0N( z6!6~;RfOyJl1vt^b9RJl)CrJAmsE0}b2Oxmja`6U_nHDu(+PNpqgIk1Gp7Adxn1gw zzvpfgwK4&kjAE91oMZ)1W5s+I2(bIks~V$271Iv^Yx=8KuO23bv2|Tj$hl`-!T!r| z+f+N$Oaqn)SinkW&yit*)dZ)%bty);b`GRqOEOuw6u>^fCFfjl+W*XQ4s6aullj!E z5v;1>cRXYjONQ`2o$t|0F3G6A;6&GXbnXDV0l?kfau@8jj3d*46c-pV^8xzx>(>L! z2!!u8iAw6ZA$4#UT&fDz%wYcp0GBJ*I@k;(!!)r#rk;&W8b?q7vP7ArO$);qwZ?05 zuW=0x0W){E^xjsGejHo?lTii;$QK0yckQ(yqo`wr z8%>)5{x;9HO@X=Q&avR4T1+vY5wKt#qg7HU7o2u@uaKZoDDYfNgiIpC()FM;&27=3 zwsvY?ljm5?7NJ@Yb?D->lU%!d!7x*cH4JW4u%#Qo1_Yvc_ThomHefvMrHR3bu6p%Y zaJjb_>lSXDP_`m?xvHw%EYqwZGstk)ThzqS?8zXYe2wQ zvVjbO8GscapZqT@Khre8H{Dopx#JwsA<&A!_K%_Jicn$3B3PUg9V!4j^N!!s`ZWH} zex0(kS(1Y^c7K}>z`3TZ8#(q~&Tw42S?-H78^A}gMtoE$-F>K!%?^8m7<}f;@u?r! zMa81yM%@eC3IUG~VgMG!T9@)&v&V0MBonEdQFE^2;nZLaXWA>}rz0wzL3ob)IMX%C zy8HyxW^Q9=xcm97@9#_xdC_FhUvO9t{|nb%t%?-Ok^1UTNq#&k0z# zh8f%7YNpS5UnjcOIJn$QPLV_5p3_|0Tr&zqVN|(1Q)LUJl$&KsSq|=ybnzS~D!msb z*>I>MLb#r$*yRFajQP~}bB6PmD>vTM&<_2aiqG(%7t1ZgH8N5&&3yp4l3}iw;r5x^ z6u?mB=1h1r%X~i-Xw(@UL9gTdq*GmYw)2{q@$*z_oLjvvl%iK3Gu^s(HV{Xg)e)W{ zKN6$ufpehx6qwet=W?io>(rGPdW>Fk`n)S%zPBB2t^^N(drA|VyT7*mI#h99QaVjE zdd_;(u2MJVnlm4h?-lC0!6ATWez?EtZ)3|{fL)vnpqqueMz9n#;OPwun5I!wuz(Fu zV$N4;n#U=3t#APrhyTR&HUu0)lzg{ie}%(u#=#xZyjhw_=6(Q#3gftH*Vl`Ft=h7hL~8tYlTV${kxtyfd^ZW?YH{Fs{g z=Qv)|oT|4C@Ra*G#texm*Z=p}>B?Q(Ia2jH3C&IeM@LS5t{v`7g)0J1C#dOQO>O?{ zaA$&xDp9OWqp2$GI(}xr+cV1%1S&PK!S8J_NTcIbS_ryETb7<@WcnR$3}o0PXl81@ zM`rH96Q6Nx^tR!qnIGW-#U&l4z%5xdV9Iq`j@{vQsVla5r)2uB+!-CKE^xsG9AbNiDbBbzbK6Z!O1^}=-+y$D`W2gnNQ-c`Z$L>%qsF1iuC2*s9s(i?iHa4fuf!QLRw`Xs?FgA)(qOP z+g7;|ax}XUE-~XbZ{92!a=mb!IGw(0p|p-P{rB(Rn_s_v9bSNi@&UVW(_Amy^aaem z(ls4;J`|8}G0DK)&GJm`+NjzH)$j4Q99)$P)NkLu9qQX%xif*anG)X z-KcZfQ1C)kT%TD6hA*uu2}a};yu$r;!abI1+dSqKL9P?-n7XwO+7z=sV_~Qdu63?n paf$OZBZyNptLlVX8KhC*{{hLC1p+!sh~EGJ002ovPDHLkV1mAzN_YSO diff --git a/wadsrc_bm/brightmaps/doom/CYBRF5.png b/wadsrc_bm/brightmaps/doom/CYBRF5.png index 8324edb21729554000c44a5805f61e5035678a19..959741072ebda05d408442289f57dd45becefca4 100644 GIT binary patch delta 1003 zcmV|TU4(>$MMXtqWMm{HBu-9F8yg!D5fLdVDJLfnjEC2ui;~XKj0009# zNkl(lx!o^~{-R2-M!3r_ zL~qxv0j80?W`8^93VwXNulk>ARe-ATEQGzaTC{7k94!Gq7iQ07>! zYY}Nh3Jl-n2II+1Jh^Ei$;d?$O%@`C+_VH^cp0dG7!p=wQZYgWDP(M;be%w=F@g+~ zVd`hZ;1z^2@YKG`Two-*=8B9VE68(&#vt~!pH&)L5r1-aDirN^d7(mepC6xqBVAPt!7<`_T&S(;(Y}l9O9MOL^FwuP_I{&O0dSzOEZ8 zO_{(p_kU~&)a;*cbO*P-+a=KL%qi@2b57Gm&svx|r=**8y68E}Cr?1z=PmzU`k`D# zk7X(HjAKLftIK6kk7Oy!EXgCtQi?jy7-H$4jVqlDVM+#LOIJC)EE(fL;NeW6kOeDw z5Ll7>Wr+pbhmB=UGP7FQon?W7>0!sI`6vt4Xnz(ul&)cqD%i_8Jh-yo?sUWQ1CpzH ziMdNg&POtS%Z^cxWh9x6Ll*KTqi3GmPHrA@agp7E9;Lm45KOwV*azZ9XDtVuNb zEq{B72W&?tJ}~`t?VGd00!nsL_t(| zUhSRDO6*D$g}pBzh#(vU2O@~zL~-Oq5L6Hm#DPv6Df$e~9r_r)XU<)H?>c99b){<8 zUs6d|Lr6EBq-uY)YSm9F-T!QCY;^x_?NyinAh)e7g9dm2k$*MF4RYCPJd{l~B{wBE zCAV6jtOep3@-j@9%eSZ*Se_ z=jT$!wzs#ty}iBe@bGZ4p&&LlHy7L4*;(!&7T1@XvKnX4M^CzGkPi+H7LdQbz7}eR zvI&-O&4e#5E`N#z2hiu|=iS}iU59k=z5?Lskm1J?^wH7L0=6xd3os#=;7gElwd2ck#xvDl>qKofPHxz==*Yz8{9u^%RpXp<(jsb7jjdt zm1i7e!j${#>+4Y(wt;*#B{n@|0&Ot$%y@gxTSA0eNq@)Qo8JnR?JpHTZh-xq9EIyX z86&jgbb}pb8#>6a4D>4C%?)%g=y1pY-5SCm(0<5N zF5zMiWzW<mqlNZ6H(nKytA zfgEz|n)R{mB?Z9G&(A@i3Aw>uVcK6ELO~O58*&FgMwQm8Qua97{Idgc_y0@!C4gxb z2%3e_Krey*`=JjqY(oRuRDJ{fhwZ8-6E^k$7k|>r(Avvd^)3ifs#rqCds>DMpzr&& z0=qnr`A^BN7w?L2(ntdfi@-IOL0VY_0W_zO*ob?``%`$LdJd@{wi|^R4o`Xg7 zDONV>O3?iiL?&z>XgP3U;+V;cs9uvfCmFJYL*OpZvKPX0=f2uVIo+V2`IpMZV1xF7 z_t@k;z&Wc|#+Dtj367xAlfe6sF5X-UL4WzwfnLcrFlcS@d^Ps6Hz6nPIa-_xSTha< z=y=Gm55Pp@hR19P8MYxCJlrqDaD1+i+2A|ki-Sym20Q@%`T1FZvQew~ud(SIKo zgab0HVVxl>V)sMtoBjmgww%k)&pJV5dS)MF0G5zT`Lc3;pk_PrmxK#4m)P=me{wf! za_QHEVW&fuKdc1Exn|w~m3PxwcoPNfhl~JiFIt6!48Y1sliuo0xH3&yL)aIWdwgGU z0oWnC)?vuO2v-TgkGTpo1v0_e3V+oEo!goUUN0Upfl$c+odOs^;#z$#o04~X;sbZTSyS#ZeO11?8<;n@69KrTkg=~1u zbYx>UxrWaLjeu?U^ip6Gve}z)UDLc9Qz__hyWns-cR;a^L74fd@HSXDWPc8)Q&ej^ z*Mudzk-oI#$k2D}2;YZ8MnE|lI+wq?eArb$xocOZ0tttVgb7(O&8O;b=73Y7O)&O& z$T`=#<$zrA&^jD49}BJ(Os;%ovI_T)ir5mV9We6@&`(cKDyzx}voF0cC3BU1j*OL7 z++-okMlS_)X~4`|C7{EUoPPuIP=JwFK>q&z9@{pCg*+r+spgSE&mS@sonmi^=Y~N} zZTQAeG63D*-*>OCuVdMU9x}UcDJwL`f;=*49b_&VjGZZy{nx~*m!w80dW7`#kW&HI zGVBHDo12>I@<5=6fm|D4sv3~5u79pdTgQ4srlJdj z)^gKz@IJ?;al?7obyXt>t_j{3>Sq!k4b3$uM z&g0?9uK(N4kk+bQ#Q5)&tpbqY#|15;A9AcBW6#T=n5aH3As0-48M68RA_WtU0#YJB z1xI@JhA-7BrZfPN1y&gJs*+RHGBvwMjaPC0GUOb9a$G}^@hNKTMv+(D1d-xGQwnWSVkif0y3S zW^WqW%J};z9bob=m>j`!Q_xN`C=)PpQ9?>UsZl>rSidFR7nB zOY^Vlj1lUrq*S8st^A>TP9IbmkMN(m`4-rb z0pT$-u=EJ8v3Cz?E&XSB9j?n^ingZj@Hff{w-)EnA$9qITl*5H5A~J+&bD`i&m6&B ze6T+Ci3G34w1;1;40!f^xGw*cJTiRdWn;r1#dDKj`m(?U`BrNyj$d+O5ZQ`&{@ecBkId z-u+aQ;^}mKbm@RP2z=`f3?LVHzHN^VT?i!q>rj^E!-4kDGumVq5`~ZJ=y*3lE~QSj zoSDk_@i|uFyl;t-&EC4*DNLrpsV}vZ+k*rYyDp*)_((S6OATi S6$r2Z0000VGd00qoRL_t(| zUhSQ`N-Z%IhCPoUf*^v02qIVtf{kEdC8AhpBNmDl7S{GYitjn+{DbSTPEKYfE7!?N z0>NAMp3Ikj{;b@#zcw~DhCjKd%me_v=e!K+27Y*WSfgO-hJTKy6yPg^Uq5uR$BN+V zf_`#xG9XVWe{XN^uO0F4GxuwHpqt=q$~POM5O}@NP20f}U^YyLo;Pe88=ylk0Ntkk z4F0a)UI^mR(a}Hl0+pM;%K&_w0jP0s@n4;HcXvmtu;uOT?O}6sa|DdPpP!!(x3{U0xtxcu3IN`DKr58{{AivTNiXF z82Rn(ZMe9&SPR^#dgxR-!1FA9G7^{937tb*q5Abhrv_M&?X&~^;o)H|e%}uCtE;OH zJ#lCCU*v$n&`K<(9i^`XxSnYMm#3yfS2E}0(%6Z--S%>Se?N{wSE6uTix^zNTwE zHX6|g6-u829}0c$^+q(L8#Y7~Md!bA_N=JyuVQ}b@D5gUfjHqd; z&IVpGGVRcX&`t29cIiBNAozCdh2~vsHh<&l=f(oFPmHc}tF%;wQs8WXxkH!2MtR@l zb1n9oy!K)~+JTjKR@&3k)8v*5ANYaw&JVo=_`AEi!3Lh-Nfloebn46kEA6asew`5L zym2DbJ+IQK!ZoQq0%TJpWuTW*{G8ft-ktz>xsVY`r<~}XuLG+<#JN z>v9RjFR48dXfA?0(5HZx@c1RQR}bB^5Uy~3bGfaiu-elo-SljNJ$J4%C-}l@j}P5d z-L}U#C-{PDj{|)w8kw?_CEv^u#}`z49Ox!QY6PES;yF9lLTZl-ox=_Ro-33rr1m(_ zW5PD0nk6d={d)nmheLOXOhUn=et&&t@L|w>JId4$rSD%2bUKkHy}>n>uLe4{ErfQK zYixiFb@H);Qf`Z5js@<`udt0yc{}N3(j9^Q#zod zQ+e1CuUc?!dzb1NZVZv}dQjmCv; z`_O}c%K50MhS$V{-r+j4bS_wff@-N_hhE?b?a*^W{|y~7D;wb+ybY!R00008-F`jX3sJVl`>gAO3W2AQ?XubSzZ{X^SO9zle|wR@9%m%p2muWt5gU= z(}(xvyKPS<7Nm=`W<)#RiOEsma`@1N*n%uik9C5zCUg9dxY{vC3Jgi%v+u+ZS?(-_ zmP|A^MAp#+OoHC)hC!d9XKtg|T0?FYR zDPf@A!G9IU$YfvCRe=&1uav;cYPFiNgp@M?Luw9YnwHt>rxlr0=*n4zi_2oscC1Ur%8*dKQm3HN zD2i%QI#IGLtAIKy&pHpbCo!t5dQ?KdKvSilU4N=Q1~J10s6&rGeC~>u$T9rV)A8Q) zrBNVLrPO@qX*yYcqq@uv3;uw_`nf<(bU4xE=jWS&FXb#kayxGw$HlA(ekQ%sAun$1 z`&7q%Z0qrKOTy;DWlhe6Qb}@PF1c+v+JRE?j*3OIFJBL~)yCH!eyk^b@OHKg&s9>9 zB!5xPMa??D%`;&y7Gb!sEGt}X!tQwuGTSHZDMqt=ZR2+zo|4R=Z1V`B0t)r(erE(8 zngr)18-~>u?|TjMs}a)bhhT*Uoag(WPKTHHrZHwg^53HC*QtYg^P zb0N#&rl<9j?VUlE39Lzbst!`z_})Ki&rI<+9`N`Dna z(>zNC0R}94(>w3ko=1I>z%VSYFuqDCv{14viYD45w=b|52^iOSMLHW2>cz4=-;b#S zT^i-sPhL6ei$98@i5^K=n`?#F4~|^`DJPfS^7$U|b4;1X9_*_Z`qW((#S!z^#_pK9 z?Dh*CvR%_DM23vMW5dXEi(~7=S$|fjs^9y_tL~uBb(vy#0`!Z;B8#+|(}r9kuE!@P21p=9};Y%$q0sjW0(rreP9enkFHpY4UTe^?#rI>lctC^JHe8R9YD(UwWp=*O$%X zf+@1sGfr+F#0--|Wtd#AyMHpn|hZ z%i!ZfDK#99Mx%So8~^jo&^)Ps0co~;>X>f^$-w|-?H%=VQ_6vQ8ZfOhvm1-olx$#_ p92zDErb#Ftm94Ws2mhYF{2Mlat)+kjF7*Ha002ovPDHLkV1iYh^|k;2 literal 2760 zcmV;(3ODtMP)pPdPzhh6R@`T2t12?UirKq*0Y=>Gn4H6OlCvK{+Y?1%$0l1nl(TFoj7r# z`E%4)0W|=~dThTAD#St_U~Fg>NGoe$)xQ>U7PW*^5n_(ICJJq3lPPjGT>dkdbI%_&^WGN zzur84{J8zDLOjN9K9H(-0G>a8zC8pMWOuBr-1=Mx$SYT_RI@p5Ya9r)~#H}pu~LhfTYra6Qc^7PVJ}qk@n0rE6gK)>jEk7l{U}=MWLut1IrcUOwNuq zg)wUM5OHoiP!tVNAX;xWM(Ks8nO2d6+W-g@d&E?t1?S(}{DAb`k(lcjo@P3bm^?wu ztu(=x5(U(HsRBtF&E?7czk>2j1M=0YS4RrWsT={sOBF=E7XJUN z$y8TEDwImeBZ&M4NSRj)b?m)tDSz|l<;pt_BtucT5Cteyfq=4cRROamm&$yrW7*HK*H$1M7=Q#a3KdAs zT8bCD7*@(;QnZJPV)<^Y$KG{+F(Aq7RJn|4Wfc~Mq21B@zQJOp3EjL8&P%PdvB32p zV`mqjJPra5f+wi9O;tMn7n2+N0EW`VK(wCgqRtM>>QEnrU_B(=7tSoRlS=iw3fc#f zG!~vrF=t@Pv;tI)%@~+KB|qh*odhIhQ2S}y(G;k$5Cg%0Jkv^CHKl;wqzah6w=nq* zXs1t~K2j>*rSw&Oo62X}G%XC$d|H&OzYivdHJ`Q0TI)UK z^$w{u2I4@Sr<+!8CKn_XAhlVQapQtT8gHUfK!(ajMFP%xvifXQYJu9twwPD7mudnO zkl?KD5HLMheeN>Bk^RJ>I!^~lkUR$@j*L|!@KByMHWDm8qf-3`P{+1CGQWP_4*)D& zfy8ODATi54z_zUn)qzH1fUEa9%lVSy#d)BUpHk21APJP`fV64^poJ*MM`o7a_>MdP z_U1U}|GV$+SISRv0>PLl^Y;=a;A{`~o~$pL90QdqSiw%|bY zAb~iA8dEFCs*$`>qsBw+?<%*oBKH5(Hha&PYc8h9j~_oKk3HHO#Ju9Q`hDW0KnrpX zHG05`7==J8n1JIH@)NYB3=R&5uC2CFK)cj9Qk`jvOntIRZ$oU_#lv0)hzbN~FF667 zTaBTR9&HYa+PG}QA)VhQkeT8WY z85_Y-4A(kMJ|FjlXU?JSh=t07k@|{&IWI_=E(&JV=iADq+D5@s7YHQ*kMf{MpxTh2 z-{*k)AgzKAnvf=?EA*ENG7u!6c7^~2kKKhz8y{)>QC^}S8d^e))?u+*r)l7v-!=MS z`=6Tr>AOli0HHuUK)!wZmdnw{S1rtG6xj#!*RNm4c5YN#W8Hmq0i;waa40MnVQ#Xz zNfwGM17+sDOsY(*6bESSp(nGdJ@m*&I>l^9Q~Y}YcmLCX98#u-N%~OdT&8JmCnx2h z5Gbo+1uZxO4#!5ZSna6?$V0sTmVG*qs*Emmb`2pZ)D^R=Af>|RN~_HfZFmlNw%ehu zM;FMhvc`h{lYe@UUFuYbinhbXDfONQV5=~_vxXWKW0fNB-o4wJ`U!&o)3=cxUPzU8 zpjS68$46Uvj$Lwas0TCmx-!40Rl{AWs*L>a=@c270A&jykhtJ>%7qrQ@?9ya?5q6M zgB)rj{mprpI0M>Xumlquf;rUYMXE34jZddY3o^EEY1eIN-?@@iIRvs}^gpH>H9g1_ zWoq0`d8A5Bq8jmp#@zhuX*L1Ut1woT7N%556lkyJHB*(-Dl!(HR~kyx&{645&w*4q zEl71kQh`d9b`^3puSsl@5+Afp@i0s4Q_2brDKgV9SRSMe-DyTz$>wzk zh?TdrkljrE$>kNvjatuD?c`qjlR08ISkOGDFwD?XfMDw*B18AzXI_W$cKp5$MN&8> zH8E~!O>C)sNjnPS-)|lyH(*s+vT4nP)z@oSk0L{=ELo8g!(10gugVHCnoo4Xg9Zca z(`nnv3No5cbi&y{vMj+mJrkRt+3sWJa#xTuSNNeGr1gP({rYuPWcsAIb%Olyg4Bpe?7lcVg?E^Fty5LzlP6D_hYugNg{&Z>g%+JKRq9#k z+4=>L_MxaSqZmac%7T)kP?RV&Dt`qT#V9J#Y#>AMLo2y*9+JZIYy;%iuU~6>+26NZ zH%Qc|qr&WEwLXxviHf(f%N83B5g-6)h1tn#T_6P!fWXuWt_pLI(_n(x933qekmt(T zZqeys7Vf;H*86%uc7gFQ)n?Xi$uV~82MJ&cQ|+b9ZdGQ>a2p^ovuZy9sm*_@Dv$WB zA0%pg@#016{BX7pAD=7C4Ja}cva=xX-@ji${@<#uUy(y)tg0*oB&w`|f&vtYVo@b9 znW?VsN$%e3Ll$7Azi-2|GDi9A*|QdyOsTpb0OY`%)v-gE+W?uW0>A=uN2)9nB;YH^ zLfXw#6>mI!xr&r3aey`sDW?2ofW#5vzyVS$c4rPx86c%N!TkLB^AXUUOm;2{q~^j} zWdahwg`3xML8=Qx5A~1$ O0000P*6~Ic6R9K=!}evgoK1zT3RnJFP4^;prD{6BqU>FV{2<`t*x!NxVRA! z5lTu*6B83oPELk~hAAm2CnqPQq@*`DH^s%pd3kwPS63Dm7JpMyQyv~385tQ44h|a| z8`|31LPA16KR<78Z!s}3A|fJPU0r8qXGTUwIyyR0QBh1xOf)n!WMpJmSXeA9EdT%i z0002x)yKI200-JhL_t(|+U=SNccMrTfV1c9%xtcSiAgq|cyJ1K)0h4K|JPP^!w3q9 zF_5?KZ9Ri%e1G;wU0qdpr9WM+{zv$)BPf(lL=ZxJ%=+&~xDj|vlBA@U{t&jR;xd8UP?5(%VjEVggvz5DyOx8`r$eQ52dz$}mxfZ$;?+S;|>9Ot_;tzCb2 z!Dj91=ju)i5LTnnXgb}ZBJRICyPdPGf!Kk-p(VmvyKC$q^TkJcqrIvcVoS&h#4>;# zc3a1B%vwhZ$OzgZaChv&(*_^l4~qxaB7ZE7_Orx#q+z(_qn}-Ob>I^i znW^14rc>_-VRuYWMyQYK*NqETzj0x0y@KQT84*0AoAN~@j6j++5fXc4hMnG3Ab*q$ z(Uey~!A|h#W=<^|hleuMypoH+)h=7LBuJK58Nkn+4$_}ub zGDcJtLKYbZFWVx-NP(d^1|_WFIW~?NS625-QiJg9>D_qOAcqP1u{kloam0%DS?q=h$>PnW{%QF2cq4GdDt!0396(|7_;=9pwJoaksP~N6Q<>CT#fMb;e+O{AFC?3p_ zHx3jOC?Q^ZqtV?WKG?9T5m*g^Ey$s9vJB?Cp{Y_?5ywwCvFF|0O~-K?cdRyoB>?L) z?9{fBYHwWL#7{D4WVj$G(SHgWx@BhmfWf=-inpK?J&mBNc8VfIGXkuPe-c7LkRSvX z^vJ%7Rx}J-Ae=~!#k?S@88RgZBGZ7#G688bVmo7-Izo7Pu_2ML1fx<&84#j$c9#xy zK*&V|M@9S*!Yv}C0w-DqK|m*|@|b`|DkLos%0<(;JLN5?LEsDt?SCL~phhzS@Bz>V zB~+b(us_$eB})xL5TX*$AEYWGgspClR+UTTcbgFY`UVIxrHCL+T-v@Gt)RFyf;vM9 zx}g1<+VL3Sn?(RmWFcVi909%2WF&i)lW=Pq$|(bS0GPyU=}$OQpK-&<;~ z3QE9YfuHw#f%hQzGas&8!O}`Cm-{qe1A_8%ZgJpT>Wb2l5 z5zZWU{TL1=e%>f92j!}a^Y{H|V|Nb1?ZnZ`N%rFF;OoKY?W2BD)PxW@aQMxhFNcF+ z^YE_=gj=WZ;?2)Zk{hN6gp6QfEb%h*{h{tknkR0PC^AaW6aVd#5QfpuGyO>jU!uA> zR4)->@?(y$IDZI8^?^~@65%K~{V>mN_Ezd>eo^G!+^p{h$QESJk9`Z4pLqKdGj)H*|V2(a|u z2$4jU2v!N`lk_r%W-Xxp0pY0*K^skUiVztBFvRRfgBA$AC|kF=XZ`hhGWn|OoPn_m zz2SrwqDw#i7=H0lo<6C9*47;{B(6UX0i2;BB>K`qQ%H1zn}?r&HoP^*8l(j07*qoM6N<$g4;C>T>t<8 literal 3871 zcmV+)58&{LP)xIh%00009a7bBm000id z000id0mpBsWB>pT(Md!>RCr$PoxQ3RNf(CayMX}_gh4P6K?GqC1i?fQG!PNRKoP`1 z(Lm5l5d#x3G7|(d15yuxY?);!n`5YC=Gy9t-I0Q_bEcGDs}dh}?+ z0tBo0ty=FU2yfrMZ9oXczkmP!hNTn?0abd=g3|8}2yXuW{`>DkqFudub#w9J#RdXx zE3MWgLaiXoIGD=y{X;?61NX8_i-75u`EM(!I{xa_tHu(j^y3x;2K&0Kg2UF8cEQKz z55ix6{k1uH^5nsy%oYd|I0XTLqS7Fawx2(L-Yg{$T)%$36}AP7@2+v)AP9mO2(iqU zVA?MTv`w)1?%li1$B!Qm#RL2Xu<&|V0bNJX?09^zo9zg(+QN$$FAjmA-+}lw9o2dc-2mqi9 zgz(w|L5lzo3;>b)3=2=U91tYX<;#~3O6QX&PY$*Pfl98#*ZR_>ONSbiBq~CMZermA zj%BTu34)+-5F{ob2o`~G>(;F%lb$_$)}%dm?p(9v))@8-kmZ7qf&mb;ChC{?YFSkK z0aeRA6S~WCEDHquz98@=ChGa~=MS2du>ycl+Pu66&@2apJrJX-)J}PP1p&Zlex?S( zTJgpnK)4$q&~zMIm$U;225KNWFI;vQh-HJ|LQ$d>N2r$I3o(Ie5?za~8N#67I**2{ z9Ry<8Ah;m063jk8z}&Z`)iDK{00B5V?kaW%gj6Y2wNVSl!x+GS3jn2~>ZxOj5x7Qp ztsSxqyAOiIau8DD(MQ3pfglhRF?X^<5bWO9$CSzUc7vFxUSkjlG)mDzsdKzTthQ;n z+z}w=Z4o4(#7#8-Ni<)SF15H|lydLPDmQ>@P+A}$jfCggJ7^*13qly^@w=-8hH&uP zo10VDP+DpRawig8AcVrZ<lV_?69mr)e6~YD6)!i#9D=YdAcO$-Kn($_fbfDs_t%=X3jOx&+~=B6 zx_N`(K3kdQX1^^HWl?AZaGp1grtoxmYLEoN z!ys#b_y{8qly*uWxR=9V+w%lLBcPSR5|nL3@d2wT!6k;$Ye0+E$B!Q$6$rT%2}c_P zE`fCl26qb*0r9a8F|qnuz?k5veLwFpPY@K)N#xU~Pd96SPQ-Pgy@qh?*s;b$>%0^Q zN;CA`>By35l#0P15G1}^0zuIB*Jnt6>ji^@zw)6U2wg35Lo*o=fB-9S^ytw8aV0YQ z5nR@T{hn4ph0i1yh5!+^G{%BQhXHp{A`)p!VJN*vFN-iW1QJXoT7t^Qd&x#*nR>Sj z%5dCB2!g~WE!R_8neibpMELE2ehINc00esOK7SzyiGZ(xz0Y4U2sI$k%TSuJ2neY) zV8|LM9l@IKmj#f>O79>@2&D@xO&<%!r){m}CIkkpkFi>Zo>%wp3j^Y}ME<=%D0U6C zARrSF-=Sb%=n~u-=zNtR2P2FXhFAigPXVLyME7D$r3To@MT8|-zje={AcXNm2wbT( zu=Dd{gr`rR4i2zNN*>~cu7TQw2a9ZT4np82*e{n;@(f*Rxev7mzC$pVBj=9=LR+n3 z00d>dm}`UJ)kK60tA*4m2okAky5)!km459mv2%}OA!HHw77~JhRlt|)`D_0r-Y3r@ z)P_J0>+J^!=Ptpkl}v)+8Lw^P13xb{lUlCerRii1h-c*H+${-$nZVEuLg`#Yr5Tlz z9w=BV^CUjg@3EZ%K|tVQ2sp#`iJa%?lO_ZqR&CXL)cWgX5#|6v?7@|Bg2CGjNarP< zZDqc;-A(inwe%cjYSVMd8t6H+PeEhYhA|}w8PDBr5#|6w>KTB5lC2EZ3rlb^S+KBO z69R8?mPG&r?WY>4?XdZr*kLRvG62Z`=rlGAs$!M2lyQGXBI!4M3c_d8e)h5L`wB9q zckKcQ1fdo{z$&TcB8%X_#-6J$3_ahmJ4Voj$Am#3Jbd`DHKV;RATQ=q>(X!&b{|v3 z?)!W&3RSzdPO&{R@zA^AjzSQy1~CvgN3|l_8hDmIK=gQzJP^i)A@!iKfu>Bc-!-Q} z*KsD-3dcD1$h0hLfw-~=ZVf^ZlurL&g6|RlG^7QxtqHnUuo#{+mSqS9=ek&eaMly1 z$J6tuj;=n5ER0C#(O9|h$xNUUqy)b1fi1YCe1&>{e24Ft+qe>ueY39fD2Y4v;=%Ip4#{4UcW@dvC>AKEw3G6 z3=r+ywAdCTlZVU)m1zk6s zqbPI>`W?<_7JlpJrGUU=07^erfySwvm?F1C4@s9REcfV2Ujj?;+<6vZ$jfqW8wdo0 zp*09&2D*W@S%Vk|#00r9#Tf6KbB!)x!xqA>^tr%9S|00x@bcx$&CQ!P58MN$UL{_o zTw*y0)F8sDWK7JMOJH9rCfK%B=n|FV4piGYA$U30w{z-)AQ-5PVEO^U14Cv^BXCq& z`TQ;r{I~<+8HKCJDYa9jDHJNJhjIxtfmNsn!u$8{o3VX>KuMSQ5{3ybu>}6FcYNZD z6bN2ppazi=aSkBl`14$0W7ebV`df<;zI^%8e12w}pVt@wfeEo_`6EW4+qfRyw>?md zeP2j@bEy5{DP^`pvHbTyQ-4rRcSu^Ao$FbIZ{NN(|CbpUa1U4nV31|-PsaLB&L}~(A{U&}rM?Db!{rBIE+=F1y8W1R*^dSfXyxX^L zH)$!&Z5f!d1b~nVHcLg>M*5usK`?y({(Yl0{vL5$Xap4mfRNJ$kg72db1cmYbj1m6 zF-0M)bu5A!lN5uULQ2%kTH-Y8ghUr0&HIVqKHY&p0=RAk$Y*{>!f0H?cxXk{&jCC8uJkn zP;PT((meyJJ_s~5cV+Zy4XSEW^=6GFQB$)lLKg@_=WJSo#v*Nt5_gK%C9aOk_jiF% zI{#tp6ZBezEYh|paWxQffC^h8!v-Y(U3$GeOg|k63iuT45g4*|i{Rbchh)BgsG`|= zjssbSyyL{@P6vWN_}8yr8zA`JK-G~+y1H5#yE4H+z$MVHVNN5Oy53`p$LoNgFOGmf zW077O5XPt>gfYSD8j7?nN<1h=P^sq=G++n>8j;2*4YAMAOaJsdM!sZRqaFx)7#fO* z69j^qpan+8!)B#fh3}?d zAlv0;_Tcz-L3sW8^+wASF@nTv8x{`;v3VBuK=}Gy)nb|It&8+Mm?HBX1RvYr+iQD6 zV+^)y5o+Audor&vK~nE}Aow>24Tx3GW;H#lI{D+rk3-9t?Fup2812MZ%ehUUQKNta z_oxSg-Fk%iHO068^KT hzQ0!xrqhVL{Xa9-0a+s9?qC1_002ovPDHLkV1ja@?S23N diff --git a/wadsrc_bm/brightmaps/doom/FATTG1.png b/wadsrc_bm/brightmaps/doom/FATTG1.png index d3749efe0fe6a3ee577316cc854a3295f9d67712..bce6010b72c77ff418f0a95076477c3587c50226 100644 GIT binary patch delta 93 zcmX@Yls`ctoQau%f#Ig+g-RgB8Q>G*T2WEq=H>?E-FSXq7Dy?2x;TbZ+)9pUO>_%)r2R7=#&*=dVZs3V!o+aSW-5dwau?>yUwf z+ruwKmce=QZ~a>Gy#6;XoFBk)@a*Tkg_DwdHtz2a?>NADeR3j0PJmv)!Luo|<=W4r z>J^862xnASFUwtgZtt~Q&wuN;tUlko;N72}hUPz->l?V&n`P-w zcm3iko_RV(^oH0gt%TV#qtj>SDrQLUc<{|<=FT&z>q2!R7=auaL$J{g!q%)~+BS1% z={C!MY`M(hcjjptPv3cFYZt3F1CXQK2GYWHr|W9y0W;(6=gf}ZUy%Et>9%p|Go$G{ w!!{=9u>yI;M+|^^LK~wuLS6Cg#>el>5`~+lc;38g3k(AWPgg&ebxsLQ06RO8`~Uy| diff --git a/wadsrc_bm/brightmaps/doom/FATTG2G8.png b/wadsrc_bm/brightmaps/doom/FATTG2G8.png index 6b12924f9b10826ccdff3e22820d76ab178980e0..06b82243f4bc67b900803f6c2bcdfd59e7a426eb 100644 GIT binary patch delta 92 zcmX@bls7>ljER|nfnm;iuLVGgGr%XrwW6ZJ&CLzSyYc+KERa(0ba4!+xRo5y%B+(h n@yp12Q`_`u6{1-oD!Mpo`a2nf#Ko7MaDpiv%n*=n1O-sFbFdq&tH)O6#V1q;uum9_x8FWU$X%N zi{ta+y20F&j^(qIG!z_ea7YV! zGI9vKdU0R$dW8EWMy8foM%N~aG%zgeyECgvGr-qTua@6H&DltYA z2RdAOzx3mJDY?inw4^<~zxG3IqS{B*BvNg0eN2+*ZOw1$U32yCHJjC@S%5apSv!=u zz>7j<$O4eM0kokzYa#Y;Ip5L1u@b5{KT*rEeA1fcsD*Nh{R>4z4+|=4aLQ{J(c0H5 zUg26_@5sl>7vETYcXZG);K(}S9CUKP`p>?n6UFo!+!82C>i_@%07*qoM6N<$f`hYg A=l}o! delta 536 zcmV+z0_XjT0;L3y7=H)`00023teB7h000SaNLh0L01m?d01m?e$8V@)0005kNkl8v&!t9Jnx9R)0bTrUa}3=)5rZeLr1u z=Qv^M`$`oRS-H~k^TK=rPD{)RpulVaEc^$Uzp= z=So%wrV>cM{WEgptoH>>ML_`*m~)mVl!-D?rg;~bPjKXN44BkRYNmM?m`{+JsaVH= zISWj;N1yK7|9^39vdu%F+Z{wOoN zc2W{7{HDSJBWqW2UopsErphn_YnD0x5`sp1cbK<3 zpdO&Bg49yyZJilD)B>%1qEdW|H{yOg?W1+gnZ)`1)Tk8d>Wa((EzMmRaQP9O6!<@s zGrG^l+}XBJ94KG{ldWF!E-;_q$R!ytshQMF^DZ!-AT?95jsX*xb505<6J?@IW^*kU ad;%Q^AQ2*MT*fW{0000n+Z000170-o3a003u6 zL_t(|+GF^S3mjmenp9{!PN~14xDEVUPbFt4)5u?=QlnCYr2eg1k4x&;s@1rpUaeXM z=lw#JT7@LFY5^{(f2&sDGVm3e)Gve!fvW6L{ndZ~TffZsI6oeCdgb>#*SBi+vdr2$F=?V6 z0Xz0)F$f%X<(wj=aeC95>hiPJEgKyKyS7Nbmwm|aRAWSwO02rY~d7HVA7|5xM^PRnzzL>k~qXP92ECWsW>d+b5O*`a0>&F zJ4=LnilIrB^XvvD5v9H#Mg#=kyZyIu2;I@N&SEJ;UvgZ)6mEmVWviqn@SGLx`Tb|r zt7Q_KWP`tcVzgam^1r0V^iANk69IkCdxDW0}op0F6)ZoyIo!Wb8;CAy9V>N152+y+akEqV9}O<rArR2uv_^Ado92ur%^67oXlc`|!MHnoCNS0*JYD@<);T3K0RXazy_Wz0 diff --git a/wadsrc_bm/brightmaps/doom/FATTH3H7.png b/wadsrc_bm/brightmaps/doom/FATTH3H7.png index 33e1741f46910833e79d1410d378cde930980fcf..1b7ca4c67437231f73cc69b6b846fd9247365d3f 100644 GIT binary patch delta 166 zcmV;X09pUd1GWK>7=Ho-0001H+7pZb000b7OjJd9cz8xeM*si+0000Z)Gbp0003o4 zL_t(|+U?Ro3V<*S1i=2>|HM!Do}L76#dZa~D7(U*q!e=;vLt~dP%Y_W+c25N+4W)V z4DF)NHH>K0vtTR2f~}dRvT_DO+)=NexLKabO0Z>N@n#TNh#wgcyQrN#!*5;N0XtyB U27gbs`Tzg`07*qoM6N<$g5v>3$^ZZW delta 447 zcmV;w0YLt?0nG!D7=H)`0000RwEG_b000SaNLh0L01m?d01m?e$8V@)0004hNkliL7JD<$Rk!YbOEW6 z^grnWBwxPNF5Oxy$?)@mB)|p|kOXf75|GTS!YokDIN)Uuknkit$t(-Z0(tS5nseL- zspZ5k_(sjct9yVXd;!-WrQe9QYa1ZElJ;=kuYt6b&(aDHsRSs!4a>JM#W{*6n(>CB$-INttN&AQYr3L6(Ni`1_%9CmUh4iFaP(k+9&u&2g zt^eZkHgS7A50C_*00~I<*a=EPNhrxI3(Nv}EbZ!)EGdG;N0 z5O4`Bw!Ht}t%rNo!2ri(rc!(FI0ey=SHeHFdvEw$-g5W;I!7kv6{$E zdB+RmEV-Alj=g)c#p-*kUzuj{d41p4y7h12+0S$89>mAhx3ONjtY@vu@Je&Vb z4zh79?l1pGRr_`=mQ#5nH7%s^t6Z$dwFx);Hyq?jkd&@tJpJN>z`TVfGZ^yZ%g;o0 z=fB+bUxD{VY{J9`_l(!C`F!|}dKU9*)pXYUUq`n8=&kk$6A;>PAnLimM4gk9zz+X$ zOfYe!+6Ig6&97nu^cyxa@i(wuv$`||D6g|T@d9f?l@)+lCp|3+<+m= N;OXk;vd$@?2>=D6o-hCa diff --git a/wadsrc_bm/brightmaps/doom/FATTH5.png b/wadsrc_bm/brightmaps/doom/FATTH5.png index c90848f834a9d58014085c19715a502fbe446dfd..00ab9b9844652343245bd054efaafb647b24ad3e 100644 GIT binary patch delta 96 zcmey$R5U>%ijkRtfuYd*;B+9x7T^=&>gMJKWQwl(wGBwAd%8G=RNPAb!OvbV!LdP7 x;EnzNR}-(iVc$Ia;G5qqXC+hnH?b>AGvo>%HR7!O{R60l!PC{xWt~$(69DLhA{_t# delta 357 zcmV-r0h<1D^#YI>e+UKu002vcQ`7(e010qNS#tmY4#NNd4#NS*Z>VGd00AdSL_t(| zUhUUW3WGopgW>Jv_Wn1rwa~XBGu0GaK4ITN)%4Fye2g)+*Kq|L*o}3r_kgJ(u+}zq z&bb|(kg4zSy5t!6n0fSm70n5M)-<+b$Zl{)U(}vA_FL47e>YFO&`-Y^F!#K%cQf%% z&&$}5(Trv6y&4Q+5sO$USoX^+6z@()Mpx)hU@T)TFDbCRaw$NU8JnZ4Q=_U{#3B~4 zQlJ&&Qh+WqHb+-=(|2@;MJ!^aKr6_l09|Hmj;_wOuWAvCSj0+!R**{py3E)dUDZwB z(IFPGh?N4ZUm%wPbeXX^x;opwszoef5i12+K`sU8GGlXeRX2S{hgifSRtmI&Tnf-- z#^&hiZ2PJfv4};i6levx6rjtD&Cyle^c@{y6U06Nq}O~>ngH=?00000NkvXXu0mjf DwDO=# diff --git a/wadsrc_bm/brightmaps/doom/FCANA0.png b/wadsrc_bm/brightmaps/doom/FCANA0.png index 1c6292ec0dfc9dcccee381e113d57819d3bdb237..3cb6b9d5b7c9c9f5d4230862aeaa2277f592636f 100644 GIT binary patch delta 335 zcmV-V0kHm#1>ORX7=Hu<0000qW$q}pUl)uI4mzoh^G0j$^$wqbc~B;X#vBkl*^t4#pUel=lhgV5Y#o1)WPZK3kdH0anbTSL hyhAj{SzN+c_W^5i_`Mz_c5VOw002ovPDHLkV1j!%lXCz7 delta 626 zcmV-&0*(FN0*(cc7%K<{0002SNFta3000SaNLh0L01m?d01m?e$8V@)00001b5ch_ z0Itp)>5(B5e;{K>QcVB=dL{q>fP?@5`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u z0ryEnK~z{r?U~z=gD?z5*;e?Z8d{(Px}XmVWh2N1tyczHkBvW;8BYQ+=vcav%;c-< zy6rWZIJWIVp>H1@FPQUylR|;W+TEf-lgx?pQVCE7f7l;N2uE!+e4!kGcD2{8B0HZ0 zJ%x_!-pq-CQiz)`)&`Zy9?_c>p43B0`pH370O>R(87R4y0=sD{HLf{-5P7+$G{&WS z0Q8WZ_nPv8anV#1OkuYE_EaJ%YrDc12~+?gx0xuYN}#JwMFACN zTiPp+e=S7xQR%ICs7jzCe36F&C>E;kd+n}P?tNPCk!J{I7zQm+`7R+B1~Lwh0EVua zp~i9CrfJHqjDDNvc^`Zp?xSn;odypXovSsK-pC=AW!Y$~>$(q|0}|)-31|-K2E`x5 zy+Ouc5Od%m?iH8tm97THC`1^zmxs-$r+T5Me_$Mn!y3W#uUM&v0rK&Zz`>e^BGBf- zhpJwhK*Bs9O`)w{8H8A2!5DXBO@ygB21QM@rL;rznKft$;| z|Kje3m_I|+JOrM^#YA@17}4giLVc>>(ns} z2(mLB4`MbA(0G0OAx6gg$aTJ_%gx>3Lr+Tgnt1plc2$(+B6klVU3cY zC9iGJ3{l~PO0s3%U5MK#D56+aO+jqNK)K*r2dH$P$hxciO`^>l+vca_1C#kmG!Ok5 zJl#d}=r3J&=!W6APtB*(8A1-qCr^&XOxoEyaxR!(&glz6?_@M&ayLUL$@Z~oYM$gc zVe;1bw}fwGqA+ap$SeF_^hVOs3+E9c8u!(9c~=mUWqYoOuP0v}h7~BSeO3Sf002ov JPDHLkV1h!UhFt&v delta 533 zcmV+w0_y$70-^+v7=H)`0000alsVx5000SaNLh0L01m?d01m?e$8V@)0005hNkl=H^_K9~?phLVs%KI*0_oi<3hMsq=%> z07~jqRI`Iv0Z@%b>hvHUfL7W_%5)%|A#}T@7X&Ndwjz-rXcxlPu7gBDP`@hpOBo~z zLSt-2XYB=1fyx!(u6GrxQWx^u*W!j=I8{I5U0ge_@q#^5bI0)r5JkK32sKWNzvFln z5jwC|ODbD@M1Q&z>4^I|G0$_Frs-G^KH(gP8Dw48v~62aI=l||z<_fY5NKJJV@Sc_ z?*^DWH8dc6-}hY{^cv2gb1#GNr$7bxCszdLxl^N+4+B6g;J~?9CvXn;Y1bt8wOd8H zD2JgS6wm=<1F1bJ2FKlFROKIoIO`-{5j3~2qy`=KLVv-=DI#rUgDNKo^#Q}=U%%jr zIM&D&kqiq9uoRF!2z~h(fZ|d^AzwqI-tjsss=;@ju}ic)S6!7p41PD(hwi_MT8SUo7-`|Np4B0xSg_RCjBEaF-^d+gRpbY~ XfMxBjL=G7900000NkvXXu0mjfHY?^r diff --git a/wadsrc_bm/brightmaps/doom/FCANC0.png b/wadsrc_bm/brightmaps/doom/FCANC0.png index 5d846bef1af6a3dc6c676858e504d542b37bb3de..7b430607c4a18acf1b620af78cd0468b8b6102df 100644 GIT binary patch delta 300 zcmV+{0n`4s1iJ!|7=Hu<0000D{kMn!000N1yGLAy6L+02D<14Xt;oc8w`5|=K6&K1A=W# z35ntP!H*2v0j!gjzyTHljecDdfS(XvFp@B|pDvMuJW#Vb6n}CqQ5DL^L$xnb32DD# z`_KVlPa(#3M-f#5abqK%nr(qvIVAU+@GDilbfM2!j8uu%8{#&NI7_inY8~zuc4fmn z)*GkCb5>T>WEYOY;_i6Kk#p625v@5zctE11g}+)tLnQ>IN-x7>RU^CO|E}88g0|~L y7n0@YXENbG#U)-uImJ(wpgXEemP%N%^n3&I&y9!KC2NlW0000xjygb@$>*2N-TMkqLh=uYl;eXJ@P8>3gD}^zS~`5V=gJ|* zIctO55qqq7Q&O{z7&{H)0%Dx7HW`??7*Ptbj##@ysQ8evPFTAW5snpNomlK{1w^FC z?-h#cPXw62s99W(qV79_&nb>Meg6s>U)rrO4 z3}l(yV|yzgBY(`2L*G1HK&gjnKssRf_9HiW*L9(7+j)h^r(9#WfreoS<2YvfqQ38g z46&0T=WvF5fB>A_*LDWD&#eQ101#Li(=@Fk{!Xp|3;1e4skhMzX_d4>c&<2PteiS5 z5*i&IeJ*zFoC!Owr;gt~1%&UA+)gMP(2)};^8g*yB!3;++k^~7@=_pF>73MOsiB-t?L5mHpHP$c|W9`K9%}xAB$J2 zGmq-!P%3=pJSuFa*1jjzTbp72|99CHG}NG!+}1i&1mt{pRY*5%Pd{|l4;Ai<%PMm& s(Ool-IV0jvV>)chzUGQELaZCczo16dt|A2si0000Obmcn$007KML_t(|+U?TY z4uUWY0MMz(ZoSz4|HoPe=+w;!4<;t=0mx3;vaU|M%Y8yXe?+G?oo#iZkhzyo@}A$= zLt#uQ*{c~fj^oyZtllH~2H9bUVJKhxNFw&|UL@yyx0z5;&^pDr!lf39pm5ntM6YB4 zs*#>(R!gk0j=I>IrR*etC*pjV@`aGryLLq;TgCv?QH!h&R7a*89if($3IL?$r2y!+ zX5&vCVusKrG?CDY!Vq`VvH_n)^=Er1665A5krdlx-RMwUg5K4-0(u={SNO{Bpa1{> M07*qoM6N<$f`CewVE_OC delta 85 zcmX@bls-YipOKk?f#K$ZstrJjEx;$l_5c6>KxXKn%XL6X#?!?yq~ccc51=R`+bzL% kM;B$WM+UiXmrh||_?C0-zopr06=RY8vpCcZfq{U4fO>j*ii(Pxo16dt{{R30cXJyr0002! zNkl}HLIo@!-juF5`uqA(I$ex z{dTIBEQixGSy^x{5n0iO296rNIX0|QBhI0 zwzfSzJy1|kX=!O;VPTq@ntFPAW@ctwTwHN+ae#n;kB^U=o16dt{{R30(isPK0002o zNkl>kfi23X)pXEsN zMT|7_2?;_Uk)G!uN5La?3UWkA{!MfP(5@+<1R;{w$=V=6{R2f`LOJKP;TTH4QO+;r zX7>yGj40>@9rfx+Ze6fcls3-AeX{r~?zkQsXDavhM8_jGX#skoK=gP%=af{nXY o#>?C})6Ha;r0?DT6CIfuR8D5gxu{C~11e?kboFyt=akR{0EZ|Vwg3PC diff --git a/wadsrc_bm/brightmaps/doom/PLAYF4F6.png b/wadsrc_bm/brightmaps/doom/PLAYF4F6.png index 0561fe21e15d19c850842d38a037605ea9cd0d47..9def391d76ad309fd6bdced89a46284a968f1e79 100644 GIT binary patch delta 270 zcmV+p0rCE3o&t~wvc{$UyLiI>1 z(QAMjqFzeUlMC8L>j-s3wZ&9%o$EEmo*AF?D?0711+}BzP?MA*+%enxj597%oP4vj z4|**~TUOC)j`~D3n603<-2wfe2B;Z!=W(EuxP5Z{lP-SdPrCf>tOiwf^_Oh`%)l!} UcGpKO000002uVdwM6N<$g2s?{T>t<8 delta 84 zcmbQolr}-akCB;yfkF3K^9LZs7T^=&`v3obAT#vP0hKX$y85}Sb4q9e05_Z&k^lez diff --git a/wadsrc_bm/brightmaps/doom/PLAYF5.png b/wadsrc_bm/brightmaps/doom/PLAYF5.png new file mode 100644 index 0000000000000000000000000000000000000000..520a9abfa2f0e791d024d6c491ab7b02d6847279 GIT binary patch literal 275 zcmV+u0qp*XP)CcnkB@P2aXmdfQBhHwo11`ufVa1|X=!O@W@bo8NB{r;cjqnw0001< zNklQEWRQe*IVnz+b-F6j>oN&yZa zo6v&ZWjsKAl^YTXS{gF;vbUSWq{98YoPhlUl?wfwewCcTBg2(9a58c0WZD9~H>YCL ZeLn6_RWj162DShI002ovPDHLkV1h*EZS(*D literal 0 HcmV?d00001 diff --git a/wadsrc_bm/brightmaps/doom/POSSE1.png b/wadsrc_bm/brightmaps/doom/POSSE1.png index 378f264b24cc23c466820aad12d8485fcf570f68..5983ef59d87d9b11f166aec595d2856a82e3ebb5 100644 GIT binary patch delta 79 zcmX@Ym^eYglaZN$fk9*2iBurP7T^=&T2WB}WUAVW$^j`cPZ!6Kid)G){{R2a$hPK4 fUX0M;ISdRQo6kn*&a#OCs$lSR^>bP0l+XkKc_kQT literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^Qb26Z!3HFSDx%bY6lZ})WHAE+-(e7DJf6QI1t?hN z>Eaj?;r{lLAs2%pkITh3Ym0y1z3M0ZRNV0cx0sUi2TOmKJ^%G0a|4d*NnX0D6<2&? z!5Nu2+xh09c}s3DiJa<^@o-U3d6}OUq@H6pCa0J}v e)RtW8v6!LtjE$A={L~1b3IVGd004|hL_t(& zL+#kR4!|G`Ls9zI{C!_49a+Fbq*IBpQFOs};ApL#_bT%}%~n}u8huYd6{?z@?o{u6 zWhWhj6?(IOkwuoz27`vc2#ml8?7PvV#E_`Vx)M~~6jD@?Lq&GH93@HYuFYq66CL%C zMb_|b_%?iJU()$3pXIZBme2CpKMRC{P!I}2p;{28 diff --git a/wadsrc_bm/brightmaps/doom/POSSE3E7.png b/wadsrc_bm/brightmaps/doom/POSSE3E7.png index b54b0723539e157abe7c62ba6bfdc7902f6227a9..464bb94dc98bae871f4ad707b78e2c86913ed4a3 100644 GIT binary patch delta 80 zcmey)m^4Aci;C2#NE3*~}L-b=lJ9mL$-R5R*^sl^cSJ^;ZKjAEM4djtRg002ovPDHLkV1j%h BPNx6> delta 365 zcmV-z0h0dL0sI1x7=H)`0002}x-l34000SaNLh0L01m?d01m?e$8V@)0003lNkluKicKbQ2!(E75otOvJG+|rGRD{~iVcC4 zieez=V5zKr6lS+^bm4jrI`r>0f;PpP4YP3=hL{lYI|i%k+JE+aj|hh5U@);!fKAi5 zYo@I@ECg7V#jfibTLcF-&$E??0H4FEsL%=zgX)QXHU&K;8HTA%q!2uP`D300000 LNkvXXu0mjf?F*RV diff --git a/wadsrc_bm/brightmaps/doom/POSSF2F8.png b/wadsrc_bm/brightmaps/doom/POSSF2F8.png index 73417044fa24b3f216fef1945c063b033efdae03..0ecd256bca209001f7cc0bbbf2165907aa0202ae 100644 GIT binary patch delta 216 zcmV;}04M*o1Ly&e7=Hu<0001?B@OWa001gbOjJcfLqjVoD>*qiJUl!xF)<}2B_Sap zC@3f`EiD`z95ppHKtMqM|Nj600F~%A8~^|Sc}YY;RCwBBxQi8BV4$=39V(e~(Uv6j zw@5XA0g2+Zq?vzZFPeBHgz`p+pE(O+Ooi|_!_B#K=FHuT+EGyMZC$wdnKM8Qv-YBT z7bZS^ErxqQ=G^F<3*vO6xD#Y=7?`sQyZf)T4Pg2iuHwxl69r;mmkMdARGB_q&{ Sx*n+j00005YK)JOW~ebfMw+3<_~?+vEimD&s3RJ$AiT`U8=;P9 z+_e+_WzrE%5WXNX4z>h*n_(o&{G<9emhxPC~HZXT~zw%qgy$8_KI3a(PYHnbo(l ziriZkb)*qPU%o93shE4mi!Y`*cDNOb~ zct{{?)-&yh``^naSo+;A3%GOqD)Zx+J!{|Z(sT7aX8vySY5$yy;yh z<<#2ors<>Xs=f((MV8vsH+Gy}7<6{7v^(EZt?;b5GFnSZ@?FMd>;`0Ch zf1r|8uSJ4@l#{26V@SoV|Qji delta 299 zcmV+`0o4Ab0lET^7=H)`0002|AF8kb000SaNLh0L01m?d01m?e$8V@)0002&Nkl1%0tK-a$fh&V37_A%BGKy|-G0-Y;9f08L#} z(pp2)nThB6)3~>PF~*s|*fWnRua9@3wN~t^$Up9RXv5; zii*~w3y?%f6iomfk2y-BXaeYXhvjHW2~EfvPe*itE?*~ffmZJ@p~Lz=KbsFD${f`{ xM`*qEhc4|WQ6!0wb)7`5#H9TsDiJcRe*xYcr7c=747&gT002ovPDHLkV1n;We|i7_ diff --git a/wadsrc_bm/brightmaps/doom/POSSF5.png b/wadsrc_bm/brightmaps/doom/POSSF5.png index 765fb3924bbc22d19910b406c18741f10bd5bd54..5ca4b31699f59325ef2ba799a1ae13a8db59cfe1 100644 GIT binary patch delta 109 zcmaFNSU*7`orRf!fuY{$svVFL3h)VWEi5c-Y-~(SObiGJ2oDbj3XAS)*$<>NJY5_^ zDsClnxH9pSJnv8{Zkk}pxa>q#L2*D&nQEV~5rduV(+P(d8T$60in+V(-zA_P22WQ% Jmvv4FO#n@HB@+Mu delta 210 zcmV;@04@K2;{lKue+UKu004~~$PEAh010qNS#tmY4#NNd4#NS*Z>VGd005OqL_t(o zN9~nC4uBvG1o4Nxapnnq0xw7F$q>vEY!gY=gxliSS=1w?)UFkJ86a|n-u$)``Sb)# z>75FF>1h){t@Uk6zX?hyBZwbBRhx+P5I=yuwEF`FpB`UDOgZP+4)zCF2i+9D9fX%m zz{-DtB|`%+u;Tr>hX7~*2G{`=K?5+rQ$$7301WUHQ4ur%1OGlnCkr9BY9;;rx&QzG M07*qoM6N<$f=8fGGynhq diff --git a/wadsrc_bm/brightmaps/doom/SMRTA0.png b/wadsrc_bm/brightmaps/doom/SMRTA0.png index fb0af9536d0dfc0d39dc95421dc5aa650ed1c1ee..49867963e05a5e642e05b16f38426e985401968d 100644 GIT binary patch delta 224 zcmV<603ZMS0`LKl7=Hu<0000fp3qYO001yhOjJc&TwFdrK4W8JBO@bkZ*NLUN>x== zS65d~PEIQ;D>^zlH8nLsK|wJwF#rGn|NsA+sm)#h004VQL_t(|+GF?+1s@njSwoZc z?)`ta|Nq~;d%*DS-FsF4cM#UQL^T+zCl$sz)dORlu7QieSy^kU;H)mVES$CXES$9( z#<~p`TYDPLf{WdSi`_m2W6fO)W8K^dSGN}~i-mRfE*>3k?(RJYVVyWp&;wyzo_o_4 a$N~W0u4+1~`&XO*0000KDBBM2m7Ml)?ohLCu%{{+!q55!RNa zHXx}jO9jM89CC2LJvkKg*N#Y`_5_Ub-#I z5{6+o{@qmXtGRE8hRyRFx~>brwv)(fF}?6MK0mV>Qq^hbgj$>40hM*(~0ssX-7@L#`1W(KmZLArBsbyKlkmya}2m}?3O%PUR za*`4hNH}IC8fIelgo(*Yv?t~*E{Y;5k+96W0|-;;%@7I6Y^;PMNoP{>6^LxH^d==t z{=KpC(dpf}sI&W@3|^KBPfvNArs=Bc?;)(7=Xvx)Ol8cyPxl_6ddMG*;h$i8Iq6der6E3;rf2FO{dmQ_=wqRXaO1A^vf=7sFc_Jps zSb>OV6;m-}QzS$_Zk delta 353 zcmV-n0iOQ&0r3Km8Gi-<003t$-o5|;010qNS#tmY4#NNd4#NS*Z>VGd00AROL_t(o zN9~p|4udcZMd?`>nHX3Ykk}9d8)9MO0Nj5{c~&h2K?`zPb#Ww=h$b)JKgP`?Y%NP| zKvG+l3Xn(~a&W*sIVOdB;-1_o6`m)aC!QyF9Pm83FAYIo5Kmbr=f&i%NIt;@gFf*j8 zsxVDc8U!Ut5CDV$gs~|ch2V)95*urVU}{;G5)!>B9EG5Qu?fQNEKX8_0u9HkB!-wh zVPUe8*c0;+7g|LjN;E9<=>Wo1dNV{rG8?PmXwsRKdzv2y4PC8WGT>Hff;(! zT_6MkJp@K*X$B`K5T1Z8i6C4dC#Z?NBY$fQS+OwbaLURr;6767-%IfVd;kNZUyr*l e;AW(4Yv~DPl|Y`5=z$6V0000VGd00A9IL_t(o zN9~p|3WP8aMcuPlT3Ogw2zFv&CpLCDfcLMmzQ`<*RUwmQs|iSgI4^&GGBGc|w<4zj z$Z17Ra8Pdm^T75&r~<16^T77O@k6j$Fb`}W{HhlAD#0~H`PQ3b%jx~@YS^vNj*fY})*1DKUSwE||A6jTagfXV}cvJ}J~sFty+ zs-QqpQLO?H095oUfTW=^QBsf%m8>8eAj>FyvH}88K??sjQ3@ztg@=Rn{A1xmIf>~x t($;l-I-Sn|W-ZGy$~uZH@o{002ovPDHLkV1gFUPtSm)`W)g#gMeQyy&M4dlp$vvh@1ffT%3ET>JqF(TF6i@jv%~s(e25n> j`EmWmA=m_v?cCb|AJR)Bk%(*v010qNS#tmY4#NNd4#NS*Z>VGd00AaRL_t(o zN9~uf3WG2ZhTCUxbaHTU5ZuJUOS!W1j2>3{Dw`OMRH8 z>0rs#yU%-P2oHu~kZapE0lfjpQRni7uIobEwqae@fFcv5@B07<0E$cy08P_`aU2C^ zhSYT(=6TM8pdYbfH0Nb4AGFx#%eg4bS5QVf#?!zZ&JeK-y5qx9j3-5 zA$juxpcS5;^0+L^r>cM4yzrtZ3h_ftWz4--`SJjZiYhJQ@gH;m0000bP0l+XkKc_kQT literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^Qb26Z!3HFSDx%bY6lZ})WHAE+-(e7DJf6QI1t?hN z>Eaj?;r{lLAs2%pkITh3Ym0y1z3M0ZRNV0cx0sUi2TOmKJ^%G0a|4d*NnX0D6<2&? z!5Nu2+xh09c}s3DiJamcTDqpkp-0wcy^N*L^+sI#CU$W}fdHL<%|9vvvxAoOT<#5)l z^x`M09Ny;i#fYw$dv@Y#rmdSTi$WKanYpjyx^nK>38T=)TQ_BPY6V!&#!#)glUrdU W@43TGzjJ}^VDNPHb6Mw<&;$U&hfk^i diff --git a/wadsrc_bm/brightmaps/doom/SPOSE3E7.png b/wadsrc_bm/brightmaps/doom/SPOSE3E7.png index 4d000fe701acf663986768824184ccd3ec018c1a..527486c477a037f0d7f6024e393690765493c518 100644 GIT binary patch delta 79 zcmaFHm^eYglaZML2qw=K1d?n4J|V6Z6%|0Hs=cTjkP`EBaSW-rmHdOBNnYYZa6PBC d*rOrK)lyX@7asOayZuPnLNZSfMVE^}7h#kJ3W^>@41PqrM< z*>rs$hw`6*_0uj0MLl}_UC=rG1<%}t)&IGYIHx6W`cF&ftp6|e(SZLm81dg_bw0RR zR{CcAK|!yX5-rAGly-8sYosO#oV(EdL`i1hlukzrXZ=ah-5=OCoqYLm)}a~efR1GF MboFyt=akR{01um45dZ)H diff --git a/wadsrc_bm/brightmaps/doom/SPOSF1.png b/wadsrc_bm/brightmaps/doom/SPOSF1.png index ca46b1913b63a8798ff004059cdfc6d1820df615..ba6a74c073402b8f76171722f4c0756cb93c335d 100644 GIT binary patch delta 256 zcmV+b0ssEU1CRoc7=Hu<0000Flq@R%001peOjJciMn+6bOlN0jQBhGI9v(kGKV4m2 zZ*OlpIyy8oGLPA0;EG+;3{{R30d*gFd0001v<6kwin2|3-r$O2l>6okrADDCK8MPEgO^KN?*jnvak*x-NDwms0000}f97=H)`0002}x-l34000SaNLh0L01m?d01m?e$8V@)0004cNkl zMGq@B04rt1Kt{n*S^Y!UFbo81n&!vsy+~zbGSXvMAbv3-L4R@?ZB{(P%V2QKWwh6k zK$(n@PF)h;u&@Gx=@51zsaM;!G|w|l(?ng@U0XABCm=9fI|5A03{H`abvlkCEz4re zqeL1EAfCatZM5&ZF%itHL9ZdH6MsewLxg9eu4`wM$GV_7)^#O7y_uboUN>Dp-Uztv z``#?nUPXMMkbk6BI-3oE*|6j51Wh2U(Ggw+le5vHtpP%90xj;IXFghNR|cMc&a<Z2>81v)mWVHiJ`Wv-E7SSEls&zr)EH;8D%qg5BraWhH&X(A>V_4AnzUKA!epT$U( k`0Sg`GeNd@X88(y0|G;!H#Lb5QUCw|07*qoM6N<$f{Y-$$p8QV diff --git a/wadsrc_bm/brightmaps/doom/SPOSF2F8.png b/wadsrc_bm/brightmaps/doom/SPOSF2F8.png index 55be2c92e8be1219f9aea6ee9b835ee28983bbde..e42e83b012ca711a88f57f70726fec361c7cd578 100644 GIT binary patch delta 241 zcmVPv9KXAGV}ClFBSb0E#?AlQ#q2i7`|LCP6($YN!%r( zX@HF-q5<^|h(%RB*s7}BIF4SJuIpHV^p$m&M$V`XY~S}R;D2MD4lI;xn&xJOpH`NH zdz}qkWdrS?2fKs12K#p9=XvgmqF^h7uCf3{RRA<$3tiYWP2C(n4~uOV+by}}>M7f{ zt(&IF2chcnK?ZyG+{lM6!VJT(AM7LK5x{^&1PE}o!CGTqfYo1?WeDIscMUQ`VR#e~ zSX-Y@T~c7Fx_@=zk0|cPac{bn0`<=s!4K8S&2%U a`^?jA^|90000psjdfS4A9Mdsbzd-P6KZ*a<-#woK0r_4T_GUw>ypK~~6 z=HZfgxeu4j>vN>1-qYu(nHNsulsQMY6fzJp0A;hn_UZ$uP5=M^07*qoM6N<$g4NV+ Al>h($ delta 428 zcmV;d0aN~p0=ff`7=H)`0002!4Fd`Q000SaNLh0L01m?d01m?e$8V@)0004ONklDF0KVM9S2H^78E1O&>{;$TZCl zg|I%&1H2;1x& zU`2L?<{CnqwB0I)SQFq};9Mw^Zar`&6aaj*;!S`H9R3FI z7T1__uXKyV2G<8{PTVS-%$pzq#0C)YTiN%WxQZRa$tB&n;wv`MLQHJmBt$%CKe3;! W&6>0@`AGx-0000xl diff --git a/wadsrc_bm/brightmaps/doom/SPOSF4F6.png b/wadsrc_bm/brightmaps/doom/SPOSF4F6.png index 2729eaa723f44bb354ff6d19dd0c85b362545b55..d8445c71d9b15bda80e5ef42e7a88c32e24adea9 100644 GIT binary patch delta 229 zcmV~1N8xr7=Hu<0002x1`L1z001XYOjJceLPAVTOf)n!U0q#gXJ>hNd01Fj zWMpJNKR-G;IxH+K|Ns90001#O@#g>l0EtONK~#9!&C<&WfG`k6QR}b+d$I&eumZ7! zR3PbKTnK*9(V`593&|ylabcE+a_9`99z8Yn;FNdMXjH!V#2Zo)LykgHJD&R-BIig>v`tTIs9jO f-%0(&G$V8em}I00000NkvXXu0mjf?Oa~D delta 384 zcmV-`0e}AW0gnTa7=H)`0000NC=s&&000SaNLh0L01m?d01m?e$8V@)0003&Nkl-xTr34f}u$q>yKy>X}@^E{7f z>DJzu-G+czm>UCPkY!m~**u-QZFf-=jvlgU8b@z;o(4|jtmI(ZqSx0>E=!#8x??mdu!l*b_VT74+w9u)(o0H)+mKx%B$JUe}O5W`t} zi1vBtKl1>GX!BLBNuyCXcUXoD)ujqYWJty4V!eX`oCFg5wV_m7+DBG0L1Lc`lait8 eG%5K%N?rk{Kch+{pNr@K0000Ym diff --git a/wadsrc_bm/brightmaps/doom/SPOSF5.png b/wadsrc_bm/brightmaps/doom/SPOSF5.png index cfbfd0201e46d021e39aa72f1fa9da9eb8db5285..2fdf53286734b141a9696308bc3011b886dea42a 100644 GIT binary patch delta 139 zcmbQrG@EgPL_G^L0|P_7(N#MjB^}@s;+mM47#<#OX=&-}>uYOktFN!`;o(tLRRxsi zdA8>skh1o4aSW-rmHfhiiDycI$^?sC*&P-cY$*!GQ2|pbA3Rae&USxTprEbo`phI- oK{}!FOy<#(FD0Jx2(UBsH8`E9{(H>~Xbgj=tDg(YI;Vst0Db~3-~a#s delta 261 zcmV+g0s8)%0hI!f7=H)`0001u9LNm-000SaNLh0L01m?d01m?e$8V@)0002TNklJkvn3?$ve%|-`A_*~So4($`q$X^IL(i?cg%I}df<6R5F6iMu zJILF=0MJ_F4Cob=QnwxqSB*wTxYykwRweBgU39$!E$oyMMK3J-nLlve2x;-2KSL6WcWZ|aBGw=+| zrpxT0yG$Iw8ej{g^X?g_RjM~ivvB}xfGv=^_V+-oQoT`{4HobLHKn`E3#V;!00000 LNkvXXu0mjf*#&N$ diff --git a/wadsrc_bm/brightmaps/doom/TREDA0.png b/wadsrc_bm/brightmaps/doom/TREDA0.png index e6ac68c9cd6bc5dc13339c6bdbb5fe0682b8fd12..d7318f94a1ab53fdaac59df5de9b978658860bd7 100644 GIT binary patch delta 595 zcmV-Z0<8U=2H^yd8Gi!+001;Cl3M@(09{Z_R7Ev4HB(bljEsz#nVEoqfK5$JLqkJQ zP*8`5hj({(a&mGnFE4IxZbwH)N=iy)Wo2q=YFk@dPEJlZI5;{wI$&U6Gcz++S64nh zK1D@EK|w)GOiVmHJpcdy0002BW}V~!00F;AL_t(|+SQX`e}94?5Qp7rHB$r>M7YE6 z|1o#qYMPxje|s}tgy$3Y{2?mF*@*LEEJk+Rp}1q!KG)t=I}$Dz{eg=Ji{d7V4^e!E zBHu9nvLPQM-m{ltMAj9bqm70?>=hJWL6L75ce$?cA&PVK{mReT6%AC2lW%Am)=1DA zFEW}nq}q$zjen*U8U~S&poc|6YElgiy`zun+VO@G`w)q&QLNez-&Q%|xI#j#(by(W zEBrJs52~eP&FR$8)UHmx9ax zhC_ZMh6$c?HJ*zRJR8G%47*(HD!t`m_tmp5!@K->!+*v8$^rR(dEVu=T!ufxz`NMf zu;Jg{4d@&oJVs6__Xug{opu0D-yv5@?aITU1X-#(q%64d{k;URHOvv#wM8fES_0Zj zjW?tncA$d39FG@`3OdZff_g~}7$kHZ&|8!mK}+Q_vT%Ou5R^r<4zW60unA#Z(v!5b z-8{fiK}=anEU*jV^7%=U9K)ebRT35>MVKUd`Ua*d1k4FxlH?eaE>+Nd?+cP?ilHFc h0G7v#U@gop{TDjW5`67;tI+@e002ovPDHLkV1gRK3KjqW delta 785 zcmV+s1Md9c1f2$u8Gix*004}U^*#Uq010qNS#tmY4#NNd4#NS*Z>VGd00PfRL_t(o zN9~s}s~SNRh2KB25J5x)5fMa1u&}VPu}NWJW1B8b{y=`M`QDyzGmJ)O=ba=Q$Ss!H z-MQbMd+yBm-rHM2+-|pmS$}s*S2cE3m%8;YsOzLgiRz@Zw12Z6fl;gt2S{N!G=gOy zW~`42yvO;8^$wbg)qY6hL(*ay+laP$oo#){s$V6)!gYq|6!?TyQ}1(}pV)eP{5kJh zAEht7DZ))W@)yPvl`=J zYa9tg$dM>+^?$sy*eIme>yd+Bkr}tVDy=u27sT~?jV__Zs=<*%@@BIEhieRi6}Z=} zNPQ6wX+#ZQ2di4dbi-$@P+jVh98}XwcZf1cQ)XV#9p*dq-mgSV1)3QE P015yANkvXXu0mjflN5F_ diff --git a/wadsrc_bm/brightmaps/doom/TREDB0.png b/wadsrc_bm/brightmaps/doom/TREDB0.png index e287aa47bb591aff693616e6b8e0dda0b86c9ed6..15ba5a66575fa46100c3f21069e4a1424656f8ef 100644 GIT binary patch delta 614 zcmV-s0-61*2J{4w8Gi!+003H$Wi zLqmXofKyXbkdTmleSI%4FK%vba&mGrGc#pnWol|_M@L6nTU$j%MKv`wI5;>uIyz2H zPGDeQN=iysS64wnK|Ve{OiWBXJUsvZ{{R30YaAtm0005WNq7 z5(|%g3v%;mx_{Lh6hb@cc(O(z7Qyj5{wV&+vPH%0y-j3`VqxC3DW-_y2DR-LjV5_a ztLNwXdJQLY*Q?`)*))I&hQ|Wd-ODTp|CAvNPC(QRgMhwBqh;qx*e4M#RlLz^s-C54eS43Z@{^|fjuDMK7yu!J)}T`0z^ZQZj@i6WM6@(c(Z5|qVmGC+H$$mK;Q-1vZRc9@2mTrB~uI_R^F>5%tnqdOVs=gN-OVa zN{F*$ioxm93j0T0m`rnw29phOeZ7R%;#1TA0p?#Fraw7L0RR9107*qoM6N<$f*S7` A>i_@% delta 799 zcmV+)1K|Ah1gi#+8Gix*0081F0#E<|010qNS#tmY4#NNd4#NS*Z>VGd00P}fL_t(o zN9~uviW)%>hWCv;EP{v#A|i;2;2{S+h~OoM9K@@T2gqTsd0O-Bd`YL7F^)4+hlEWX zhIXdw>#D!1W_Dj*n%M1jYqI_S-d_rQK;gnt78HgHZpz5GV1EVUQnCTuybfJ)W}5Qt zgv(i-Kz3OeuFC2Z-CbNu;-6T-xb~wK8Q=p}n}PesaScjbn`s02B3XCmw9~Xzo#*rU zY&L88I>2o_9w%p5)9Dl(i*z$>s2`FXm&cV8^Ydzm;uecVHAjhD8{=@m@&q}CpWbhP zb8D~HGsfhZet+^P7vxd0`MyM4tyaV(T2>pJF|OBZaOlM%u@dg7I;4gWM`&0Z9H-%y z>m|5(N1jLnO18}3!sNe5{ z!vb)xudlM;2H*&7GMR9N8-Rl)4hz7MX0Ub!7?Ug&pnpY(F$M>Vfeq7+Gzkr2OdAu| zcDrS4W7?SLcDr3zDaa9{AP@ukPVKmcY4@qHu;`A*Bc%xIJGG0MS?51YJL*8+g=t5o zvSPHO2)+Vh-o{L;Jsj3|erMqZOa(#h_xmvIoY}AsW188RA)|-YE^I0eheM=xAL?5X zIkSK9w|`=}Tn+{Ul4Ze*fNQ6*#UIw^!i2WO@u|k2j^pYYmUMnLfZ}kuT+Zk7>2z}a zy|C-`3XYpRBG_k%An;_cjD_{L!o*=ba$QeE?=OYn!x|jnc!z}r;Sm9H=h5q4f!IMQ zaU&i7*unE_VJ!H`1|C^)&nru5?KqqVo)5@a@PCn>{CVn5`-TNSt^3d1#VOWiv*D}i z$;N2b=4kjB|Cq8<7p}i?ibeU-GfvafNK0mY*x)vCiu*35V)?f@>bw<}WGa=y=#rrB z%%yzKH_2a>Nzc+5X}^LRO1a90U6QF(id4;fr_eh+C@~-SQYp;KDyg7FukUAf=!u)2 dp0N2j+u z428Mfv#`_)c$dukKjf7m&4hrMHq)to;N=_X92r&PBJ$0?8rir<`JpTp<=@uIeAa_n zc?a<}9z^-aC{MA4a$d4(MSh0H%xPslE3eo$P-f8v*+M&6Uu=Q2-a?6P#6b&M(zrr` zatcd}EILOqRe#e$r%S~vN~sjff;LN(a+?C((Wm{YctwRhi$c~YxAs}vHg6*#X0$MG zi;fj~UY9R#$yqoY0xfnm05(~QD_G7UvBV6vNhUA|dt(^%X$&%`kNqcuV(fd)#*S8u zTtN(mLqW4z(P*ya-UiM_Jh3)Hz{qMvR_kQhtSaniv44H_td_B?R@m!{{mUBpzCzGk z!GC*NhJVAzYq7840gevjB=PDpQSQVHUaAz;cQa zCxVGd00QhuL_t(o zN9C8zisC>Nh5LKdGl zGVb+ZnF4Ve3))u9T?fRTK4mb`OWcl%;tQxrY1-V&HJV z(g0cFR`U&T+JD+=wHQn$lkkjNmLeh5>cT&Wv)K$?LW`Aylg9ac4vu^=2-cTyNW;pw z>dLYMw=`ees+#5bTDTypf~1T##*{l5=(?=fCplchGC}E+gUFzXp)q|c^qyWJwq)_2OC=gjK-+j2)8^sVsZ zj!6A|XL}oSTGhk0zSF^gYd95v*6X$T%-TMTdf(~&ZU~khR_<)4Vz=8lbN63;D|G(N z?>kSUM1S8I&g^tL?RLAX)rtWx0xXosRKOM(aV_2ed+W?Bg<#Iue zD2#tSi~%_2wOW^`=r~x?a=l)EK8%3`vUEHiYXwfe*^1%A7~J7-&w|FD{N8TTvSb(1R47GMGu2Kxk%8Gi!+0094SLH+;$0ANr|R7F`?SzljYjEszGe<{9ZfU|?5Q zS4v7sK|w)2K0ZuLOgub1|Ns90004(M?>ztj0oF-GK~#9!y?>MGlH(u@ggr+Rk24s9 z%`M^gf6E1&b@`Gfo0?jFgov-y-6EtKmn$xdYTT?yBdY6M@OQgjL2+$XZ3mA3ej6`` ze?f2=a3@Hk7)h_VU{9m*hptF>#gndhfFg<3N;Ev3woJo1m z6+=2A)_-2PW~nE$HA=N-MNKjp9+EQ(A&W(pzN}G*O>pTfeKda+XO!86WwJ-H z(HE(k5^>xi#`b8`*+(-u?d#jnDVe)oUE1{S0qCI9n!);QBo>JOmkP=e2z#Xn^sN*U zsQ3LhfxPc~D#nSI_mm(yMIk3iFRvsua<3f~!{5jl&VQjNy*%l4b7a@snHM=%PkJeR zp}gGLT;#7Tk)F#rNeNbGUW&d$PkWKKVf_Dl8}4`C1`HMuj*;y;dxV6QR#*TlHptdo z*!tt623~g?)ETh#`+E&wT38}%TZ5LjEeACB7H1?Z4$ygV|N6QM>|nrRSS2kk06m9c zfOLvnAZ7^O*@6sw-UfuuAR3FSwrWg9Tuq;bmvn~IdB#R>=SCzBGxVzJDI)tDko zCXX1HEAO!+gvq4DNa=C~!(%SUPE(8yvKzqqdJ&9)#iRcNOfMc(EKqUl00000NkvXX Hu0mjfup$&P delta 812 zcmV+{1JnHb1h@u}8Gix*002kLCYt~N010qNS#tmY4#NNd4#NS*Z>VGd00QbsL_t(o zN9C8ziW)%_h3Ac2h#(?@NDxFta3SJCH*T_U;l^zqAa9WeHDCG+H(b;U32!}MH4UV^wmii@})MfXp7oBD(Q-NhHV?y33m{6A$ z^91D@(GrxtIfx7zD=H_9Mk7F6Db|1+kH>?-032b!^?&<)vEUkTNaHx*3fF*xB^+VE zQKs2D3G@_82UsF#^x$BzVQt<~Cek2!mNB~6v#Qt7S=4Q}TU#s8MbHq49(9xn%jO-$ z!m_D59*=(SJbPCA-{u{C(5Kep9Zm4BJKJf@Zq*vLtvhWDxQ1N;Xusc^*PZRdsPj(e zyP;Utuz$R>?TXXsp`c~-dw0&pa^;@xAE{DV6ZntB=LBO@sN+uuH_aX#si8~w) z7zc=^1ZRrj%x({=~HC z%%_zyOJ{f4rmPiGml>K``KR8nziiXPR>XW6Dk*uFB|EbzYejT3ohj&pRV`w=;j>n# qE+utI4yx&OcZf1cQ)XUK3G*9Am~+C7$rlU&0000 zLqmXofKyXbkdTmleSI%4FK%vba&mGrGc#pnWol|_M@L6nTU$j%MKv`wI5;>uIyz2H zPGDeQN=iysS64wnK|Ve{OiWBXJUsvZ{{R30YaAtm0005aNqXxVQ zU@MgNBo-d~7JuaC({!tMD1>&>@xvB{SOmxK_@nqM%N7;0w>FVIiiLU8rkEm*JJhy& zG@9fwt)A!Y^%_p*u2;vC*)@O(hQ|W7-ODTp|CAvNPC(QRgMdDVfdtg~?o&YCcbydD z#O1woAh|(8PKz!-XsP7xW~3Nii7}ibFS@+w0uXm)=6}Q`*42rw;U>Bqh;qx< zx{T41>(9v@iVeVb=w+998`l3`Z@_bV1A9QiV+2hDM@WGN1&E-&A-GgETwY#stgGf5 z$}K|v{+=V+9M_1-(h}5VStHr1qy;IU9viQ!kB>@GgFQ{cY=ov(NWR9tM}AASB2;w) z6SDYv>thKUOJsqlc(rH~qVmE|wdHcBfxrv2Wl0(J-dFcKOQsk=th`rAn2j7^mZH750z1Fq!5U4JI4n_Ie4e#Z}XP0Vk#|XAmk$bN~PV07*qoM6N<$ Eg5Eh8y#N3J delta 807 zcmV+?1K9ld1hWQ^8Gix*004}U^*#Uq010qNS#tmY4#NNd4#NS*Z>VGd00QMnL_t(o zN9~uviW)%>hWCv;h#(?@hzO!0c*sEyB6!Im2k|Q80dm-Dp4NOjU+6S5uA?(WNZ75z z(9TqSUG-Pj%{1y|Bo{>9pbNfUwbMl!9SRCKGTh(#_;hKO{LW&nqY9`_&M|&FAxKjS{&&jUxcd z3uGI9djAQWu790Q#~f2+`pKhQkbA}Eeu=nTF3C%@tR^^fT&-5%(2GT4CEP=ONC!e3 zsbNiU>_%9wmpHR4-LF4%dKjr88OIn?I}L|NS;h(%SbsYcj4755a74&41_z6Q4bzS? zNeyDmF($9gX2aOV9AmQEZnt5rphk>_Knyr{YR5H9yDx=>Wp_9nXhqn$Q@fa%b^gP& zqYs?BFzu*RR*ZHu!B;@c+n8zf42PXNzq4=yrh=q)yIq)e&TQC+G0kkuka339E^I3H z`+cN#AAjmw5jnH}@V8>ISoHgSieXx&gN*<)@b?||Cr)w2-iP2#iD)b8M_&2rX{mKIOrx$ao?m=EdMq~o!7#W zOr=(sT@qB!T*|k4ll)bgj4bVu{1wzx%3U_?l1!ymq;BS(LT{WwiTS{nT47ywNd;|s l{UCYP!$`u6ELT*)`~v8BbHaE{wzmKP002ovPDHLkV1mIai{=0T diff --git a/wadsrc_bm/brightmaps/doom/TbluC0.png b/wadsrc_bm/brightmaps/doom/TbluC0.png index 0ef110aec933e16a7814b64bcb90d4720000fcc9..e1771d71c3e6d847c65c7639a67e66e5515477a0 100644 GIT binary patch delta 624 zcmV-$0+0Q|2L1$)8Gi!+001;Cl3M@(0BKN6R7GE3Us+jMm6erEO-+o9jC6E#Vq#)Y zP*9nfnL|TEcXxMFQ&WI|fMsQ6a&mGnFE4IxZZk78M@L6nTU%;sYDGmwH8nLjI5;{w zI!;bbS65eHU|>p0N2j+u z428Mfvk<5k@GhD6f5^dM&XJ;OxH|bd7^;CiHz+@lm7iU3o_0qT zi}JIzGN1LJR?ZL1Fh7X$-6)S|4dpy%)q>oIhRkVYJ}WQSFHmOD3Ry!NSwC!sv|dAr zUc^ody3(*ff`4)fOS3GBqZq4cq0_nI1*KGqWkDZvlyZ{-z0pteQ}KcddliK&QEu$3 zwoP6~LQH68-WDAz^mNZBZ^>EM?E+nPu>dw%Iu|hCLt=?pY?Dl25O&5e=-U`%P#3#z z2F2KQoQ(~w7`cKN42ObdwW85n%bg9Jjd);fgn*INihr!u!LnIZ*wAA8>R2sfS*@_u z7yB=3VL(L2j?4+kbuS zLG9HIQXbs#<>d(ATewEpwjN#Gwl$zXwRl0&VGkT`z6W~6 zS|Z2~ER1CU0000< KMNUMnLSTZ|m=&-9 delta 822 zcmV-61IhgU1i}W88Gix*004}U^*#Uq010qNS#tmY4#NNd4#NS*Z>VGd00Q($L_t(o zN9C8jYBE6-hI2<2B8Z3}5(H5ZEJQ36!6t=;2)15;ZHip2dEy&RhTX*d$tz#rJH;|P zGw0d&{LHRCJ(aNQ^;%-rzuodzH9kbwL!i$3{ds|&U>_3ZLw}%#c%{px&IU?Db3$R6 zafTuY;g@EP)G1IbgTMe8hFw@RVF%dg{%tdoUEU! zS^sR5QCHz?2v^bC0GNl+wHbmEpV6t*K9V$f@{Dbjn@HJxCR_7;eRj#jxf!+lR!_fRDc#iqX!3z z18d72VImEpr;X9YnN_@o%A#&QpW8}-E{uXe^r$0DShm~|Ea|hUTd&thv-O>F=Q*=F z|F+yw2Yo9%xg%14-`U>AoL2R)t?zU&;2KT^pyhIDKC`wDquzIVzZ-(3hm||qsn~2b z&fNW1-+v07fAjm!(&+NQAWQrGzEt4ko2?i=jKS@8JIydq zL0CE*4$R#9@vYFyCoADXclciBnD@Mu3_+!gX<(KOhj~8&MAP4$Ub<{jF7*+W-In07*qoM6N<$g8RmS AC;$Ke diff --git a/wadsrc_bm/doomdefs.bm b/wadsrc_bm/doomdefs.bm index 7e302282..7d970d7e 100644 --- a/wadsrc_bm/doomdefs.bm +++ b/wadsrc_bm/doomdefs.bm @@ -40,6 +40,7 @@ brightmap sprite PLAYF4F6 brightmap sprite PLAYF5 { + map "brightmaps/doom/PLAYf5.png" iwad disablefullbright } @@ -74,6 +75,7 @@ brightmap sprite POSSF4F6 brightmap sprite POSSF5 { + map "brightmaps/doom/possf5.png" iwad disablefullbright } @@ -109,6 +111,7 @@ brightmap sprite SPOSF4F6 brightmap sprite SPOSF5 { iwad + map "brightmaps/doom/SPOSf5.png" disablefullbright } @@ -141,6 +144,7 @@ brightmap sprite CPOSE4 brightmap sprite CPOSE5 { + map "brightmaps/doom/CPOSE5.png" iwad disablefullbright } @@ -189,12 +193,14 @@ brightmap sprite CPOSF3 brightmap sprite CPOSF4 { + map "brightmaps/doom/CPOSF4.png" iwad disablefullbright } brightmap sprite CPOSF5 { + map "brightmaps/doom/CPOSF5.png" iwad disablefullbright } @@ -1076,6 +1082,7 @@ brightmap sprite FATTH4H6 brightmap sprite FATTH5 { + map "brightmaps/doom/FATTH5.png" iwad disablefullbright } diff --git a/wadsrc_bm/zdoom.lst b/wadsrc_bm/zdoom.lst index f3167356..5eee3862 100644 --- a/wadsrc_bm/zdoom.lst +++ b/wadsrc_bm/zdoom.lst @@ -182,6 +182,23 @@ brightmaps/doom/TREDB0.png brightmaps/doom/TREDB0.png brightmaps/doom/TREDC0.png brightmaps/doom/TREDC0.png brightmaps/doom/TREDD0.png brightmaps/doom/TREDD0.png +brightmaps/doom/BON2B0.png brightmaps/doom/BON2B0.png +brightmaps/doom/BON2C0.png brightmaps/doom/BON2C0.png +brightmaps/doom/BON2D0.png brightmaps/doom/BON2D0.png +brightmaps/doom/FCANA0.png brightmaps/doom/FCANA0.png +brightmaps/doom/FCANB0.png brightmaps/doom/FCANB0.png +brightmaps/doom/FCANC0.png brightmaps/doom/FCANC0.png +brightmaps/doom/SMRTA0.png brightmaps/doom/SMRTA0.png +brightmaps/doom/SMRTB0.png brightmaps/doom/SMRTB0.png +brightmaps/doom/SMRTC0.png brightmaps/doom/SMRTC0.png +brightmaps/doom/SMRTD0.png brightmaps/doom/SMRTD0.png +brightmaps/doom/TbluB0.png brightmaps/doom/TbluB0.png +brightmaps/doom/TbluC0.png brightmaps/doom/TbluC0.png +brightmaps/doom/TREDA0.png brightmaps/doom/TREDA0.png +brightmaps/doom/TREDB0.png brightmaps/doom/TREDB0.png +brightmaps/doom/TREDC0.png brightmaps/doom/TREDC0.png +brightmaps/doom/TREDD0.png brightmaps/doom/TREDD0.png + brightmaps/heretic/BEASI1.png brightmaps/heretic/BEASI1.png brightmaps/heretic/BEASI2I8.png brightmaps/heretic/BEASI2I8.png brightmaps/heretic/BEASI3I7.png brightmaps/heretic/BEASI3I7.png