diff options
Diffstat (limited to 'engines/parallaction')
30 files changed, 985 insertions, 150 deletions
diff --git a/engines/parallaction/adlib.cpp b/engines/parallaction/adlib.cpp new file mode 100644 index 0000000000..134e5cfbf3 --- /dev/null +++ b/engines/parallaction/adlib.cpp @@ -0,0 +1,808 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/system.h" + +#include "audio/fmopl.h" +#include "audio/mpu401.h" +#include "audio/softsynth/emumidi.h" + +namespace Parallaction { + +const uint kNumVoices = 9; +// adlib FM voices 0-5 +const uint kNumMelodic = 6; +// adlib FM voice 6 and 7-8 +const uint kNumPercussion = 5; + +// mask for maximum volume level +#define LEVEL_MASK 0x7f + +struct OPLOperator { + uint8 characteristic; // amplitude modulation, vibrato, envelope, keyboard scaling, modulator frequency + uint8 levels; + uint8 attackDecay; + uint8 sustainRelease; + uint8 waveform; +}; + +struct MelodicProgram { + OPLOperator op[2]; + uint8 feedbackAlgo; +}; + +struct PercussionNote { + OPLOperator op[2]; + uint8 feedbackAlgo; + uint8 percussion; + uint8 valid; + uint16 frequency; + uint8 octave; +}; + +static const MelodicProgram melodicPrograms[128] = { + {{{ 0x1, 0x51, 0xf2, 0xb2, 0x0 }, { 0x11, 0x0, 0xf2, 0xa2, 0x0 }}, 0x0 }, + {{{ 0xc2, 0x4b, 0xf1, 0x53, 0x0 }, { 0xd2, 0x0, 0xf2, 0x74, 0x0 }}, 0x4 }, + {{{ 0x81, 0x9d, 0xf2, 0x74, 0x0 }, { 0x13, 0x0, 0xf2, 0xf1, 0x0 }}, 0x6 }, + {{{ 0x3, 0x4f, 0xf1, 0x53, 0x0 }, { 0x17, 0x3, 0xf2, 0x74, 0x0 }}, 0x6 }, + {{{ 0xd1, 0x81, 0x81, 0x73, 0x2 }, { 0xd4, 0x0, 0xe1, 0x34, 0x0 }}, 0x3 }, + {{{ 0x1, 0x0, 0x94, 0xa6, 0x0 }, { 0x2, 0x0, 0x83, 0x26, 0x0 }}, 0x1 }, + {{{ 0xf3, 0x84, 0x81, 0x2, 0x1 }, { 0x55, 0x80, 0xdd, 0x3, 0x0 }}, 0x4 }, + {{{ 0x5, 0x8a, 0xf2, 0x26, 0x0 }, { 0x1, 0x80, 0xf3, 0x48, 0x0 }}, 0x0 }, + {{{ 0x32, 0x0, 0xb1, 0x14, 0x0 }, { 0x12, 0x0, 0xfd, 0x36, 0x0 }}, 0x3 }, + {{{ 0x1, 0x0, 0x82, 0xa, 0x2 }, { 0x2, 0x0, 0x85, 0x15, 0x0 }}, 0x3 }, + {{{ 0xd1, 0x1, 0x97, 0xaa, 0x0 }, { 0x4, 0xd, 0xf3, 0xa5, 0x1 }}, 0x9 }, + {{{ 0x17, 0x0, 0xf2, 0x62, 0x0 }, { 0x12, 0x0, 0xf2, 0x72, 0x0 }}, 0x8 }, + {{{ 0x6, 0x0, 0xff, 0xf4, 0x0 }, { 0xc4, 0x0, 0xf8, 0xb5, 0x0 }}, 0xe }, + {{{ 0xc0, 0x81, 0xf2, 0x13, 0x2 }, { 0xc0, 0xc1, 0xf3, 0x14, 0x2 }}, 0xb }, + {{{ 0x44, 0x53, 0xf5, 0x31, 0x0 }, { 0x60, 0x80, 0xfd, 0x22, 0x0 }}, 0x6 }, + {{{ 0xe0, 0x80, 0xf4, 0xf2, 0x0 }, { 0x61, 0x0, 0xf2, 0x6, 0x0 }}, 0x8 }, + {{{ 0xc1, 0x6, 0x83, 0x23, 0x0 }, { 0xc1, 0x4, 0xf0, 0x26, 0x0 }}, 0x1 }, + {{{ 0x26, 0x0, 0xf4, 0xb6, 0x0 }, { 0x21, 0x0, 0x81, 0x4b, 0x0 }}, 0x1 }, + {{{ 0x24, 0x80, 0xff, 0xf, 0x0 }, { 0x21, 0x80, 0xff, 0xf, 0x0 }}, 0x1 }, + {{{ 0x24, 0x4f, 0xf2, 0xb, 0x0 }, { 0x31, 0x0, 0x52, 0xb, 0x0 }}, 0xb }, + {{{ 0x31, 0x8, 0x81, 0xb, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0x0 }, + {{{ 0x70, 0xc5, 0x52, 0x11, 0x1 }, { 0x71, 0x80, 0x31, 0xfe, 0x1 }}, 0x0 }, + {{{ 0x51, 0x88, 0x10, 0xf0, 0x0 }, { 0x42, 0x83, 0x40, 0xfc, 0x0 }}, 0x8 }, + {{{ 0xf0, 0xd9, 0x81, 0x3, 0x0 }, { 0xb1, 0x80, 0xf1, 0x5, 0x0 }}, 0xa }, + {{{ 0x21, 0x4f, 0xf1, 0x31, 0x0 }, { 0x2, 0x80, 0xc3, 0x45, 0x0 }}, 0x0 }, + {{{ 0x7, 0x8f, 0x9c, 0x33, 0x1 }, { 0x1, 0x80, 0x8a, 0x13, 0x0 }}, 0x0 }, + {{{ 0x21, 0x40, 0xf1, 0x31, 0x0 }, { 0x6, 0x80, 0xf4, 0x44, 0x0 }}, 0x0 }, + {{{ 0x21, 0x40, 0xf1, 0x31, 0x3 }, { 0x81, 0x0, 0xf4, 0x44, 0x2 }}, 0x2 }, + {{{ 0x11, 0x8d, 0xfd, 0x11, 0x0 }, { 0x11, 0x80, 0xfd, 0x11, 0x0 }}, 0x8 }, + {{{ 0xf0, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 }, + {{{ 0xf1, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 }, + {{{ 0xcd, 0x9e, 0x55, 0xd1, 0x0 }, { 0xd1, 0x0, 0xf2, 0x71, 0x0 }}, 0xe }, + {{{ 0x1, 0x0, 0xf2, 0x88, 0x0 }, { 0x1, 0x0, 0xf5, 0x88, 0x0 }}, 0x1 }, + {{{ 0x30, 0xd, 0xf2, 0xef, 0x0 }, { 0x21, 0x0, 0xf5, 0x78, 0x0 }}, 0x6 }, + {{{ 0x0, 0x10, 0xf4, 0xd9, 0x0 }, { 0x0, 0x0, 0xf5, 0xd7, 0x0 }}, 0x4 }, + {{{ 0x1, 0x4c, 0xf2, 0x50, 0x0 }, { 0x1, 0x40, 0xd2, 0x59, 0x0 }}, 0x8 }, + {{{ 0x20, 0x11, 0xe2, 0x8a, 0x0 }, { 0x20, 0x0, 0xe4, 0xa8, 0x0 }}, 0xa }, + {{{ 0x21, 0x40, 0x7b, 0x4, 0x1 }, { 0x21, 0x0, 0x75, 0x72, 0x0 }}, 0x2 }, + {{{ 0x31, 0xd, 0xf2, 0xef, 0x0 }, { 0x21, 0x0, 0xf5, 0x78, 0x0 }}, 0xa }, + {{{ 0x1, 0xc, 0xf5, 0x2f, 0x1 }, { 0x0, 0x80, 0xf5, 0x5c, 0x0 }}, 0x0 }, + {{{ 0xb0, 0x1c, 0x81, 0x3, 0x2 }, { 0x20, 0x0, 0x54, 0x67, 0x2 }}, 0xe }, + {{{ 0x1, 0x0, 0xf1, 0x65, 0x0 }, { 0x1, 0x80, 0xa3, 0xa8, 0x2 }}, 0x1 }, + {{{ 0xe1, 0x4f, 0xc1, 0xd3, 0x2 }, { 0x21, 0x0, 0x32, 0x74, 0x1 }}, 0x0 }, + {{{ 0x2, 0x0, 0xf6, 0x16, 0x0 }, { 0x12, 0x0, 0xf2, 0xf8, 0x0 }}, 0x1 }, + {{{ 0xe0, 0x63, 0xf8, 0xf3, 0x0 }, { 0x70, 0x80, 0xf7, 0xf3, 0x0 }}, 0x4 }, + {{{ 0x1, 0x6, 0xf3, 0xff, 0x0 }, { 0x8, 0x0, 0xf7, 0xff, 0x0 }}, 0x4 }, + {{{ 0x21, 0x16, 0xb0, 0x81, 0x1 }, { 0x22, 0x0, 0xb3, 0x13, 0x1 }}, 0xc }, + {{{ 0x1, 0x4f, 0xf0, 0xff, 0x0 }, { 0x30, 0x0, 0x90, 0xf, 0x0 }}, 0x6 }, + {{{ 0x0, 0x10, 0xf1, 0xf2, 0x2 }, { 0x1, 0x0, 0xf1, 0xf2, 0x3 }}, 0x0 }, + {{{ 0x1, 0x4f, 0xf1, 0x50, 0x0 }, { 0x21, 0x80, 0xa3, 0x5, 0x3 }}, 0x6 }, + {{{ 0xb1, 0x3, 0x55, 0x3, 0x0 }, { 0xb1, 0x3, 0x8, 0xa, 0x0 }}, 0x9 }, + {{{ 0x22, 0x0, 0xa9, 0x34, 0x1 }, { 0x1, 0x0, 0xa2, 0x42, 0x2 }}, 0x2 }, + {{{ 0xa0, 0xdc, 0x81, 0x31, 0x3 }, { 0xb1, 0x80, 0xf1, 0x1, 0x3 }}, 0x0 }, + {{{ 0x1, 0x4f, 0xf1, 0x50, 0x0 }, { 0x21, 0x80, 0xa3, 0x5, 0x3 }}, 0x6 }, + {{{ 0xf1, 0x80, 0xa0, 0x72, 0x0 }, { 0x74, 0x0, 0x90, 0x22, 0x0 }}, 0x9 }, + {{{ 0xe1, 0x13, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0xf0, 0xfc, 0x1 }}, 0xa }, + {{{ 0x31, 0x1c, 0x41, 0xb, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe }, + {{{ 0x71, 0x1c, 0x41, 0x1f, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe }, + {{{ 0x21, 0x1c, 0x53, 0x1d, 0x0 }, { 0xa1, 0x80, 0x52, 0x3b, 0x0 }}, 0xc }, + {{{ 0x21, 0x1d, 0xa4, 0xae, 0x1 }, { 0x21, 0x0, 0xb1, 0x9e, 0x0 }}, 0xc }, + {{{ 0xe1, 0x16, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0x81, 0x9e, 0x0 }}, 0xa }, + {{{ 0xe1, 0x15, 0x71, 0xae, 0x0 }, { 0xe2, 0x0, 0x81, 0x9e, 0x0 }}, 0xe }, + {{{ 0x21, 0x16, 0x71, 0xae, 0x0 }, { 0x21, 0x0, 0x81, 0x9e, 0x0 }}, 0xe }, + {{{ 0x71, 0x1c, 0x41, 0x1f, 0x0 }, { 0xa1, 0x80, 0x92, 0x3b, 0x0 }}, 0xe }, + {{{ 0x21, 0x4f, 0x81, 0x53, 0x0 }, { 0x32, 0x0, 0x22, 0x2c, 0x0 }}, 0xa }, + {{{ 0x22, 0x4f, 0x81, 0x53, 0x0 }, { 0x32, 0x0, 0x22, 0x2c, 0x0 }}, 0xa }, + {{{ 0x23, 0x4f, 0x81, 0x53, 0x0 }, { 0x34, 0x0, 0x22, 0x2c, 0x0 }}, 0xa }, + {{{ 0xe1, 0x16, 0x71, 0xae, 0x0 }, { 0xe1, 0x0, 0x81, 0x9e, 0x0 }}, 0xa }, + {{{ 0x71, 0xc5, 0x6e, 0x17, 0x0 }, { 0x22, 0x5, 0x8b, 0xe, 0x0 }}, 0x2 }, + {{{ 0xe6, 0x27, 0x70, 0xf, 0x1 }, { 0xe3, 0x0, 0x60, 0x9f, 0x0 }}, 0xa }, + {{{ 0x30, 0xc8, 0xd5, 0x19, 0x0 }, { 0xb1, 0x80, 0x61, 0x1b, 0x0 }}, 0xc }, + {{{ 0x32, 0x9a, 0x51, 0x1b, 0x0 }, { 0xa1, 0x82, 0xa2, 0x3b, 0x0 }}, 0xc }, + {{{ 0xad, 0x3, 0x74, 0x29, 0x0 }, { 0xa2, 0x82, 0x73, 0x29, 0x0 }}, 0x7 }, + {{{ 0x21, 0x83, 0x74, 0x17, 0x0 }, { 0x62, 0x8d, 0x65, 0x17, 0x0 }}, 0x7 }, + {{{ 0x94, 0xb, 0x85, 0xff, 0x1 }, { 0x13, 0x0, 0x74, 0xff, 0x0 }}, 0xc }, + {{{ 0x74, 0x87, 0xa4, 0x2, 0x0 }, { 0xd6, 0x80, 0x45, 0x42, 0x0 }}, 0x2 }, + {{{ 0xb3, 0x85, 0x76, 0x21, 0x1 }, { 0x20, 0x0, 0x3d, 0xc1, 0x0 }}, 0x6 }, + {{{ 0x17, 0x4f, 0xf2, 0x61, 0x0 }, { 0x12, 0x8, 0xf1, 0xb4, 0x0 }}, 0x8 }, + {{{ 0x4f, 0x86, 0x65, 0x1, 0x0 }, { 0x1f, 0x0, 0x32, 0x74, 0x0 }}, 0x4 }, + {{{ 0xe1, 0x23, 0x71, 0xae, 0x0 }, { 0xe4, 0x0, 0x82, 0x9e, 0x0 }}, 0xa }, + {{{ 0x11, 0x86, 0xf2, 0xbd, 0x0 }, { 0x4, 0x80, 0xa0, 0x9b, 0x1 }}, 0x8 }, + {{{ 0x20, 0x90, 0xf5, 0x9e, 0x2 }, { 0x11, 0x0, 0xf4, 0x5b, 0x3 }}, 0xc }, + {{{ 0xf0, 0x80, 0x34, 0xe4, 0x0 }, { 0x7e, 0x0, 0xa2, 0x6, 0x0 }}, 0x8 }, + {{{ 0x90, 0xf, 0xff, 0x1, 0x3 }, { 0x0, 0x0, 0x1f, 0x1, 0x0 }}, 0xe }, + {{{ 0x1, 0x4f, 0xf0, 0xff, 0x0 }, { 0x33, 0x0, 0x90, 0xf, 0x0 }}, 0x6 }, + {{{ 0x1e, 0x0, 0x1f, 0xf, 0x0 }, { 0x10, 0x0, 0x1f, 0x7f, 0x0 }}, 0x0 }, + {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 }, + {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 }, + {{{ 0x93, 0x6, 0xc1, 0x4, 0x1 }, { 0x82, 0x0, 0x51, 0x9, 0x0 }}, 0x6 }, + {{{ 0xa0, 0x0, 0x96, 0x33, 0x0 }, { 0x20, 0x0, 0x55, 0x2b, 0x0 }}, 0x6 }, + {{{ 0x0, 0xc0, 0xff, 0x5, 0x0 }, { 0x0, 0x0, 0xff, 0x5, 0x3 }}, 0x0 }, + {{{ 0x4, 0x8, 0xf8, 0x7, 0x0 }, { 0x1, 0x0, 0x82, 0x74, 0x0 }}, 0x8 }, + {{{ 0x0, 0x0, 0x2f, 0x5, 0x0 }, { 0x20, 0x0, 0xff, 0x5, 0x3 }}, 0xa }, + {{{ 0x93, 0x0, 0xf7, 0x7, 0x2 }, { 0x0, 0x0, 0xf7, 0x7, 0x0 }}, 0xa }, + {{{ 0x0, 0x40, 0x80, 0x7a, 0x0 }, { 0xc4, 0x0, 0xc0, 0x7e, 0x0 }}, 0x8 }, + {{{ 0x90, 0x80, 0x55, 0xf5, 0x0 }, { 0x0, 0x0, 0x55, 0xf5, 0x0 }}, 0x8 }, + {{{ 0xe1, 0x80, 0x34, 0xe4, 0x0 }, { 0x69, 0x0, 0xf2, 0x6, 0x0 }}, 0x8 }, + {{{ 0x3, 0x2, 0xf0, 0xff, 0x3 }, { 0x11, 0x80, 0xf0, 0xff, 0x2 }}, 0x2 }, + {{{ 0x1e, 0x0, 0x1f, 0xf, 0x0 }, { 0x10, 0x0, 0x1f, 0x7f, 0x0 }}, 0x0 }, + {{{ 0x0, 0x0, 0x2f, 0x1, 0x0 }, { 0x0, 0x0, 0xff, 0x1, 0x0 }}, 0x4 }, + {{{ 0xbe, 0x0, 0xf1, 0x1, 0x3 }, { 0x31, 0x0, 0xf1, 0x1, 0x0 }}, 0x4 }, + {{{ 0x93, 0x85, 0x3f, 0x6, 0x1 }, { 0x0, 0x0, 0x5f, 0x7, 0x0 }}, 0x6 }, + {{{ 0x6, 0x0, 0xa0, 0xf0, 0x0 }, { 0x44, 0x0, 0xc5, 0x75, 0x0 }}, 0xe }, + {{{ 0x60, 0x0, 0x10, 0x81, 0x0 }, { 0x20, 0x8c, 0x12, 0x91, 0x0 }}, 0xe }, + {{{ 0x1, 0x40, 0xf1, 0x53, 0x0 }, { 0x8, 0x40, 0xf1, 0x53, 0x0 }}, 0x0 }, + {{{ 0x31, 0x0, 0x56, 0x31, 0x0 }, { 0x16, 0x0, 0x7d, 0x41, 0x0 }}, 0x0 }, + {{{ 0x0, 0x10, 0xf2, 0x72, 0x0 }, { 0x13, 0x0, 0xf2, 0x72, 0x0 }}, 0xc }, + {{{ 0x10, 0x0, 0x75, 0x93, 0x1 }, { 0x1, 0x0, 0xf5, 0x82, 0x1 }}, 0x0 }, + {{{ 0x0, 0x0, 0xf6, 0xff, 0x2 }, { 0x0, 0x0, 0xf6, 0xff, 0x0 }}, 0x8 }, + {{{ 0x30, 0x0, 0xff, 0xa0, 0x3 }, { 0x63, 0x0, 0x65, 0xb, 0x2 }}, 0x0 }, + {{{ 0x2a, 0x0, 0xf6, 0x87, 0x0 }, { 0x2b, 0x0, 0x76, 0x25, 0x0 }}, 0x0 }, + {{{ 0x85, 0x0, 0xb8, 0x84, 0x0 }, { 0x43, 0x0, 0xe5, 0x8f, 0x0 }}, 0x6 }, + {{{ 0x7, 0x4f, 0xf2, 0x60, 0x0 }, { 0x12, 0x0, 0xf2, 0x72, 0x0 }}, 0x8 }, + {{{ 0x5, 0x40, 0xb3, 0xd3, 0x0 }, { 0x86, 0x80, 0xf2, 0x24, 0x0 }}, 0x2 }, + {{{ 0xd0, 0x0, 0x11, 0xcf, 0x0 }, { 0xd1, 0x0, 0xf4, 0xe8, 0x3 }}, 0x0 }, + {{{ 0x5, 0x4e, 0xda, 0x25, 0x2 }, { 0x1, 0x0, 0xf9, 0x15, 0x0 }}, 0xa }, + {{{ 0x3, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xff, 0x6, 0x0 }}, 0x0 }, + {{{ 0x13, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xf9, 0x5, 0x0 }}, 0x0 }, + {{{ 0xf0, 0x1, 0x97, 0x17, 0x0 }, { 0x21, 0xd, 0xf1, 0x18, 0x0 }}, 0x8 }, + {{{ 0xf1, 0x41, 0x11, 0x11, 0x0 }, { 0xf1, 0x41, 0x11, 0x11, 0x0 }}, 0x2 }, + {{{ 0x13, 0x0, 0x8f, 0x7, 0x2 }, { 0x2, 0x0, 0xff, 0x6, 0x0 }}, 0x0 }, + {{{ 0x1, 0x0, 0x2f, 0x1, 0x0 }, { 0x1, 0x0, 0xaf, 0x1, 0x3 }}, 0xf }, + {{{ 0x1, 0x6, 0xf3, 0xff, 0x0 }, { 0x8, 0x0, 0xf7, 0xff, 0x0 }}, 0x4 }, + {{{ 0xc0, 0x4f, 0xf1, 0x3, 0x0 }, { 0xbe, 0xc, 0x10, 0x1, 0x0 }}, 0x2 }, + {{{ 0x0, 0x2, 0xf0, 0xff, 0x0 }, { 0x11, 0x80, 0xf0, 0xff, 0x0 }}, 0x6 }, + {{{ 0x81, 0x47, 0xf1, 0x83, 0x0 }, { 0xa2, 0x4, 0x91, 0x86, 0x0 }}, 0x6 }, + {{{ 0xf0, 0xc0, 0xff, 0xff, 0x3 }, { 0xe5, 0x0, 0xfb, 0xf0, 0x0 }}, 0xe }, + {{{ 0x0, 0x2, 0xf0, 0xff, 0x0 }, { 0x11, 0x80, 0xf0, 0xff, 0x0 }}, 0x6 } +}; + +static const PercussionNote percussionNotes[47] = { + {{{ 0x0, 0xb, 0xa8, 0x38, 0x0 }, { 0x0, 0x0, 0xd6, 0x49, 0x0 }}, 0x0, 0x4, 0x1, 0x97, 0x4 }, + {{{ 0xc0, 0xc0, 0xf8, 0x3f, 0x2 }, { 0xc0, 0x0, 0xf6, 0x8e, 0x0 }}, 0x0, 0x4, 0x1, 0xf7, 0x4 }, + {{{ 0xc0, 0x80, 0xc9, 0xab, 0x0 }, { 0xeb, 0x40, 0xb5, 0xf6, 0x0 }}, 0x1, 0x3, 0x1, 0x6a, 0x6 }, + {{{ 0xc, 0x0, 0xd8, 0xa6, 0x0 }, { 0x0, 0x0, 0xd6, 0x4f, 0x0 }}, 0x1, 0x3, 0x1, 0x6c, 0x5 }, + {{{ 0x1, 0x0, 0xe2, 0xd2, 0x0 }, { 0x3, 0x41, 0x8f, 0x48, 0x49 }}, 0xc, 0x4, 0x1, 0x2f, 0x5 }, + {{{ 0x0, 0x0, 0xc8, 0x58, 0x3 }, { 0x0, 0x0, 0xf6, 0x4f, 0x0 }}, 0x9, 0x3, 0x1, 0x108, 0x4 }, + {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf2, 0xff, 0xe0, 0x50, 0x52 }}, 0x5d, 0x2, 0x1, 0x9f, 0x5 }, + {{{ 0xe, 0x9, 0xb9, 0x47, 0x0 }, { 0xeb, 0x40, 0xf5, 0xe6, 0x0 }}, 0x0, 0x0, 0x1, 0x82, 0x6 }, + {{{ 0x0, 0x0, 0xd6, 0x83, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x2, 0x1, 0xc7, 0x5 }, + {{{ 0x1, 0x9, 0x89, 0x67, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x0, 0x1, 0x80, 0x6 }, + {{{ 0x1, 0x0, 0xd6, 0x96, 0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a, 0x2, 0x1, 0xed, 0x5 }, + {{{ 0x0, 0x9, 0xa9, 0x55, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x1, 0x82, 0x6 }, + {{{ 0x2, 0x0, 0xc6, 0x96, 0x0 }, { 0xe0, 0x0, 0xe0, 0x40, 0x0 }}, 0x1, 0x2, 0x1, 0x123, 0x5 }, + {{{ 0x5, 0x0, 0xf6, 0x56, 0x0 }, { 0xf7, 0xff, 0xb3, 0x90, 0x4f }}, 0x1, 0x2, 0x1, 0x15b, 0x5 }, + {{{ 0x1, 0x0, 0xf7, 0x14, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x1ac, 0x5 }, + {{{ 0x0, 0x0, 0xf6, 0x56, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x1, 0x2, 0x1, 0x18b, 0x5 }, + {{{ 0x0, 0x83, 0xfb, 0x5, 0x0 }, { 0xf7, 0x41, 0x39, 0x90, 0x79 }}, 0x1, 0x1, 0x1, 0xc8, 0x5 }, + {{{ 0x0, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0xf9, 0x5 }, + {{{ 0x1, 0x0, 0xa0, 0x5, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x27a, 0x6 }, + {{{ 0x0, 0x5, 0xf3, 0x6, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x108, 0x7 }, + {{{ 0x1, 0x0, 0xf9, 0x34, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x147, 0x4 }, + {{{ 0x0, 0x0, 0xf7, 0x16, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x2, 0x1, 0x120, 0x6 }, + {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x42, 0x6 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x1, 0x0, 0xff, 0x5, 0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7, 0x1, 0x1, 0x6d, 0x5 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 }, + {{{ 0x0, 0x0, 0x0, 0x0, 0x0 }, { 0x0, 0x0, 0x0, 0x0, 0x0 }}, 0x0, 0x0, 0x0, 0x3fc, 0x4 } +}; + +const uint16 melodicFrequencies[36] = { + 0x55, 0x5a, 0x60, 0x66, 0x6c, 0x72, 0x79, 0x80, 0x88, + 0x90, 0x99, 0xa1, 0xab, 0xb5, 0xc0, 0xcc, 0xd8, 0xe5, + 0xf2, 0x101, 0x110, 0x120, 0x132, 0x143, 0x156, 0x16b, 0x181, + 0x198, 0x1b0, 0x1ca, 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x286 +}; + +class AdLibDriver; + +class AdLibChannel : public MidiChannel_MPU401 { +public: + void reset(); + + uint8 _program; + uint8 _volume; + uint8 _pedal; +}; + +struct MelodicVoice { + bool _used; + uint8 _channel; + uint8 _program; + + uint8 _key; + uint32 _timestamp; + uint16 _frequency; + int8 _octave; +}; + +class AdLibDriver : public MidiDriver_Emulated { +public: + AdLibDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { + for (uint i = 0; i < 16; ++i) + _channels[i].init(this, i); + } + + int open(); + void close(); + void send(uint32 b); + MidiChannel *allocateChannel(); + MidiChannel *getPercussionChannel() { return &_channels[9]; } + + bool isStereo() const { return false; } + int getRate() const { return _mixer->getOutputRate(); } + + void generateSamples(int16 *buf, int len); + +protected: + OPL::OPL *_opl; + AdLibChannel _channels[16]; + MelodicVoice _voices[kNumMelodic]; + uint8 _notesPerPercussion[kNumPercussion]; + + uint _lastVoice; + + uint8 _percussionMask; + + void noteOff(uint8 channel, uint8 note); + void noteOn(uint8 channel, uint8 note, uint8 velocity); + void allNotesOff(); + void setModulationWheel(uint8 channel, uint8 value); + void setFootController(uint8 channel, uint8 value); + void setVolume(uint8 channel, uint8 value); + void setPitchBend(uint8 channel, int16 value); + + void playNote(uint8 voice, uint8 octave, uint16 frequency); + + void programOperatorSimple(uint8 offset, const OPLOperator &op); + void programOperator(uint8 offset, const OPLOperator &op); + void setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume); + + void setupPercussion(const PercussionNote ¬e); + void playPercussion(uint8 channel, const PercussionNote ¬e, uint8 velocity); + + void programMelodicVoice(uint8 voice, uint8 program); + void playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity); + void muteMelodicVoice(uint8 voice); + + void initVoices(); +}; + +MidiDriver *createAdLibDriver() { + return new AdLibDriver(g_system->getMixer()); +} + +void AdLibChannel::reset() { + _program = 0; + _volume = 127; + _pedal = 0; +} + +/* + bit 7 - Clear: AM depth is 1 dB + bit 6 - Clear: Vibrato depth is 7 cent + bit 5 - Set: Rhythm enabled (6 melodic voices) + bit 4 - Bass drum off + bit 3 - Snare drum off + bit 2 - Tom tom off + bit 1 - Cymbal off + bit 0 - Hi Hat off +*/ +const uint8 kDefaultPercussionMask = 0x20; + +int AdLibDriver::open() { + if (_isOpen) + return MERR_ALREADY_OPEN; + + MidiDriver_Emulated::open(); + + _opl = OPL::Config::create(); + _opl->init(getRate()); + _opl->writeReg(0x1, 0x20); // set bit 5 (enable all waveforms) + + // Reset the OPL registers. + for (uint i = 0; i < kNumVoices; ++i) { + _opl->writeReg(0xA0 + i, 0); // frequency + _opl->writeReg(0xB0 + i, 0); // key on + _opl->writeReg(0xC0 + i, 0); // feedback + } + _opl->writeReg(0xBD, kDefaultPercussionMask); + + initVoices(); + + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + return 0; +} + +void AdLibDriver::close() { + if (!_isOpen) + return; + + _isOpen = false; + _mixer->stopHandle(_mixerSoundHandle); + + delete _opl; +} + +void AdLibDriver::send(uint32 b) { + uint channel = b & 0xf; + uint cmd = (b >> 4) & 0xf; + uint param1 = (b >> 8) & 0xff; + uint param2 = (b >> 16) & 0xff; + + switch (cmd) { + case 8: + noteOff(channel, param1); + break; + case 9: + // TODO: map volume? + noteOn(channel, param1, param2); + break; + case 11: + // controller change + switch (param1) { + case 1: + setModulationWheel(channel, param2); + break; + case 4: + setFootController(channel, param2); + break; + case 7: + setVolume(channel, param2); + break; + case 123: + // all notes off + allNotesOff(); + break; + } + break; + case 12: + // program change + _channels[channel]._program = param1; + break; + case 14: + setPitchBend(channel, (param1 | (param2 << 7)) - 0x2000); + break; + } +} + +void AdLibDriver::noteOff(uint8 channel, uint8 note) { + if (channel == 9) { + if (note < 35 || note > 81) + return; + + _percussionMask &= ~(1 << percussionNotes[note - 35].percussion); + _opl->writeReg(0xBD, _percussionMask); + return; + } + + for (int i = kNumMelodic - 1; i >= 0; --i) { + if (_voices[i]._channel != channel) + continue; + if (_voices[i]._key != note) + continue; + muteMelodicVoice(i); + _voices[i]._used = false; + return; + } + + //debug(1, "failed to find voice off for channel %d, note %d", channel, note); +} + +void AdLibDriver::noteOn(uint8 channel, uint8 note, uint8 velocity) { + if (channel == 9) { + if (note < 35 || note > 81) + return; + + const PercussionNote &info = percussionNotes[note - 35]; + if (!info.valid) + return; + + if (note != _notesPerPercussion[info.percussion]) { + setupPercussion(info); + _notesPerPercussion[info.percussion] = note; + } + + playPercussion(channel, info, velocity); + return; + } + + if (velocity == 0) { + noteOff(channel, note); + return; + } + + // We want to play a note on a melodic (voice) channel. + + // First, look for a voice playing the same note with the same program. + for (uint i = 0; i < kNumMelodic; ++i) { + if (_voices[i]._channel != channel || _voices[i]._key != note) + continue; + if (_voices[i]._program != _channels[channel]._program) + continue; + muteMelodicVoice(i); + playMelodicNote(i, channel, note, velocity); + return; + } + + // The loops below try to start at _lastVoice and find a voice to use. + // They ignore _lastVoice itself, and update _lastVoice if they succeed. + + // Then, try finding a melodic voice with the same program. + for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) { + if (_voices[i]._used) + continue; + if (_voices[i]._program != _channels[channel]._program) + continue; + playMelodicNote(i, channel, note, velocity); + _lastVoice = i; + return; + } + + // Then, try finding a free melodic voice of any kind. + for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) { + if (_voices[i]._used) + continue; + programMelodicVoice(i, _channels[channel]._program); + playMelodicNote(i, channel, note, velocity); + _lastVoice = i; + return; + } + + // Then just try finding a melodic voice with the same program, + // and steal it. + for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) { + if (_voices[i]._program != _channels[channel]._program) + continue; + muteMelodicVoice(i); + playMelodicNote(i, channel, note, velocity); + _lastVoice = i; + return; + } + + // Finally, just take control of the channel used least recently. + uint voiceId = 0; + uint32 bestTimestamp = 0xffffffff; + for (uint i = 0; i < kNumMelodic; ++i) + if (bestTimestamp > _voices[i]._timestamp) { + voiceId = i; + bestTimestamp = _voices[i]._timestamp; + } + + //debug(1, "ran out of voices for channel %d, note %d, program %d: reused voice %d", channel, note, _channels[channel]._program, voiceId); + programMelodicVoice(voiceId, _channels[channel]._program); + playMelodicNote(voiceId, channel, note, velocity); + _lastVoice = voiceId; +} + +// TODO: this doesn't match original +void AdLibDriver::allNotesOff() { + for (uint i = 0; i < kNumMelodic; ++i) { + muteMelodicVoice(i); + _voices[i]._used = false; + } + + _percussionMask = kDefaultPercussionMask; + _opl->writeReg(0xBD, kDefaultPercussionMask); +} + +void AdLibDriver::setModulationWheel(uint8 channel, uint8 value) { + if (value >= 64) + _percussionMask |= 0x80; + else + _percussionMask &= 0x7f; + + _opl->writeReg(0xBD, _percussionMask); +} + +void AdLibDriver::setFootController(uint8 channel, uint8 value) { + _channels[channel]._pedal = (value >= 64); +} + +void AdLibDriver::setVolume(uint8 channel, uint8 value) { + _channels[channel]._volume = value; +} + +void AdLibDriver::setPitchBend(uint8 channel, int16 value) { + for (uint i = 0; i < kNumMelodic; ++i) { + if (_voices[i]._channel != channel || !_voices[i]._used) + continue; + + // index into frequency table + uint f = 12 + (_voices[i]._key % 12); + + int16 bendAmount = value; + if (bendAmount > 0) { + // bend up two semitones + bendAmount *= (melodicFrequencies[f + 2] - melodicFrequencies[f]); + } else { + // bend down two semitones + bendAmount *= (melodicFrequencies[f] - melodicFrequencies[f - 2]); + } + bendAmount /= 0x2000; + bendAmount += melodicFrequencies[f]; // add the base frequency + playNote(i, _voices[i]._octave, bendAmount); + _voices[i]._timestamp = g_system->getMillis(); + } +} + +void AdLibDriver::playNote(uint8 voice, uint8 octave, uint16 frequency) { + /* Percussions are always fed keyOn = 0 even to set the note, as they are activated using the + BD register instead. I wonder if they can just be fed the same value as melodic voice and + be done with it. */ + uint8 keyOn = (voice < kNumMelodic) ? 0x20 : 0; + + // key on, octave, high 2 bits of frequency + _opl->writeReg(0xB0 + voice, keyOn | ((octave & 7) << 2) | ((frequency >> 8) & 3)); + // low 8 bits of frequency + _opl->writeReg(0xA0 + voice, frequency & 0xff); +} + +void AdLibDriver::programOperatorSimple(uint8 offset, const OPLOperator &op) { + _opl->writeReg(0x40 + offset, op.levels & LEVEL_MASK); + _opl->writeReg(0x60 + offset, op.attackDecay); + _opl->writeReg(0x80 + offset, op.sustainRelease); +} + +void AdLibDriver::programOperator(uint8 offset, const OPLOperator &op) { + _opl->writeReg(0x20 + offset, op.characteristic); + _opl->writeReg(0x60 + offset, op.attackDecay); + _opl->writeReg(0x80 + offset, op.sustainRelease); + _opl->writeReg(0xE0 + offset, op.waveform); + _opl->writeReg(0x40 + offset, op.levels); +} + +const uint16 adlibLogVolume[] = { + 0, 37, 58, 73, 85, 95, 103, 110, 116, 121, 127, 131, 135, 139, 143, 146, + 149, 153, 155, 158, 161, 163, 165, 168, 170, 172, 174, 176, 178, 179, 181, 183, + 184, 186, 188, 189, 191, 192, 193, 195, 196, 197, 198, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 219, + 220, 221, 222, 223, 223, 224, 225, 226, 226, 227, 228, 228, 229, 230, 231, 231, + 232, 233, 233, 234, 234, 235, 236, 236, 237, 237, 238, 239, 239, 240, 240, 241, + 241, 242, 242, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, + 249, 250, 250, 251, 251, 252, 252, 253, 253, 253, 254, 254, 255, 255, 256, 256, + 256 +}; + +void AdLibDriver::setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume) { + uint8 programLevel = LEVEL_MASK; + if (!forceVolume) + programLevel -= (op.levels & LEVEL_MASK); + + uint32 noteLevel = adlibLogVolume[velocity]; + uint32 channelLevel = adlibLogVolume[_channels[channel]._volume]; + // programLevel comes from the static data and is probably already in the correct logarithmic scale + uint32 finalLevel = LEVEL_MASK - ((noteLevel * channelLevel * programLevel) >> 16); + + // high 2 bits are scaling level, the rest is (inversed) volume + _opl->writeReg(0x40 + offset, (op.levels & 0xc0) | (finalLevel & 0x3f)); +} + +const uint8 operatorOffsetsForPercussion[] = { + 0x11, // hi-hat + 0x15, // cymbal + 0x12, // tom tom + 0x14 // snare drum +}; + +void AdLibDriver::setupPercussion(const PercussionNote ¬e) { + if (note.percussion < 4) { + // simple percussion (1 operator) + + // turn off relevant percussion + _percussionMask &= ~(1 << note.percussion); + _opl->writeReg(0xBD, _percussionMask); + + programOperatorSimple(operatorOffsetsForPercussion[note.percussion], note.op[0]); + return; + } + + // bass drum (2 operators) + + // turn off bass drum + _percussionMask &= ~(0x10); + _opl->writeReg(0xBD, _percussionMask); + + programOperator(0x10, note.op[0]); + programOperator(0x13, note.op[1]); + + _opl->writeReg(0xC0 + 6, note.feedbackAlgo); +} + +void AdLibDriver::playPercussion(uint8 channel, const PercussionNote ¬e, uint8 velocity) { + if (note.percussion < 4) { + // simple percussion (1 operator) + + // turn off relevant percussion + _percussionMask &= ~(1 << note.percussion); + _opl->writeReg(0xBD, _percussionMask); + + setOperatorLevel(operatorOffsetsForPercussion[note.percussion], note.op[0], velocity, channel, true); + + if (note.percussion == 2) { + // tom tom + playNote(8, note.octave, note.frequency); + } else if (note.percussion == 3) { + // snare drum + playNote(7, note.octave, note.frequency); + } + + // turn on relevant percussion + _percussionMask |= (1 << note.percussion); + _opl->writeReg(0xBD, _percussionMask); + return; + } + + // turn off bass drum + _percussionMask &= ~(0x10); + _opl->writeReg(0xBD, _percussionMask); + + if (note.feedbackAlgo & 1) { + // operators 1 and 2 in additive synthesis + setOperatorLevel(0x10, note.op[0], velocity, channel, true); + setOperatorLevel(0x13, note.op[1], velocity, channel, true); + } else { + // operator 2 is modulating operator 1 + setOperatorLevel(0x13, note.op[1], velocity, channel, true); + } + + playNote(6, note.octave, note.frequency); + + // turn on bass drum + _percussionMask |= 0x10; + _opl->writeReg(0xBD, _percussionMask); +} + +const uint8 offset1ForMelodic[kNumVoices] = { 0x0, 0x1, 0x2, 0x8, 0x9, 0xa, 0x10, 0x11, 0x12 }; +const uint8 offset2ForMelodic[kNumVoices] = { 0x3, 0x4, 0x5, 0xb, 0xc, 0xd, 0x13, 0x14, 0x15 }; + +void AdLibDriver::programMelodicVoice(uint8 voice, uint8 program) { + assert(program < 128); + assert(voice < kNumMelodic); + + const MelodicProgram &info = melodicPrograms[program]; + uint8 offset1 = offset1ForMelodic[voice]; + uint8 offset2 = offset2ForMelodic[voice]; + + // Start at lowest volume. + _opl->writeReg(0x40 + offset1, LEVEL_MASK); + _opl->writeReg(0x40 + offset2, LEVEL_MASK); + + muteMelodicVoice(voice); + + programOperator(offset1, info.op[0]); + programOperator(offset2, info.op[1]); + + _opl->writeReg(0xC0 + voice, info.feedbackAlgo); +} + +void AdLibDriver::playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity) { + assert(voice < kNumMelodic); + + uint8 octave = note / 12; + uint8 f = 12 + (note % 12); + + if (octave > 7) + octave = 7; + + const MelodicProgram &info = melodicPrograms[_channels[channel]._program]; + uint8 offset1 = offset1ForMelodic[voice]; + uint8 offset2 = offset2ForMelodic[voice]; + + if (info.feedbackAlgo & 1) { + setOperatorLevel(offset1, info.op[0], velocity, channel, false); + setOperatorLevel(offset2, info.op[1], velocity, channel, false); + } else { + setOperatorLevel(offset2, info.op[1], velocity, channel, true); + } + + playNote(voice, octave, melodicFrequencies[f]); + + _voices[voice]._program = _channels[channel]._program; + _voices[voice]._key = note; + _voices[voice]._channel = channel; + _voices[voice]._timestamp = g_system->getMillis(); + _voices[voice]._frequency = melodicFrequencies[f]; + _voices[voice]._octave = octave; + _voices[voice]._used = true; +} + +void AdLibDriver::muteMelodicVoice(uint8 voice) { + _opl->writeReg(0xB0 + voice, 0 | ((_voices[voice]._octave & 7) << 2) | ((_voices[voice]._frequency >> 8) & 3)); +} + +MidiChannel *AdLibDriver::allocateChannel() { + for (uint i = 0; i < 16; ++i) { + if (i == 9) + continue; + + if (_channels[i].allocate()) + return &_channels[i]; + } + + return NULL; +} + +void AdLibDriver::generateSamples(int16 *buf, int len) { + memset(buf, 0, sizeof(int16) * len); + _opl->readBuffer(buf, len); +} + +void AdLibDriver::initVoices() { + _percussionMask = kDefaultPercussionMask; + _opl->writeReg(0xBD, _percussionMask); + + for (uint i = 0; i < 16; ++i) + _channels[i].reset(); + + for (uint i = 0; i < kNumMelodic; ++i) { + _voices[i]._key = 0xff; + _voices[i]._program = 0xff; + _voices[i]._channel = 0xff; + _voices[i]._timestamp = 0; + _voices[i]._frequency = 0; + _voices[i]._octave = 0; + _voices[i]._used = false; + } + + for (uint i = 0; i < kNumPercussion; ++i) + _notesPerPercussion[i] = 0xff; + + _lastVoice = 0; +} + +} // namespace Parallaction diff --git a/engines/parallaction/callables_ns.cpp b/engines/parallaction/callables_ns.cpp index 64885c7ff3..c1720a1a8e 100644 --- a/engines/parallaction/callables_ns.cpp +++ b/engines/parallaction/callables_ns.cpp @@ -275,7 +275,7 @@ void Parallaction_ns::_c_contaFoglie(void *parm) { if (num_foglie != 6) return; - _globalFlags |= 0x1000; + g_globalFlags |= 0x1000; return; } @@ -286,7 +286,7 @@ void Parallaction_ns::_c_zeroFoglie(void *parm) { } void Parallaction_ns::_c_trasformata(void *parm) { - _engineFlags ^= kEngineTransformedDonna; + g_engineFlags ^= kEngineTransformedDonna; // No need to invoke changeCharacter here, as // transformation happens on a location switch // and character change is automatically triggered. @@ -295,11 +295,11 @@ void Parallaction_ns::_c_trasformata(void *parm) { void Parallaction_ns::_c_offMouse(void *parm) { _input->setMouseState(MOUSE_DISABLED); - _engineFlags |= kEngineBlockInput; + g_engineFlags |= kEngineBlockInput; } void Parallaction_ns::_c_onMouse(void *parm) { - _engineFlags &= ~kEngineBlockInput; + g_engineFlags &= ~kEngineBlockInput; _input->setMouseState(MOUSE_ENABLED_SHOW); } @@ -389,7 +389,7 @@ void Parallaction_ns::_c_finito(void *parm) { } void Parallaction_ns::_c_ridux(void *parm) { - changeCharacter(_minidinoName); + changeCharacter(g_minidinoName); return; } @@ -444,7 +444,7 @@ void Parallaction_ns::_c_startIntro(void *parm) { _soundManI->playMusic(); } - _engineFlags |= kEngineBlockInput; + g_engineFlags |= kEngineBlockInput; _input->setMouseState(MOUSE_DISABLED); _intro = true; } diff --git a/engines/parallaction/debug.cpp b/engines/parallaction/debug.cpp index 0cb329e0f0..25acac9b06 100644 --- a/engines/parallaction/debug.cpp +++ b/engines/parallaction/debug.cpp @@ -103,7 +103,7 @@ bool Debugger::Cmd_Locations(int argc, const char **argv) { bool Debugger::Cmd_GlobalFlags(int argc, const char **argv) { - uint32 flags = _globalFlags; + uint32 flags = g_globalFlags; DebugPrintf("+------------------------------+---------+\n" "| flag name | value |\n" @@ -128,10 +128,10 @@ bool Debugger::Cmd_ToggleGlobalFlag(int argc, const char **argv) { DebugPrintf("invalid flag '%s'\n", argv[1]); } else { i--; - if ((_globalFlags & (1 << i)) == 0) - _globalFlags |= (1 << i); + if ((g_globalFlags & (1 << i)) == 0) + g_globalFlags |= (1 << i); else - _globalFlags &= ~(1 << i); + g_globalFlags &= ~(1 << i); } break; diff --git a/engines/parallaction/dialogue.cpp b/engines/parallaction/dialogue.cpp index e0bd6a6677..78cc23311f 100644 --- a/engines/parallaction/dialogue.cpp +++ b/engines/parallaction/dialogue.cpp @@ -192,7 +192,7 @@ void DialogueManager::transitionToState(DialogueState newState) { bool DialogueManager::testAnswerFlags(Answer *a) { uint32 flags = _vm->getLocationFlags(); if (a->_yesFlags & kFlagsGlobal) - flags = _globalFlags | kFlagsGlobal; + flags = g_globalFlags | kFlagsGlobal; return ((a->_yesFlags & flags) == a->_yesFlags) && ((a->_noFlags & ~flags) == a->_noFlags); } @@ -370,9 +370,9 @@ protected: bool _askPassword; bool checkPassword() { - return ((!scumm_stricmp(_vm->_char.getBaseName(), _doughName) && _vm->_password.hasPrefix("1732461")) || - (!scumm_stricmp(_vm->_char.getBaseName(), _donnaName) && _vm->_password.hasPrefix("1622")) || - (!scumm_stricmp(_vm->_char.getBaseName(), _dinoName) && _vm->_password.hasPrefix("179"))); + return ((!scumm_stricmp(_vm->_char.getBaseName(), g_doughName) && _vm->_password.hasPrefix("1732461")) || + (!scumm_stricmp(_vm->_char.getBaseName(), g_donnaName) && _vm->_password.hasPrefix("1622")) || + (!scumm_stricmp(_vm->_char.getBaseName(), g_dinoName) && _vm->_password.hasPrefix("179"))); } void resetPassword() { diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp index ee981a2c7d..8988897456 100644 --- a/engines/parallaction/disk_br.cpp +++ b/engines/parallaction/disk_br.cpp @@ -31,7 +31,7 @@ namespace Parallaction { -extern byte _braAmigaFramesDefaultPalette[]; +extern byte braAmigaFramesDefaultPalette[]; struct Sprite { uint16 size; @@ -475,7 +475,7 @@ void AmigaDisk_br::loadBackground(BackgroundInfo& info, const char *filename) { } delete stream; } else { - p = _braAmigaFramesDefaultPalette; + p = braAmigaFramesDefaultPalette; for (i = 0; i < 16; i++) { r = *p >> 2; p++; diff --git a/engines/parallaction/disk_ns.cpp b/engines/parallaction/disk_ns.cpp index 8d4afd6847..bad854525d 100644 --- a/engines/parallaction/disk_ns.cpp +++ b/engines/parallaction/disk_ns.cpp @@ -262,8 +262,15 @@ Common::SeekableReadStream *DosDisk_ns::tryOpenFile(const char* name) { Script* Disk_ns::loadLocation(const char *name) { char path[PATH_LEN]; + const char *charName = _vm->_char.getBaseName(); - sprintf(path, "%s%s/%s.loc", _vm->_char.getBaseName(), _language.c_str(), name); + // WORKAROUND: Special case for the Multilingual DOS version: during the ending + // sequence, it tries to load a non-existing file using "Dinor" as a character + // name. In this case, the character name should be just "dino". + if (!strcmp(charName, "Dinor")) + charName = "dino"; + + sprintf(path, "%s%s/%s.loc", charName, _language.c_str(), name); debugC(3, kDebugDisk, "Disk_ns::loadLocation(%s): trying '%s'", name, path); Common::SeekableReadStream *stream = tryOpenFile(path); @@ -328,7 +335,7 @@ GfxObj* DosDisk_ns::loadTalk(const char *name) { } char v20[30]; - if (_engineFlags & kEngineTransformedDonna) { + if (g_engineFlags & kEngineTransformedDonna) { sprintf(v20, "%stta.cnv", name); } else { sprintf(v20, "%stal.cnv", name); diff --git a/engines/parallaction/exec.cpp b/engines/parallaction/exec.cpp index 8594d02641..122abf9e0e 100644 --- a/engines/parallaction/exec.cpp +++ b/engines/parallaction/exec.cpp @@ -56,7 +56,7 @@ void ProgramExec::runScript(ProgramPtr script, AnimationPtr a) { } void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator last) { - if (_engineFlags & kEnginePauseJobs) { + if (g_engineFlags & kEnginePauseJobs) { return; } @@ -110,7 +110,7 @@ void CommandExec::runList(CommandList::iterator first, CommandList::iterator las } if (cmd->_flagsOn & kFlagsGlobal) { - useFlags = _globalFlags | kFlagsGlobal; + useFlags = g_globalFlags | kFlagsGlobal; useLocalFlags = false; } else { useFlags = _vm->getLocationFlags(); @@ -182,7 +182,7 @@ void CommandExec::suspend() { } void CommandExec::runSuspended() { - if (_engineFlags & kEngineWalking) { + if (g_engineFlags & kEngineWalking) { return; } diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp index 658ef5af82..985ea29311 100644 --- a/engines/parallaction/exec_br.cpp +++ b/engines/parallaction/exec_br.cpp @@ -307,7 +307,7 @@ DECLARE_COMMAND_OPCODE(testsfx) { DECLARE_COMMAND_OPCODE(ret) { - _engineFlags |= kEngineReturn; + g_engineFlags |= kEngineReturn; } @@ -327,7 +327,7 @@ DECLARE_INSTRUCTION_OPCODE(invalid) { DECLARE_COMMAND_OPCODE(clear) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags &= ~ctxt._cmd->_flags; + g_globalFlags &= ~ctxt._cmd->_flags; } else { _vm->clearLocationFlags(ctxt._cmd->_flags); } @@ -356,7 +356,7 @@ DECLARE_COMMAND_OPCODE(get) { DECLARE_COMMAND_OPCODE(toggle) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags ^= ctxt._cmd->_flags; + g_globalFlags ^= ctxt._cmd->_flags; } else { _vm->toggleLocationFlags(ctxt._cmd->_flags); } @@ -373,7 +373,7 @@ DECLARE_COMMAND_OPCODE(invalid) { DECLARE_COMMAND_OPCODE(set) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags |= ctxt._cmd->_flags; + g_globalFlags |= ctxt._cmd->_flags; } else { _vm->setLocationFlags(ctxt._cmd->_flags); } diff --git a/engines/parallaction/exec_ns.cpp b/engines/parallaction/exec_ns.cpp index d8fbdea971..3ea4332e50 100644 --- a/engines/parallaction/exec_ns.cpp +++ b/engines/parallaction/exec_ns.cpp @@ -151,7 +151,7 @@ DECLARE_INSTRUCTION_OPCODE(call) { DECLARE_INSTRUCTION_OPCODE(wait) { - if (_engineFlags & kEngineWalking) { + if (g_engineFlags & kEngineWalking) { ctxt._ip--; ctxt._suspend = true; } @@ -198,7 +198,7 @@ DECLARE_COMMAND_OPCODE(invalid) { DECLARE_COMMAND_OPCODE(set) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags |= ctxt._cmd->_flags; + g_globalFlags |= ctxt._cmd->_flags; } else { _vm->setLocationFlags(ctxt._cmd->_flags); } @@ -208,7 +208,7 @@ DECLARE_COMMAND_OPCODE(set) { DECLARE_COMMAND_OPCODE(clear) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags &= ~ctxt._cmd->_flags; + g_globalFlags &= ~ctxt._cmd->_flags; } else { _vm->clearLocationFlags(ctxt._cmd->_flags); } @@ -267,7 +267,7 @@ DECLARE_COMMAND_OPCODE(call) { DECLARE_COMMAND_OPCODE(toggle) { if (ctxt._cmd->_flags & kFlagsGlobal) { ctxt._cmd->_flags &= ~kFlagsGlobal; - _globalFlags ^= ctxt._cmd->_flags; + g_globalFlags ^= ctxt._cmd->_flags; } else { _vm->toggleLocationFlags(ctxt._cmd->_flags); } diff --git a/engines/parallaction/font.cpp b/engines/parallaction/font.cpp index d4c9aefd32..3b40960381 100644 --- a/engines/parallaction/font.cpp +++ b/engines/parallaction/font.cpp @@ -29,7 +29,7 @@ namespace Parallaction { -extern byte _amigaTopazFont[]; +extern byte amigaTopazFont[]; class BraFont : public Font { @@ -675,7 +675,7 @@ void Parallaction_ns::initFonts() { _introFont = _disk->loadFont("slide"); } else { _dialogueFont = _disk->loadFont("comic"); - Common::MemoryReadStream stream(_amigaTopazFont, 2600, DisposeAfterUse::NO); + Common::MemoryReadStream stream(amigaTopazFont, 2600, DisposeAfterUse::NO); _labelFont = new AmigaFont(stream); _menuFont = _disk->loadFont("slide"); _introFont = _disk->loadFont("intro"); diff --git a/engines/parallaction/gfxbase.cpp b/engines/parallaction/gfxbase.cpp index 852235ce34..a9889cc7af 100644 --- a/engines/parallaction/gfxbase.cpp +++ b/engines/parallaction/gfxbase.cpp @@ -152,22 +152,22 @@ void Gfx::freeCharacterObjects() { freeDialogueObjects(); } -void BackgroundInfo::loadGfxObjMask(const char *name, GfxObj *obj) { +void BackgroundInfo::loadGfxObjMask(Parallaction *vm, const char *name, GfxObj *obj) { debugC(1, kDebugGraphics, "BackgroundInfo::loadGfxObjMask(\"%s\")", name); Common::Rect rect; obj->getRect(0, rect); - MaskBuffer *buf = _vm->_disk->loadMask(name, rect.width(), rect.height()); + MaskBuffer *buf = vm->_disk->loadMask(name, rect.width(), rect.height()); obj->_maskId = addMaskPatch(buf); obj->_hasMask = true; } -void BackgroundInfo::loadGfxObjPath(const char *name, GfxObj *obj) { +void BackgroundInfo::loadGfxObjPath(Parallaction *vm, const char *name, GfxObj *obj) { Common::Rect rect; obj->getRect(0, rect); - PathBuffer *buf = _vm->_disk->loadPath(name, rect.width(), rect.height()); + PathBuffer *buf = vm->_disk->loadPath(name, rect.width(), rect.height()); obj->_pathId = addPathPatch(buf); obj->_hasPath = true; @@ -226,6 +226,11 @@ void Gfx::drawGfxObject(GfxObj *obj, Graphics::Surface &surf) { rect.translate(x, y); data = obj->getData(obj->frame); + // WORKAROUND: During the end credits, game scripts try to show a + // non-existing frame. We change it to an existing one here. + if (obj->frame == 14 && obj->getNum() == 9 && !strcmp(obj->getName(), "Dinor")) + obj->frame = 8; + if (obj->getSize(obj->frame) == obj->getRawSize(obj->frame)) { blt(rect, data, &surf, obj->layer, obj->scale, obj->transparentKey); } else { diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp index 9855830478..59cd02e6ef 100644 --- a/engines/parallaction/graphics.cpp +++ b/engines/parallaction/graphics.cpp @@ -80,11 +80,11 @@ void drawCircle(int xCenter, int yCenter, int radius, int color, void (*plotProc Palette::Palette() { - int gameType = _vm->getGameType(); + int gameType = g_vm->getGameType(); if (gameType == GType_Nippon) { _colors = 32; - _hb = (_vm->getPlatform() == Common::kPlatformAmiga); + _hb = (g_vm->getPlatform() == Common::kPlatformAmiga); } else if (gameType == GType_BRA) { _colors = 256; diff --git a/engines/parallaction/graphics.h b/engines/parallaction/graphics.h index f8cb4b3647..e9daabb194 100644 --- a/engines/parallaction/graphics.h +++ b/engines/parallaction/graphics.h @@ -377,14 +377,14 @@ public: void toggleMaskPatch(uint id, int x, int y, bool apply); uint16 getMaskLayer(uint16 z) const; void finalizeMask(); - void loadGfxObjMask(const char *name, GfxObj *obj); + void loadGfxObjMask(Parallaction *vm, const char *name, GfxObj *obj); // path management bool hasPath(); uint addPathPatch(PathBuffer *patch); void togglePathPatch(uint id, int x, int y, bool apply); void finalizePath(); - void loadGfxObjPath(const char *name, GfxObj *obj); + void loadGfxObjPath(Parallaction *vm, const char *name, GfxObj *obj); }; diff --git a/engines/parallaction/gui_ns.cpp b/engines/parallaction/gui_ns.cpp index 3794aeae29..082c37f666 100644 --- a/engines/parallaction/gui_ns.cpp +++ b/engines/parallaction/gui_ns.cpp @@ -787,7 +787,7 @@ public: } destroyLabels(); - _engineFlags &= ~kEngineBlockInput; + g_engineFlags &= ~kEngineBlockInput; return _helper->getState("selectcharacter"); } diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp index 453bf9849d..4fbd9b99cc 100644 --- a/engines/parallaction/input.cpp +++ b/engines/parallaction/input.cpp @@ -188,15 +188,15 @@ int Input::updateGameInput() { int event = kEvNone; if (!isMouseEnabled() || - (_engineFlags & kEngineBlockInput) || - (_engineFlags & kEngineWalking) || - (_engineFlags & kEngineChangeLocation)) { + (g_engineFlags & kEngineBlockInput) || + (g_engineFlags & kEngineWalking) || + (g_engineFlags & kEngineChangeLocation)) { debugC(3, kDebugInput, "updateGameInput: input flags (mouse: %i, block: %i, walking: %i, changeloc: %i)", isMouseEnabled(), - (_engineFlags & kEngineBlockInput) == 0, - (_engineFlags & kEngineWalking) == 0, - (_engineFlags & kEngineChangeLocation) == 0 + (g_engineFlags & kEngineBlockInput) == 0, + (g_engineFlags & kEngineWalking) == 0, + (g_engineFlags & kEngineChangeLocation) == 0 ); return event; @@ -289,7 +289,7 @@ void Input::walkTo(const Common::Point &dest) { bool Input::translateGameInput() { - if (_engineFlags & kEnginePauseJobs) { + if (g_engineFlags & kEnginePauseJobs) { return false; } @@ -312,7 +312,7 @@ bool Input::translateGameInput() { // test if mouse is hovering on an interactive zone for the currently selected inventory item ZonePtr z = _vm->hitZone(_activeItem._id, mousePos.x, mousePos.y); - if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((_engineFlags & kEngineWalking) == 0)) && ((!z) || (ACTIONTYPE(z) != kZoneCommand))) { + if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((g_engineFlags & kEngineWalking) == 0)) && ((!z) || (ACTIONTYPE(z) != kZoneCommand))) { walkTo(mousePos); return true; } @@ -361,7 +361,7 @@ void Input::enterInventoryMode() { if (hitCharacter) { if (_activeItem._id != 0) { _activeItem._index = (_activeItem._id >> 16) & 0xFFFF; - _engineFlags |= kEngineDragging; + g_engineFlags |= kEngineDragging; } else { setArrowCursor(); } @@ -384,9 +384,9 @@ void Input::exitInventoryMode() { int pos = _vm->getHoverInventoryItem(mousePos.x, mousePos.y); _vm->highlightInventoryItem(-1); // disable - if ((_engineFlags & kEngineDragging)) { + if ((g_engineFlags & kEngineDragging)) { - _engineFlags &= ~kEngineDragging; + g_engineFlags &= ~kEngineDragging; ZonePtr z = _vm->hitZone(kZoneMerge, _activeItem._index, _vm->getInventoryItemIndex(pos)); if (z) { diff --git a/engines/parallaction/module.mk b/engines/parallaction/module.mk index d65653cd92..36572a51df 100644 --- a/engines/parallaction/module.mk +++ b/engines/parallaction/module.mk @@ -1,6 +1,7 @@ MODULE := engines/parallaction MODULE_OBJS := \ + adlib.o \ balloons.o \ callables_br.o \ callables_ns.o \ diff --git a/engines/parallaction/objects.cpp b/engines/parallaction/objects.cpp index d3529c5dd9..50556c3ec4 100644 --- a/engines/parallaction/objects.cpp +++ b/engines/parallaction/objects.cpp @@ -203,7 +203,7 @@ Zone::Zone() { } Zone::~Zone() { - _vm->_gfx->unregisterLabel(_label); + g_vm->_gfx->unregisterLabel(_label); delete _label; } @@ -325,7 +325,7 @@ int16 ScriptVar::getValue() { } if (_flags & kParaRandom) { - return (_vm->_rnd.getRandomNumber(65536) * _value) >> 16; + return (g_vm->_rnd.getRandomNumber(65536) * _value) >> 16; } error("Parameter is not an r-value"); diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp index 3b1b7d54a0..e6ef53aa78 100644 --- a/engines/parallaction/parallaction.cpp +++ b/engines/parallaction/parallaction.cpp @@ -33,12 +33,12 @@ #include "parallaction/walk.h" namespace Parallaction { -Parallaction *_vm = NULL; +Parallaction *g_vm = NULL; // public stuff -char _saveData1[30] = { '\0' }; -uint32 _engineFlags = 0; -uint32 _globalFlags = 0; +char g_saveData1[30] = { '\0' }; +uint32 g_engineFlags = 0; +uint32 g_globalFlags = 0; // private stuff @@ -48,7 +48,7 @@ Parallaction::Parallaction(OSystem *syst, const PARALLACTIONGameDescription *gam // Setup mixer syncSoundSettings(); - _vm = this; + g_vm = this; DebugMan.addDebugChannel(kDebugDialogue, "dialogue", "Dialogues debug level"); DebugMan.addDebugChannel(kDebugParser, "parser", "Parser debug level"); DebugMan.addDebugChannel(kDebugDisk, "disk", "Disk debug level"); @@ -87,7 +87,7 @@ Parallaction::~Parallaction() { Common::Error Parallaction::init() { _gameType = getGameType(); - _engineFlags = 0; + g_engineFlags = 0; _objectsNames = NULL; _globalFlagsNames = NULL; _location._hasSound = false; @@ -129,13 +129,9 @@ GUI::Debugger *Parallaction::getDebugger() { return _debugger; } -bool canScroll() { - return (_vm->_gfx->_backgroundInfo->width > _vm->_screenWidth); -} - void Parallaction::updateView() { - if ((_engineFlags & kEnginePauseJobs) && (_input->_inputMode != Input::kInputModeInventory)) { + if ((g_engineFlags & kEnginePauseJobs) && (_input->_inputMode != Input::kInputModeInventory)) { return; } @@ -147,14 +143,14 @@ void Parallaction::updateView() { void Parallaction::pauseJobs() { debugC(9, kDebugExec, "pausing jobs execution"); - _engineFlags |= kEnginePauseJobs; + g_engineFlags |= kEnginePauseJobs; return; } void Parallaction::resumeJobs() { debugC(9, kDebugExec, "resuming jobs execution"); - _engineFlags &= ~kEnginePauseJobs; + g_engineFlags &= ~kEnginePauseJobs; return; } @@ -265,7 +261,7 @@ void Parallaction::runGameFrame(int event) { if (shouldQuit()) return; - if (_engineFlags & kEngineChangeLocation) { + if (g_engineFlags & kEngineChangeLocation) { changeLocation(); } @@ -900,14 +896,14 @@ void CharacterName::bind(const char *name) { if (!_dummy) { if (!strcmp(name, "donna")) { - _engineFlags &= ~kEngineTransformedDonna; + g_engineFlags &= ~kEngineTransformedDonna; } else { - if (_engineFlags & kEngineTransformedDonna) { + if (g_engineFlags & kEngineTransformedDonna) { _suffix = _suffixTras; } else { const char *s = strstr(name, "tras"); if (s) { - _engineFlags |= kEngineTransformedDonna; + g_engineFlags |= kEngineTransformedDonna; _suffix = _suffixTras; end = s; } @@ -953,7 +949,7 @@ void Parallaction::beep() { void Parallaction::scheduleLocationSwitch(const char *location) { debugC(9, kDebugExec, "scheduleLocationSwitch(%s)\n", location); _newLocationName = location; - _engineFlags |= kEngineChangeLocation; + g_engineFlags |= kEngineChangeLocation; } } // End of namespace Parallaction diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h index 0d56b62e2f..2dbb0227d6 100644 --- a/engines/parallaction/parallaction.h +++ b/engines/parallaction/parallaction.h @@ -104,17 +104,17 @@ struct PARALLACTIONGameDescription; -extern uint32 _engineFlags; -extern char _saveData1[]; -extern uint32 _globalFlags; -extern const char *_dinoName; -extern const char *_donnaName; -extern const char *_doughName; -extern const char *_drkiName; -extern const char *_minidinoName; -extern const char *_minidonnaName; -extern const char *_minidoughName; -extern const char *_minidrkiName; +extern uint32 g_engineFlags; +extern char g_saveData1[]; +extern uint32 g_globalFlags; +extern const char *g_dinoName; +extern const char *g_donnaName; +extern const char *g_doughName; +extern const char *g_drkiName; +extern const char *g_minidinoName; +extern const char *g_minidonnaName; +extern const char *g_minidoughName; +extern const char *g_minidrkiName; @@ -601,7 +601,7 @@ private: void _c_password(void *); }; -extern Parallaction *_vm; +extern Parallaction *g_vm; } // End of namespace Parallaction diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp index 658a8e8795..07755fac5f 100644 --- a/engines/parallaction/parallaction_br.cpp +++ b/engines/parallaction/parallaction_br.cpp @@ -79,7 +79,7 @@ Common::Error Parallaction_br::init() { _cmdExec = new CommandExec_br(this); _programExec = new ProgramExec_br(this); - _walker = new PathWalker_BR; + _walker = new PathWalker_BR(this); _part = -1; _nextPart = -1; @@ -161,10 +161,10 @@ Common::Error Parallaction_br::go() { // initCharacter(); - while (((_engineFlags & kEngineReturn) == 0) && (!shouldQuit())) { + while (((g_engineFlags & kEngineReturn) == 0) && (!shouldQuit())) { runGame(); } - _engineFlags &= ~kEngineReturn; + g_engineFlags &= ~kEngineReturn; cleanupGame(); } @@ -259,7 +259,7 @@ void Parallaction_br::cleanupGame() { _countersNames = 0; _numLocations = 0; - _globalFlags = 0; + g_globalFlags = 0; memset(_localFlags, 0, sizeof(_localFlags)); memset(_locationNames, 0, sizeof(_locationNames)); memset(_zoneFlags, 0, sizeof(_zoneFlags)); @@ -275,7 +275,7 @@ void Parallaction_br::changeLocation() { cleanupGame(); // more cleanup needed for part changes (see also saveload) - _globalFlags = 0; + g_globalFlags = 0; cleanInventory(true); strcpy(_characterName1, "null"); @@ -358,7 +358,7 @@ void Parallaction_br::changeLocation() { // TODO: implement the music commands which control music execution _soundMan->execute(SC_PLAYMUSIC); - _engineFlags &= ~kEngineChangeLocation; + g_engineFlags &= ~kEngineChangeLocation; _newLocationName.clear(); _nextPart = -1; } @@ -548,7 +548,7 @@ void Parallaction_br::scheduleWalk(int16 x, int16 y, bool fromUser) { } } - _engineFlags |= kEngineWalking; + g_engineFlags |= kEngineWalking; } void Parallaction_br::setFollower(const Common::String &name) { diff --git a/engines/parallaction/parallaction_ns.cpp b/engines/parallaction/parallaction_ns.cpp index 0b92db1f0a..d33be0aa47 100644 --- a/engines/parallaction/parallaction_ns.cpp +++ b/engines/parallaction/parallaction_ns.cpp @@ -182,7 +182,7 @@ Common::Error Parallaction_ns::init() { _cmdExec = new CommandExec_ns(this); _programExec = new ProgramExec_ns(this); - _walker = new PathWalker_NS; + _walker = new PathWalker_NS(this); _sarcophagusDeltaX = 0; _movingSarcophagus = false; @@ -310,6 +310,7 @@ void Parallaction_ns::changeBackground(const char* background, const char* mask, _system->delayMillis(20); _gfx->setPalette(pal); _gfx->updateScreen(); + return; } if (path == 0) { @@ -382,8 +383,8 @@ void Parallaction_ns::changeLocation() { changeCharacter(locname.character()); } - strcpy(_saveData1, locname.location()); - parseLocation(_saveData1); + strcpy(g_saveData1, locname.location()); + parseLocation(g_saveData1); if (_location._startPosition.x != -1000) { _char._ani->setX(_location._startPosition.x); @@ -399,7 +400,7 @@ void Parallaction_ns::changeLocation() { // BUG #1837503: kEngineChangeLocation flag must be cleared *before* commands // and acommands are executed, so that it can be set again if needed. - _engineFlags &= ~kEngineChangeLocation; + g_engineFlags &= ~kEngineChangeLocation; _cmdExec->run(_location._commands); @@ -412,6 +413,11 @@ void Parallaction_ns::changeLocation() { if (!_intro) { _input->setMouseState(oldMouseState); + // WORKAROUND: Fix a script bug in the Multilingual DOS version of + // Nippon Safes: the mouse cursor is incorrectly hidden outside the + // cave at the end of the game. Fix it here. + if (!strcmp(_location._name, "ingressocav")) + _input->setMouseState(MOUSE_ENABLED_SHOW); } debugC(1, kDebugExec, "changeLocation() done"); @@ -526,10 +532,10 @@ void Parallaction_ns::cleanupGame() { _soundManI->stopMusic(); _inTestResult = false; - _engineFlags &= ~kEngineTransformedDonna; + g_engineFlags &= ~kEngineTransformedDonna; _numLocations = 0; - _globalFlags = 0; + g_globalFlags = 0; memset(_localFlags, 0, sizeof(_localFlags)); memset(_locationNames, 0, sizeof(_locationNames)); @@ -553,7 +559,7 @@ void Parallaction_ns::scheduleWalk(int16 x, int16 y, bool fromUser) { } _walker->buildPath(a, x, y); - _engineFlags |= kEngineWalking; + g_engineFlags |= kEngineWalking; } }// namespace Parallaction diff --git a/engines/parallaction/parser_br.cpp b/engines/parallaction/parser_br.cpp index 0904dbf655..e7f1b1b1ed 100644 --- a/engines/parallaction/parser_br.cpp +++ b/engines/parallaction/parser_br.cpp @@ -767,10 +767,10 @@ void LocationParser_br::parseGetData(ZonePtr z) { data->_gfxobj = obj; } else if (!scumm_stricmp(_tokens[0], "mask")) { - _out->_info->loadGfxObjMask(_tokens[1], data->_gfxobj); + _out->_info->loadGfxObjMask(_vm, _tokens[1], data->_gfxobj); } else if (!scumm_stricmp(_tokens[0], "path")) { - _out->_info->loadGfxObjPath(_tokens[1], data->_gfxobj); + _out->_info->loadGfxObjPath(_vm, _tokens[1], data->_gfxobj); } else if (!scumm_stricmp(_tokens[0], "icon")) { data->_getIcon = 4 + _vm->_objectsNames->lookup(_tokens[1]); diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp index f1d1db53e9..41ff74f0b4 100644 --- a/engines/parallaction/parser_ns.cpp +++ b/engines/parallaction/parser_ns.cpp @@ -246,7 +246,7 @@ DECLARE_ANIM_PARSER(file) { char vC8[200]; strcpy(vC8, _tokens[1]); - if (_engineFlags & kEngineTransformedDonna) { + if (g_engineFlags & kEngineTransformedDonna) { if (!scumm_stricmp(_tokens[1], "donnap") || !scumm_stricmp(_tokens[1], "donnapa")) { strcat(vC8, "tras"); } diff --git a/engines/parallaction/saveload.cpp b/engines/parallaction/saveload.cpp index 8de2d89b18..2ecc5377a4 100644 --- a/engines/parallaction/saveload.cpp +++ b/engines/parallaction/saveload.cpp @@ -88,7 +88,7 @@ void SaveLoad_ns::doLoadGame(uint16 slot) { _vm->_score = atoi(s.c_str()); s = f->readLine(); - _globalFlags = atoi(s.c_str()); + g_globalFlags = atoi(s.c_str()); s = f->readLine(); _vm->_numLocations = atoi(s.c_str()); @@ -151,7 +151,7 @@ void SaveLoad_ns::doSaveGame(uint16 slot, const char* name) { sprintf(s, "%s\n", _vm->_char.getFullName()); f->writeString(s); - sprintf(s, "%s\n", _saveData1); + sprintf(s, "%s\n", g_saveData1); f->writeString(s); sprintf(s, "%d\n", _vm->_char._ani->getX()); f->writeString(s); @@ -159,7 +159,7 @@ void SaveLoad_ns::doSaveGame(uint16 slot, const char* name) { f->writeString(s); sprintf(s, "%d\n", _vm->_score); f->writeString(s); - sprintf(s, "%u\n", _globalFlags); + sprintf(s, "%u\n", g_globalFlags); f->writeString(s); sprintf(s, "%d\n", _vm->_numLocations); diff --git a/engines/parallaction/sound.h b/engines/parallaction/sound.h index e875e69334..e12e50e278 100644 --- a/engines/parallaction/sound.h +++ b/engines/parallaction/sound.h @@ -33,6 +33,7 @@ #define PATH_LEN 200 class MidiParser; +class MidiDriver; namespace Parallaction { @@ -41,6 +42,7 @@ class MidiPlayer; class Parallaction_br; class MidiPlayer_MSC; +MidiDriver *createAdLibDriver(); class SoundManImpl { public: diff --git a/engines/parallaction/sound_br.cpp b/engines/parallaction/sound_br.cpp index 0925e55309..ad510eb1f1 100644 --- a/engines/parallaction/sound_br.cpp +++ b/engines/parallaction/sound_br.cpp @@ -91,20 +91,20 @@ public: }; void MidiParser_MSC::parseMetaEvent(EventInfo &info) { - uint8 type = read1(_position._play_pos); - uint8 len = read1(_position._play_pos); + uint8 type = read1(_position._playPos); + uint8 len = read1(_position._playPos); info.ext.type = type; info.length = len; info.ext.data = 0; if (type == 0x51) { - info.ext.data = _position._play_pos; + info.ext.data = _position._playPos; } else { warning("unknown meta event 0x%02X", type); info.ext.type = 0; } - _position._play_pos += len; + _position._playPos += len; } void MidiParser_MSC::parseMidiEvent(EventInfo &info) { @@ -116,13 +116,13 @@ void MidiParser_MSC::parseMidiEvent(EventInfo &info) { case 0xA: case 0xB: case 0xE: - info.basic.param1 = read1(_position._play_pos); - info.basic.param2 = read1(_position._play_pos); + info.basic.param1 = read1(_position._playPos); + info.basic.param2 = read1(_position._playPos); break; case 0xC: case 0xD: - info.basic.param1 = read1(_position._play_pos); + info.basic.param1 = read1(_position._playPos); info.basic.param2 = 0; break; @@ -135,9 +135,9 @@ void MidiParser_MSC::parseMidiEvent(EventInfo &info) { } void MidiParser_MSC::parseNextEvent(EventInfo &info) { - info.start = _position._play_pos; + info.start = _position._playPos; - if (_position._play_pos >= _trackEnd) { + if (_position._playPos >= _trackEnd) { // fake an end-of-track meta event info.delta = 0; info.event = 0xFF; @@ -146,8 +146,9 @@ void MidiParser_MSC::parseNextEvent(EventInfo &info) { return; } - info.delta = readVLQ(_position._play_pos); - info.event = read1(_position._play_pos); + info.length = 0; + info.delta = readVLQ(_position._playPos); + info.event = read1(_position._playPos); if (info.event == 0xFF) { parseMetaEvent(info); @@ -155,7 +156,7 @@ void MidiParser_MSC::parseNextEvent(EventInfo &info) { } if (info.event < 0x80) { - _position._play_pos--; + _position._playPos--; info.event = _lastEvent; } @@ -185,7 +186,7 @@ bool MidiParser_MSC::loadMusic(byte *data, uint32 size) { _lastEvent = 0; _trackEnd = data + size; - _num_tracks = 1; + _numTracks = 1; _tracks[0] = pos; setTempo(500000); @@ -224,7 +225,12 @@ MidiPlayer_MSC::MidiPlayer_MSC() : _paused(false) { MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM); - _driver = MidiDriver::createMidi(dev); + const MusicType musicType = MidiDriver::getMusicType(dev); + if (musicType == MT_ADLIB) { + _driver = createAdLibDriver(); + } else { + _driver = MidiDriver::createMidi(dev); + } assert(_driver); int ret = _driver->open(); diff --git a/engines/parallaction/sound_ns.cpp b/engines/parallaction/sound_ns.cpp index dcc71e4f2f..0ee3d73556 100644 --- a/engines/parallaction/sound_ns.cpp +++ b/engines/parallaction/sound_ns.cpp @@ -168,13 +168,13 @@ void DosSoundMan_ns::playCharacterMusic(const char *character) { char *name = const_cast<char *>(character); const char *newMusicFile = 0; - if (!scumm_stricmp(name, _dinoName)) { + if (!scumm_stricmp(name, g_dinoName)) { newMusicFile = "dino"; } else - if (!scumm_stricmp(name, _donnaName)) { + if (!scumm_stricmp(name, g_donnaName)) { newMusicFile = "donna"; } else - if (!scumm_stricmp(name, _doughName)) { + if (!scumm_stricmp(name, g_doughName)) { newMusicFile = "nuts"; } else { warning("unknown character '%s' in DosSoundMan_ns_ns::playCharacterMusic", character); diff --git a/engines/parallaction/staticres.cpp b/engines/parallaction/staticres.cpp index 73e78cae3c..f09b1241bc 100644 --- a/engines/parallaction/staticres.cpp +++ b/engines/parallaction/staticres.cpp @@ -89,14 +89,14 @@ byte Input::_resMouseArrow_BR_Amiga[512] = { /* This palette snippet is used for animations in Big Red Adventure. */ -byte _braAmigaFramesDefaultPalette[48] = { +byte braAmigaFramesDefaultPalette[48] = { 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0xFF, 0xE0, 0xCF, 0x7F, 0x7F, 0x7F, 0xD9, 0x9C, 0x84, 0x00, 0x9E, 0xF0, 0x91, 0xCC, 0x36, 0xFF, 0x6A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x11, 0xB0, 0xEE, 0xF0, 0xFF, 0x17, 0x3D, 0x18, 0xAC, 0x3A, 0xB0, 0x00, 0x00, 0x7D, 0x00, 0x00, 0xFF, 0xA8, 0xFF, }; -byte _amigaTopazFont[2600] = { +byte amigaTopazFont[2600] = { 0x00, 0x00, 0x03, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x79, 0x00, 0x00, 0x03, 0xe9, 0x00, 0x00, 0x02, 0x79, 0x70, 0xff, 0x4e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, @@ -263,7 +263,7 @@ byte _amigaTopazFont[2600] = { }; -const char *_callableNamesRes_ns[] = { +const char *callableNamesRes_ns[] = { "Projector", "HBOff", "StartIntro", @@ -292,7 +292,7 @@ const char *_callableNamesRes_ns[] = { }; -const char *_callableNamesRes_br[] = { +const char *callableNamesRes_br[] = { "blufade", "resetpalette", "ferrcycle", @@ -301,15 +301,15 @@ const char *_callableNamesRes_br[] = { "password" }; -const char *_dinoName = "dino"; -const char *_donnaName = "donna"; -const char *_doughName = "dough"; -const char *_drkiName = "drki"; +const char *g_dinoName = "dino"; +const char *g_donnaName = "donna"; +const char *g_doughName = "dough"; +const char *g_drkiName = "drki"; -const char *_minidinoName = "minidino"; -const char *_minidonnaName = "minidonna"; -const char *_minidoughName = "minidough"; -const char *_minidrkiName = "minidrki"; +const char *g_minidinoName = "minidino"; +const char *g_minidonnaName = "minidonna"; +const char *g_minidoughName = "minidough"; +const char *g_minidrkiName = "minidrki"; #define CALLABLE_NS(x) &Parallaction_ns::x @@ -391,7 +391,7 @@ const Parallaction_br::Callable Parallaction_br::_amigaCallables[] = { void Parallaction_ns::initResources() { - _callableNames = new Table(ARRAYSIZE(_callableNamesRes_ns), _callableNamesRes_ns); + _callableNames = new Table(ARRAYSIZE(callableNamesRes_ns), callableNamesRes_ns); _localFlagNames = new FixedTable(NUM_LOCATIONS, 1); _localFlagNames->addData("visited"); @@ -406,7 +406,7 @@ void Parallaction_ns::initResources() { void Parallaction_br::initResources() { - _callableNames = new Table(ARRAYSIZE(_callableNamesRes_br), _callableNamesRes_br); + _callableNames = new Table(ARRAYSIZE(callableNamesRes_br), callableNamesRes_br); _localFlagNames = new FixedTable(NUM_LOCATIONS, 2); _localFlagNames->addData("visited"); diff --git a/engines/parallaction/walk.cpp b/engines/parallaction/walk.cpp index 53237db4ef..19162cd7db 100644 --- a/engines/parallaction/walk.cpp +++ b/engines/parallaction/walk.cpp @@ -55,27 +55,27 @@ WalkFrames _char24WalkFrames_NS = { }; static int getPathWidth() { - if (!_vm->_gfx->_backgroundInfo->_path) { + if (!g_vm->_gfx->_backgroundInfo->_path) { warning("getPathWidth() _path is NULL!"); return 0; } else - return _vm->_gfx->_backgroundInfo->_path->w; + return g_vm->_gfx->_backgroundInfo->_path->w; } static int getPathHeight() { - if (!_vm->_gfx->_backgroundInfo->_path) { + if (!g_vm->_gfx->_backgroundInfo->_path) { warning("getPathHeight() _path is NULL!"); return 0; } else - return _vm->_gfx->_backgroundInfo->_path->h; + return g_vm->_gfx->_backgroundInfo->_path->h; } static bool isPathClear(uint16 x, uint16 y) { - if (!_vm->_gfx->_backgroundInfo->_path) { + if (!g_vm->_gfx->_backgroundInfo->_path) { warning("isPathClear() _path is NULL!"); return false; } else - return (_vm->_gfx->_backgroundInfo->_path->getValue(x, y) ? true : false); + return (g_vm->_gfx->_backgroundInfo->_path->getValue(x, y) ? true : false); } // adjusts position towards nearest walkable point @@ -306,7 +306,7 @@ void PathWalker_NS::checkDoor(const Common::Point &foot) { } void PathWalker_NS::finalizeWalk() { - _engineFlags &= ~kEngineWalking; + g_engineFlags &= ~kEngineWalking; Common::Point foot; _a->getFoot(foot); @@ -316,7 +316,7 @@ void PathWalker_NS::finalizeWalk() { } void PathWalker_NS::walk() { - if ((_engineFlags & kEngineWalking) == 0) { + if ((g_engineFlags & kEngineWalking) == 0) { return; } @@ -382,7 +382,7 @@ void PathWalker_NS::updateDirection(const Common::Point& pos, const Common::Poin _a->setF(frames->firstWalkFrame[_direction] + (_step / frames->frameRepeat[_direction]) % frames->numWalkFrames[_direction]); } -PathWalker_NS::PathWalker_NS() : _direction(WALK_DOWN), _step(0) { +PathWalker_NS::PathWalker_NS(Parallaction *vm) : _direction(WALK_DOWN), _step(0), _vm(vm) { } bool PathWalker_BR::directPathExists(const Common::Point &from, const Common::Point &to) { @@ -481,7 +481,7 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) { } void PathWalker_BR::finalizeWalk(State &s) { - _engineFlags &= ~kEngineWalking; + g_engineFlags &= ~kEngineWalking; Common::Point foot; _character._a->getFoot(foot); @@ -508,8 +508,8 @@ void PathWalker_BR::finalizeWalk(State &s) { #if 0 // TODO: Input::walkTo must be extended to support destination frame in addition to coordinates - if (_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput() - _engineFlags &= ~FINAL_WALK_FRAME; + if (g_engineFlags & FINAL_WALK_FRAME) { // this flag is set in readInput() + g_engineFlags &= ~FINAL_WALK_FRAME; _ch._a->_frame = _moveToF; // from readInput()... } else { _ch._a->_frame = _dirFrame; // from walk() @@ -523,7 +523,7 @@ void PathWalker_BR::finalizeWalk(State &s) { } void PathWalker_BR::walk() { - if ((_engineFlags & kEngineWalking) == 0) { + if ((g_engineFlags & kEngineWalking) == 0) { return; } @@ -714,7 +714,7 @@ void PathWalker_BR::doWalk(State &s) { } } -PathWalker_BR::PathWalker_BR() { +PathWalker_BR::PathWalker_BR(Parallaction *vm) : _vm(vm) { _character._active = false; _character._step = 0; _follower._active = false; diff --git a/engines/parallaction/walk.h b/engines/parallaction/walk.h index 6796991f9d..ae6db6eaf1 100644 --- a/engines/parallaction/walk.h +++ b/engines/parallaction/walk.h @@ -49,8 +49,10 @@ class PathWalker_NS { void checkDoor(const Common::Point &foot); void updateDirection(const Common::Point& pos, const Common::Point& to); + Parallaction *_vm; + public: - PathWalker_NS(); + PathWalker_NS(Parallaction *vm); void buildPath(AnimationPtr a, uint16 x, uint16 y); void walk(); @@ -79,8 +81,10 @@ class PathWalker_BR { void doWalk(State &s); void checkTrap(const Common::Point &p); + Parallaction *_vm; + public: - PathWalker_BR(); + PathWalker_BR(Parallaction *vm); ~PathWalker_BR() { } void setCharacterPath(AnimationPtr a, uint16 x, uint16 y); |