From 8a0fea72e610f53cce37921f3df207ec1c410880 Mon Sep 17 00:00:00 2001 From: hrydgard Date: Sun, 9 Nov 2008 18:38:15 +0000 Subject: [PATCH] More AX Wii work - ax parsing corrected (hopefully), still no sound :p git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@1102 8ced0084-cf51-0410-be5f-012b33b47a6e --- .../Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj | 16 + .../Plugin_DSP_HLE/Src/Logging/Logging.cpp | 10 +- .../Plugin_DSP_HLE/Src/UCodes/UCode_AX.cpp | 432 ++++-------------- .../Plugin_DSP_HLE/Src/UCodes/UCode_AX.h | 11 +- .../Src/UCodes/UCode_AXStructs.h | 7 +- .../Plugin_DSP_HLE/Src/UCodes/UCode_AXWii.cpp | 396 ++++++++++++++++ .../Plugin_DSP_HLE/Src/UCodes/UCode_AXWii.h | 58 +++ .../Src/UCodes/UCode_AX_ADPCM.h | 89 ++++ .../Src/UCodes/UCode_AX_Voice.h | 196 ++++++++ .../Plugin_DSP_HLE/Src/UCodes/UCodes.cpp | 5 +- 10 files changed, 850 insertions(+), 370 deletions(-) create mode 100644 Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXWii.cpp create mode 100644 Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXWii.h create mode 100644 Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX_ADPCM.h create mode 100644 Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX_Voice.h diff --git a/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj b/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj index 89e9453269..00ffeb27e2 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj +++ b/Source/Plugins/Plugin_DSP_HLE/Plugin_DSP_HLE.vcproj @@ -682,10 +682,26 @@ RelativePath=".\Src\UCodes\UCode_AX.h" > + + + + + + + + > 1); - samplePos += 2; - newSamplePos += 2; - } - - int scale = 1 << (adpcm.pred_scale & 0xF); - int coef_idx = adpcm.pred_scale >> 4; - - s32 coef1 = adpcm.coefs[coef_idx * 2 + 0]; - s32 coef2 = adpcm.coefs[coef_idx * 2 + 1]; - - int temp = (samplePos & 1) ? - (g_dspInitialize.pARAM_Read_U8(samplePos >> 1) & 0xF) : - (g_dspInitialize.pARAM_Read_U8(samplePos >> 1) >> 4); - - if (temp >= 8) - temp -= 16; - - // 0x400 = 0.5 in 11-bit fixed point - int val = (scale * temp) + ((0x400 + coef1 * adpcm.yn1 + coef2 * adpcm.yn2) >> 11); - - if (val > 0x7FFF) - val = 0x7FFF; - else if (val < -0x7FFF) - val = -0x7FFF; - - adpcm.yn2 = adpcm.yn1; - adpcm.yn1 = val; - - samplePos++; - } - - return adpcm.yn1; -} - -void ADPCM_Loop(AXParamBlock& pb) -{ - if (!pb.is_stream) - { - pb.adpcm.yn1 = pb.adpcm_loop_info.yn1; - pb.adpcm.yn2 = pb.adpcm_loop_info.yn2; - pb.adpcm.pred_scale = pb.adpcm_loop_info.pred_scale; - } - //else stream and we should not attempt to replace values -} - - -// ======================================================================================= -// Volume control (ramping) -// -------------- -u16 ADPCM_Vol(u16 vol, u16 delta, u16 mixer_control) -{ - int x = vol; - if (delta && delta < 0x5000) - x += delta * 20 * 8; // unsure what the right step is - else if (delta && delta > 0x5000) - //x -= (0x10000 - delta); // this is to small, it's often 1 - x -= (0x10000 - delta) * 20 * 16; // if this was 20 * 8 the sounds in Fire Emblem and Paper Mario - // did not have time to go to zero before the were closed - - // make lower limits - if (x < 0) x = 0; - //if (pb.mixer_control < 1000 && x < pb.mixer_control) x = pb.mixer_control; // does this make - // any sense? - - // make upper limits - //if (mixer_control > 1000 && x > mixer_control) x = mixer_control; // maybe mixer_control also - // has a volume target? - //if (x >= 0x7fff) x = 0x7fff; // this seems a little high - if (x >= 0x4e20) x = 0x4e20; // add a definitive limit at 20 000 - return x; // update volume -} -// ============== - -void MixAddVoice(AXParamBlock &pb, int *templbuffer, int *temprbuffer, int _iSize) -{ -#ifdef _WIN32 - ratioFactor = 32000.0f / (float)DSound::DSound_GetSampleRate(); -#else - ratioFactor = 32000.0f / 44100.0f; -#endif - // get necessary values const u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; const u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; @@ -260,159 +171,51 @@ void MixAddVoice(AXParamBlock &pb, int *templbuffer, int *temprbuffer, int _iSiz pb.adpcm_loop_info.pred_scale = 0; pb.adpcm_loop_info.yn1 = 0; pb.adpcm_loop_info.yn2 = 0; } - // ============= - if (pb.running) +} + +int ReadOutPBs(u32 pbs_address, AXParamBlock* _pPBs, int _num) +{ + int count = 0; + u32 blockAddr = pbs_address; + + // reading and 'halfword' swap + for (int i = 0; i < _num; i++) { - // ======================================================================================= - // Set initial parameters - // ------------ - //constants - const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) * ratioFactor); - - //variables - u32 samplePos = (pb.audio_addr.cur_addr_hi << 16) | pb.audio_addr.cur_addr_lo; - u32 frac = pb.src.cur_addr_frac; - // ============= - - // ======================================================================================= - // Handle no-src streams - No src streams have pb.src_type == 2 and have pb.src.ratio_hi = 0 - // and pb.src.ratio_lo = 0. We handle that by setting the sampling ratio integer to 1. This - // makes samplePos update in the correct way. I'm unsure how we are actually supposed to - // detect that this setting. Updates did not fix this automatically. - // --------------------------------------------------------------------------------------- - // Stream settings - // src_type = 2 (most other games have src_type = 0) - // ------------ - // Affected games: - // Baten Kaitos - Eternal Wings (2003) - // Baten Kaitos - Origins (2006)? - // Soul Calibur 2: The movie music use src_type 2 but it needs no adjustment, perhaps - // the sound format plays in to, Baten use ADPCM SC2 use PCM16 - // ------------ - if(pb.src_type == 2 && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0)) + const short *pSrc = (const short *)g_dspInitialize.pGetMemoryPointer(blockAddr); + if (pSrc != NULL) { - pb.src.ratio_hi = 1; - } - // ============= - - - // ======================================================================================= - // Games that use looping to play non-looping music streams - SSBM has info in all - // pb.adpcm_loop_info parameters but has pb.audio_addr.looping = 0. If we treat these streams - // like any other looping streams the music works. I'm unsure how we are actually supposed to - // detect that these kinds of blocks should be looping. It seems like pb.mixer_control == 0 may - // identify these types of blocks. Updates did not write any looping values. - // -------------- - if( - (pb.adpcm_loop_info.pred_scale || pb.adpcm_loop_info.yn1 || pb.adpcm_loop_info.yn2) - && pb.mixer_control == 0 - && gSSBM - ) - { - pb.audio_addr.looping = 1; - } - // ============== - - // ======================================================================================= - // Walk through _iSize. _iSize = numSamples. If the game goes slow _iSize will be higher to - // compensate for that. _iSize can be as low as 100 or as high as 2000 some cases. - for (int s = 0; s < _iSize; s++) - { - int sample = 0; - frac += ratio; - u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac - - // ======================================================================================= - // Process sample format - // -------------- - switch (pb.audio_addr.sample_format) + short *pDest = (short *)&_pPBs[i]; + for (size_t p = 0; p < sizeof(AXParamBlock) / 2; p++) { - case AUDIOFORMAT_PCM8: - pb.adpcm.yn2 = pb.adpcm.yn1; //save last sample - pb.adpcm.yn1 = ((s8)g_dspInitialize.pARAM_Read_U8(samplePos)) << 8; - - if (pb.src_type == SRCTYPE_NEAREST) - { - sample = pb.adpcm.yn1; - } - else //linear interpolation - { - sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac)) >> 16; - } - - samplePos = newSamplePos; - break; - - case AUDIOFORMAT_PCM16: - pb.adpcm.yn2 = pb.adpcm.yn1; //save last sample - pb.adpcm.yn1 = (s16)(u16)((g_dspInitialize.pARAM_Read_U8(samplePos * 2) << 8) | (g_dspInitialize.pARAM_Read_U8((samplePos * 2 + 1)))); - if (pb.src_type == SRCTYPE_NEAREST) - sample = pb.adpcm.yn1; - else //linear interpolation - sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac)) >> 16; - - samplePos = newSamplePos; - break; - - case AUDIOFORMAT_ADPCM: - sample = ADPCM_Step(pb, samplePos, newSamplePos, frac); - break; - - default: - break; + pDest[p] = Common::swap16(pSrc[p]); } - // ================ - - // ======================================================================================= - // Volume control - frac &= 0xffff; - - int vol = pb.vol_env.cur_volume >> 9; - sample = sample * vol >> 8; - - if (pb.mixer_control & MIXCONTROL_RAMPING) - { - int x = pb.vol_env.cur_volume; - x += pb.vol_env.cur_volume_delta; // I'm not sure about this, can anybody find a game - // that use this? Or how does it work? - if (x < 0) x = 0; - if (x >= 0x7fff) x = 0x7fff; - pb.vol_env.cur_volume = x; // maybe not per sample?? :P - } - - int leftmix = pb.mixer.volume_left >> 5; - int rightmix = pb.mixer.volume_right >> 5; - // =============== - int left = sample * leftmix >> 8; - int right = sample * rightmix >> 8; - //adpcm has to walk from oldSamplePos to samplePos here - templbuffer[s] += left; - temprbuffer[s] += right; - - if (samplePos >= sampleEnd) - { - if (pb.audio_addr.looping == 1) - { - samplePos = loopPos; - if (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM) - ADPCM_Loop(pb); - } - else - { - pb.running = 0; - break; - } - } - } // end of the _iSize loop - // ============ - if (gVolume) // allow us to turn this off in the debugger - { - pb.mixer.volume_left = ADPCM_Vol(pb.mixer.volume_left, pb.mixer.unknown, pb.mixer_control); - pb.mixer.volume_right = ADPCM_Vol(pb.mixer.volume_right, pb.mixer.unknown2, pb.mixer_control); + blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo; + count++; } - pb.src.cur_addr_frac = (u16)frac; - pb.audio_addr.cur_addr_hi = samplePos >> 16; - pb.audio_addr.cur_addr_lo = (u16)samplePos; + else + break; + } + + // return the number of read PBs + return count; +} + +void WriteBackPBs(u32 pbs_address, AXParamBlock* _pPBs, int _num) +{ + u32 blockAddr = pbs_address; + + // write back and 'halfword'swap + for (int i = 0; i < _num; i++) + { + short* pSrc = (short*)&_pPBs[i]; + short* pDest = (short*)g_dspInitialize.pGetMemoryPointer(blockAddr); + for (size_t p = 0; p < sizeof(AXParamBlock) / 2; p++) + { + pDest[p] = Common::swap16(pSrc[p]); + } + + // next block + blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo; } } @@ -421,7 +224,7 @@ void CUCode_AX::MixAdd(short* _pBuffer, int _iSize) AXParamBlock PBs[NUMBER_OF_PBS]; // read out pbs - int numberOfPBs = ReadOutPBs(1, PBs, NUMBER_OF_PBS); + int numberOfPBs = ReadOutPBs(m_addressPBs, PBs, NUMBER_OF_PBS); if (_iSize > 1024 * 1024) _iSize = 1024 * 1024; @@ -435,6 +238,34 @@ void CUCode_AX::MixAdd(short* _pBuffer, int _iSize) CUCode_AX::Logging(_pBuffer, _iSize, 0); } + // --------------------------------------------------------------------------------------- + // Make the updates we are told to do + // This code is buggy, TODO - fix. If multiple updates in a ms, only does first. + // ------------ + for (int i = 0; i < numberOfPBs; i++) { + u16 *pDest = (u16 *)&PBs[i]; + u16 upd0 = pDest[34]; u16 upd1 = pDest[35]; u16 upd2 = pDest[36]; // num_updates + u16 upd3 = pDest[37]; u16 upd4 = pDest[38]; + u16 upd_hi = pDest[39]; // update addr + u16 upd_lo = pDest[40]; + const u32 updaddr = (u32)(upd_hi << 16) | upd_lo; + const u16 updpar = Memory_Read_U16(updaddr); + const u16 upddata = Memory_Read_U16(updaddr + 2); + // some safety checks, I hope it's enough, how long does the memory go? + if(updaddr > 0x80000000 && updaddr < 0x82000000 + && updpar < 63 && updpar > 3 && upddata >= 0 // updpar > 3 because we don't want to change + // 0-3, those are important + && (upd0 || upd1 || upd2 || upd3 || upd4) // We should use these in some way to I think + // but I don't know how or when + && gSequenced) // on and off option + { + pDest[updpar] = upddata; + } + } + + //aprintf(1, "%08x %04x %04x\n", updaddr, updpar, upddata); + // ------------ + for (int i = 0; i < numberOfPBs; i++) { AXParamBlock& pb = PBs[i]; @@ -442,7 +273,7 @@ void CUCode_AX::MixAdd(short* _pBuffer, int _iSize) } // write back out pbs - WriteBackPBs(PBs, numberOfPBs); + WriteBackPBs(m_addressPBs, PBs, numberOfPBs); for (int i = 0; i < _iSize; i++) { @@ -490,6 +321,8 @@ bool CUCode_AX::AXTask(u32& _uMail) u32 Addr__12; u32 Addr__4_1; u32 Addr__4_2; + u32 Addr__4_3; + u32 Addr__4_4; u32 Addr__5_1; u32 Addr__5_2; u32 Addr__6; @@ -507,12 +340,10 @@ bool CUCode_AX::AXTask(u32& _uMail) case AXLIST_STUDIOADDR: //00 Addr__AXStudio = Memory_Read_U32(uAddress); uAddress += 4; - if (wii_mode) - uAddress += 6; DebugLog("AXLIST studio address: %08x", Addr__AXStudio); break; - case 0x001: + case 0x001: // 2byte x 10 { u32 address = Memory_Read_U32(uAddress); uAddress += 4; @@ -549,7 +380,7 @@ bool CUCode_AX::AXTask(u32& _uMail) DebugLog("AXLIST command 0x0003 ????"); break; - case 0x0004: + case 0x0004: // AUX? Addr__4_1 = Memory_Read_U32(uAddress); uAddress += 4; Addr__4_2 = Memory_Read_U32(uAddress); @@ -572,12 +403,8 @@ bool CUCode_AX::AXTask(u32& _uMail) break; case AXLIST_SBUFFER: - // Hopefully this is where in main ram to write. Addr__AXOutSBuffer = Memory_Read_U32(uAddress); uAddress += 4; - if (wii_mode) { - uAddress += 12; - } DebugLog("AXLIST OutSBuffer address: %08x", Addr__AXOutSBuffer); break; @@ -590,10 +417,6 @@ bool CUCode_AX::AXTask(u32& _uMail) case AXLIST_COMPRESSORTABLE: // 0xa Addr__A = Memory_Read_U32(uAddress); uAddress += 4; - if (wii_mode) { - // There's one more here. -// uAddress += 4; - } DebugLog("AXLIST CompressorTable address: %08x", Addr__A); break; @@ -632,22 +455,7 @@ bool CUCode_AX::AXTask(u32& _uMail) uAddress += 6 * 4; // 6 Addresses. break; - case 0x000d: - if (wii_mode) { - uAddress += 4 * 4; // 4 addresses. another aux? - break; - } - // non-wii : fall through - - case 0x000b: - if (wii_mode) { - uAddress += 2; // one 0x8000 in rabbids - uAddress += 4 * 2; // then two RAM addressses - break; - } - // non-wii : fall through - - default: + default: { static bool bFirst = true; if (bFirst == true) @@ -665,8 +473,8 @@ bool CUCode_AX::AXTask(u32& _uMail) } // Wii AX will always show this - //PanicAlert(szTemp); - bFirst = false; + PanicAlert(szTemp); + // bFirst = false; } // unknown command so stop the execution of this TaskList @@ -683,81 +491,3 @@ bool CUCode_AX::AXTask(u32& _uMail) m_rMailHandler.PushMail(0xDCD10001); return true; } - -int CUCode_AX::ReadOutPBs(int a, AXParamBlock* _pPBs, int _num) -{ - int count = 0; - u32 blockAddr = m_addressPBs; - - // reading and 'halfword' swap - for (int i = 0; i < _num; i++) - { - const short *pSrc = (const short *)g_dspInitialize.pGetMemoryPointer(blockAddr); - if (pSrc != NULL) - { - short *pDest = (short *)&_pPBs[i]; - for (size_t p = 0; p < sizeof(AXParamBlock) / 2; p++) - { - pDest[p] = Common::swap16(pSrc[p]); - - // To avoid a performance drop in the Release build I place this in the debug - // build only - #if defined(_DEBUG) || defined(DEBUGFAST) - gLastBlock = blockAddr + p*2 + 2; // save last block location - #endif - } - // --------------------------------------------------------------------------------------- - // Make the updates we are told to do - // ------------ - if(a) // only do this once every 5 ms - { - u16 upd0 = pDest[34]; u16 upd1 = pDest[35]; u16 upd2 = pDest[36]; // num_updates - u16 upd3 = pDest[37]; u16 upd4 = pDest[38]; - u16 upd_hi = pDest[39]; // update addr - u16 upd_lo = pDest[40]; - const u32 updaddr = (u32)(upd_hi << 16) | upd_lo; - const u16 updpar = Memory_Read_U16(updaddr); - const u16 upddata = Memory_Read_U16(updaddr + 2); - // some safety checks, I hope it's enough, how long does the memory go? - if(updaddr > 0x80000000 && updaddr < 0x82000000 - && updpar < 63 && updpar > 3 && upddata >= 0 // updpar > 3 because we don't want to change - // 0-3, those are important - && (upd0 || upd1 || upd2 || upd3 || upd4) // We should use these in some way to I think - // but I don't know how or when - && gSequenced) // on and off option - { - pDest[updpar] = upddata; - } - } - //aprintf(1, "%08x %04x %04x\n", updaddr, updpar, upddata); - // ------------ - - blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo; - count++; - } - else - break; - } - - // return the number of read PBs - return count; -} - -void CUCode_AX::WriteBackPBs(AXParamBlock* _pPBs, int _num) -{ - u32 blockAddr = m_addressPBs; - - // write back and 'halfword'swap - for (int i = 0; i < _num; i++) - { - short* pSrc = (short*)&_pPBs[i]; - short* pDest = (short*)g_dspInitialize.pGetMemoryPointer(blockAddr); - for (size_t p = 0; p < sizeof(AXParamBlock) / 2; p++) - { - pDest[p] = Common::swap16(pSrc[p]); - } - - // next block - blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo; - } -} diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX.h b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX.h index 6614f8b034..8d10824462 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX.h +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX.h @@ -28,7 +28,7 @@ enum class CUCode_AX : public IUCode { public: - CUCode_AX(CMailHandler& _rMailHandler, bool wii = false); + CUCode_AX(CMailHandler& _rMailHandler); virtual ~CUCode_AX(); void HandleMail(u32 _uMail); @@ -55,14 +55,13 @@ private: int *templbuffer; int *temprbuffer; - bool wii_mode; - // ax task message handler bool AXTask(u32& _uMail); - void SendMail(u32 _uMail); - int ReadOutPBs(int a, AXParamBlock *_pPBs, int _num); - void WriteBackPBs(AXParamBlock *_pPBs, int _num); }; +int ReadOutPBs(u32 pbs_address, AXParamBlock* _pPBs, int _num); +void WriteBackPBs(u32 pbs_address, AXParamBlock* _pPBs, int _num); + + #endif // _UCODE_AX diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXStructs.h b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXStructs.h index a2ca4b1d66..f1e24384c3 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXStructs.h +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXStructs.h @@ -15,8 +15,8 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#ifndef UCODE_AX_STRUCTS -#define UCODE_AX_STRUCTS +#ifndef _UCODE_AX_STRUCTS_H +#define _UCODE_AX_STRUCTS_H struct PBMixer { @@ -203,5 +203,4 @@ enum { MIXCONTROL_RAMPING = 8, }; - -#endif +#endif // _UCODE_AX_STRUCTS_H diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXWii.cpp b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXWii.cpp new file mode 100644 index 0000000000..6ff90ee0c4 --- /dev/null +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXWii.cpp @@ -0,0 +1,396 @@ +// Copyright (C) 2003-2008 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "../Debugger/Debugger.h" +#include "../Logging/Console.h" // for aprintf + +#ifdef _WIN32 +#include "../PCHW/DSoundStream.h" +#endif +#include "../PCHW/Mixer.h" +#include "../MailHandler.h" + +#include "UCodes.h" +#include "UCode_AXStructs.h" +#include "UCode_AXWii.h" +#include "UCode_AX_Voice.h" + +CUCode_AXWii::CUCode_AXWii(CMailHandler& _rMailHandler) + : IUCode(_rMailHandler) + , m_addressPBs(0xFFFFFFFF) +{ + // we got loaded + m_rMailHandler.PushMail(0xDCD10000); + m_rMailHandler.PushMail(0x80000000); // handshake ??? only (crc == 0xe2136399) needs it ... + + templbuffer = new int[1024 * 1024]; + temprbuffer = new int[1024 * 1024]; +} + +CUCode_AXWii::~CUCode_AXWii() +{ + m_rMailHandler.Clear(); + delete [] templbuffer; + delete [] temprbuffer; +} + +void CUCode_AXWii::HandleMail(u32 _uMail) +{ + if ((_uMail & 0xFFFF0000) == MAIL_AX_ALIST) + { + // a new List + } + else + { + AXTask(_uMail); + } +} + +int ReadOutPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num) +{ + int count = 0; + u32 blockAddr = pbs_address; + + // reading and 'halfword' swap + for (int i = 0; i < _num; i++) + { + const short *pSrc = (const short *)g_dspInitialize.pGetMemoryPointer(blockAddr); + if (pSrc != NULL) + { + short *pDest = (short *)&_pPBs[i]; + for (size_t p = 0; p < sizeof(AXParamBlock) / 2; p++) + { + pDest[p] = Common::swap16(pSrc[p]); + } + blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo; + count++; + } + else + break; + } + + // return the number of read PBs + return count; +} + +void WriteBackPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num) +{ + u32 blockAddr = pbs_address; + + // write back and 'halfword'swap + for (int i = 0; i < _num; i++) + { + short* pSrc = (short*)&_pPBs[i]; + short* pDest = (short*)g_dspInitialize.pGetMemoryPointer(blockAddr); + for (size_t p = 0; p < sizeof(AXParamBlockWii) / 2; p++) + { + pDest[p] = Common::swap16(pSrc[p]); + } + + // next block + blockAddr = (_pPBs[i].next_pb_hi << 16) | _pPBs[i].next_pb_lo; + } +} + +void CUCode_AXWii::MixAdd(short* _pBuffer, int _iSize) +{ + AXParamBlockWii PBs[NUMBER_OF_PBS]; + + // read out pbs + int numberOfPBs = ReadOutPBsWii(m_addressPBs, PBs, NUMBER_OF_PBS); + + if (_iSize > 1024 * 1024) + _iSize = 1024 * 1024; + + memset(templbuffer, 0, _iSize * sizeof(int)); + memset(temprbuffer, 0, _iSize * sizeof(int)); + + // write logging data to debugger + //if (m_frame) + { +// CUCode_AXWii::Logging(_pBuffer, _iSize, 0); + } + + // --------------------------------------------------------------------------------------- + // Make the updates we are told to do + // This code is buggy, TODO - fix. If multiple updates in a ms, only does first. + // ------------ + /* + for (int i = 0; i < numberOfPBs; i++) { + u16 *pDest = (u16 *)&PBs[i]; + u16 upd0 = pDest[34]; u16 upd1 = pDest[35]; u16 upd2 = pDest[36]; // num_updates + u16 upd_hi = pDest[39]; // update addr + u16 upd_lo = pDest[40]; + const u32 updaddr = (u32)(upd_hi << 16) | upd_lo; + const u16 updpar = Memory_Read_U16(updaddr); + const u16 upddata = Memory_Read_U16(updaddr + 2); + // some safety checks, I hope it's enough, how long does the memory go? + if(updaddr > 0x80000000 && updaddr < 0x82000000 + && updpar < 63 && updpar > 3 && upddata >= 0 // updpar > 3 because we don't want to change + // 0-3, those are important + && (upd0 || upd1 || upd2 || upd3 || upd4) // We should use these in some way to I think + // but I don't know how or when + && gSequenced) // on and off option + { + pDest[updpar] = upddata; + } + }*/ + + //aprintf(1, "%08x %04x %04x\n", updaddr, updpar, upddata); + // ------------ + + for (int i = 0; i < numberOfPBs; i++) + { + AXParamBlockWii& pb = PBs[i]; + MixAddVoice(pb, templbuffer, temprbuffer, _iSize); + } + + // write back out pbs + WriteBackPBsWii(m_addressPBs, PBs, numberOfPBs); + + for (int i = 0; i < _iSize; i++) + { + // Clamp into 16-bit. Maybe we should add a volume compressor here. + int left = templbuffer[i] + _pBuffer[0]; + int right = temprbuffer[i] + _pBuffer[1]; + if (left < -32767) left = -32767; + if (left > 32767) left = 32767; + if (right < -32767) right = -32767; + if (right > 32767) right = 32767; + *_pBuffer++ = left; + *_pBuffer++ = right; + } + + // write logging data to debugger again after the update + //if (m_frame) + { +// CUCode_AXWii::Logging(_pBuffer, _iSize, 1); + } +} + + +void CUCode_AXWii::Update() +{ + // check if we have to sent something + if (!m_rMailHandler.IsEmpty()) + { + g_dspInitialize.pGenerateDSPInterrupt(); + } +} + +// AX seems to bootup one task only and waits for resume-callbacks +// everytime the DSP has "spare time" it sends a resume-mail to the CPU +// and the __DSPHandler calls a AX-Callback which generates a new AXFrame +bool CUCode_AXWii::AXTask(u32& _uMail) +{ + u32 uAddress = _uMail; + DebugLog("AXTask - AXCommandList-Addr: 0x%08x", uAddress); + + u32 Addr__AXStudio; + u32 Addr__AXOutSBuffer; + u32 Addr__AXOutSBuffer_1; + u32 Addr__AXOutSBuffer_2; + u32 Addr__A; + u32 Addr__12; + u32 Addr__4_1; + u32 Addr__4_2; + u32 Addr__4_3; + u32 Addr__4_4; + u32 Addr__5_1; + u32 Addr__5_2; + u32 Addr__6; + u32 Addr__9; + + bool bExecuteList = true; + + if (true) + { + // PanicAlert("%i", sizeof(AXParamBlockWii)); // 252 ?? + FILE *f = fopen("D:\\axdump.txt", "a"); + if (!f) + f = fopen("D:\\axdump.txt", "w"); + + u32 addr = uAddress; + for (int i = 0; i < 100; i++) { + fprintf(f, "%02x\n", Memory_Read_U16(addr + i * 2)); + } + fprintf(f, "===========------------------------------------------------=\n"); + fclose(f); + } + else + { + // PanicAlert("%i", sizeof(AXParamBlock)); // 192 + } + + while (bExecuteList) + { + static int last_valid_command = 0; + u16 iCommand = Memory_Read_U16(uAddress); + uAddress += 2; + switch (iCommand) + { + case 0x0000: //00 + Addr__AXStudio = Memory_Read_U32(uAddress); + uAddress += 4; + DebugLog("AXLIST studio address: %08x", Addr__AXStudio); + break; + + case 0x0001: + { + u32 address = Memory_Read_U32(uAddress); + uAddress += 4; + DebugLog("AXLIST 1: %08x", address); + } + break; + + // + // Somewhere we should be getting a bitmask of AX_SYNC values + // that tells us what has been updated + // Dunno if important + // + case 0x0002: //02 + { + m_addressPBs = Memory_Read_U32(uAddress); + uAddress += 4; + + mixer_HLEready = true; + DebugLog("AXLIST PB address: %08x", m_addressPBs); +#ifdef _WIN32 + DebugLog("Update the SoundThread to be in sync"); + DSound::DSound_UpdateSound(); //do it in this thread to avoid sync problems +#endif + } + break; + + case 0x0003: + DebugLog("AXLIST command 0x0003 ????"); + break; + + case 0x0004: // AUX? + Addr__4_1 = Memory_Read_U32(uAddress); + uAddress += 4; + DebugLog("AXLIST 4 address: %08x", Addr__4_1); + break; + + case 0x0005: + Addr__5_1 = Memory_Read_U32(uAddress); + uAddress += 4; + Addr__5_2 = Memory_Read_U32(uAddress); + uAddress += 4; + + uAddress += 2; + DebugLog("AXLIST 5_1 5_2 addresses: %08x %08x", Addr__5_1, Addr__5_2); + break; + + case 0x0006: + Addr__6 = Memory_Read_U32(uAddress); + uAddress += 10; + DebugLog("AXLIST 6 address: %08x", Addr__6); + break; + + case 0x0007: // AXLIST_SBUFFER + Addr__AXOutSBuffer = Memory_Read_U32(uAddress); + uAddress += 4; + // uAddress += 12; + DebugLog("AXLIST OutSBuffer address: %08x", Addr__AXOutSBuffer); + break; + + case 0x0009: + Addr__9 = Memory_Read_U32(uAddress); + uAddress += 4; + DebugLog("AXLIST 6 address: %08x", Addr__9); + break; + + case 0x000a: // AXLIST_COMPRESSORTABLE + Addr__A = Memory_Read_U32(uAddress); + uAddress += 4; + //Addr__A = Memory_Read_U32(uAddress); + uAddress += 4; + DebugLog("AXLIST CompressorTable address: %08x", Addr__A); + break; + + case 0x000b: + uAddress += 2; // one 0x8000 in rabbids + uAddress += 4 * 2; // then two RAM addressses + break; + + case 0x000d: + uAddress += 4 * 4; + break; + + case 0x000e: + // This is the end. + bExecuteList = false; + DebugLog("AXLIST end, wii stylee."); + break; + + case 0x0010: //Super Monkey Ball 2 + DebugLog("AXLIST unknown"); + //should probably read/skip stuff here + uAddress += 8; + break; + + case 0x0011: + uAddress += 4; + break; + + case 0x0012: + Addr__12 = Memory_Read_U16(uAddress); + uAddress += 2; + break; + + case 0x0013: + uAddress += 6 * 4; // 6 Addresses. + break; + + + default: + { + static bool bFirst = true; + if (bFirst == true) + { + char szTemp[2048]; + sprintf(szTemp, "Unknown AX-Command 0x%x (address: 0x%08x). Last valid: %02x\n", + iCommand, uAddress - 2, last_valid_command); + int num = -32; + while (num < 64+32) + { + char szTemp2[128] = ""; + sprintf(szTemp2, "%s0x%04x\n", num == 0 ? ">>" : " ", Memory_Read_U16(uAddress + num)); + strcat(szTemp, szTemp2); + num += 2; + } + + // Wii AX will always show this + PanicAlert(szTemp); + // bFirst = false; + } + + // unknown command so stop the execution of this TaskList + bExecuteList = false; + } + break; + } + if (bExecuteList) + last_valid_command = iCommand; + } + DebugLog("AXTask - done, send resume"); + + // i hope resume is okay AX + m_rMailHandler.PushMail(0xDCD10001); + return true; +} diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXWii.h b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXWii.h new file mode 100644 index 0000000000..7445e68ed8 --- /dev/null +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AXWii.h @@ -0,0 +1,58 @@ +// Copyright (C) 2003-2008 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_AXWII +#define _UCODE_AXWII + +#include "UCode_AXStructs.h" + +#define NUMBER_OF_PBS 64 + +class CUCode_AXWii : public IUCode +{ +public: + CUCode_AXWii(CMailHandler& _rMailHandler); + virtual ~CUCode_AXWii(); + + void HandleMail(u32 _uMail); + void MixAdd(short* _pBuffer, int _iSize); + void Update(); + + // this is a little ugly perhaps, feel free to move it out of here + void Logging(short* _pBuffer, int _iSize, int a); + +private: + enum + { + MAIL_AX_ALIST = 0xBABE0000, + }; + + // PBs + u32 m_addressPBs; + + int *templbuffer; + int *temprbuffer; + + // ax task message handler + bool AXTask(u32& _uMail); + void SendMail(u32 _uMail); +}; + +int ReadOutPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num); +void WriteBackPBsWii(u32 pbs_address, AXParamBlockWii* _pPBs, int _num); + +#endif // _UCODE_AXWII diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX_ADPCM.h b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX_ADPCM.h new file mode 100644 index 0000000000..ab69842595 --- /dev/null +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX_ADPCM.h @@ -0,0 +1,89 @@ +// Copyright (C) 2003-2008 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_AX_ADPCM_H +#define _UCODE_AX_ADPCM_H + +inline s16 ADPCM_Step(PBADPCMInfo &adpcm, u32& samplePos, u32 newSamplePos, u16 frac) +{ + while (samplePos < newSamplePos) + { + if ((samplePos & 15) == 0) + { + adpcm.pred_scale = g_dspInitialize.pARAM_Read_U8((samplePos & ~15) >> 1); + samplePos += 2; + newSamplePos += 2; + } + + int scale = 1 << (adpcm.pred_scale & 0xF); + int coef_idx = adpcm.pred_scale >> 4; + + s32 coef1 = adpcm.coefs[coef_idx * 2 + 0]; + s32 coef2 = adpcm.coefs[coef_idx * 2 + 1]; + + int temp = (samplePos & 1) ? + (g_dspInitialize.pARAM_Read_U8(samplePos >> 1) & 0xF) : + (g_dspInitialize.pARAM_Read_U8(samplePos >> 1) >> 4); + + if (temp >= 8) + temp -= 16; + + // 0x400 = 0.5 in 11-bit fixed point + int val = (scale * temp) + ((0x400 + coef1 * adpcm.yn1 + coef2 * adpcm.yn2) >> 11); + + if (val > 0x7FFF) + val = 0x7FFF; + else if (val < -0x7FFF) + val = -0x7FFF; + + adpcm.yn2 = adpcm.yn1; + adpcm.yn1 = val; + + samplePos++; + } + + return adpcm.yn1; +} + +// ======================================================================================= +// Volume control (ramping) +// -------------- +inline u16 ADPCM_Vol(u16 vol, u16 delta, u16 mixer_control) +{ + int x = vol; + if (delta && delta < 0x5000) + x += delta * 20 * 8; // unsure what the right step is + else if (delta && delta > 0x5000) + //x -= (0x10000 - delta); // this is to small, it's often 1 + x -= (0x10000 - delta) * 20 * 16; // if this was 20 * 8 the sounds in Fire Emblem and Paper Mario + // did not have time to go to zero before the were closed + + // make lower limits + if (x < 0) x = 0; + //if (pb.mixer_control < 1000 && x < pb.mixer_control) x = pb.mixer_control; // does this make + // any sense? + + // make upper limits + //if (mixer_control > 1000 && x > mixer_control) x = mixer_control; // maybe mixer_control also + // has a volume target? + //if (x >= 0x7fff) x = 0x7fff; // this seems a little high + if (x >= 0x4e20) x = 0x4e20; // add a definitive limit at 20 000 + return x; // update volume +} +// ============== + +#endif // _UCODE_AX_ADPCM_H diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX_Voice.h b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX_Voice.h new file mode 100644 index 0000000000..1362ea9635 --- /dev/null +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCode_AX_Voice.h @@ -0,0 +1,196 @@ +// Copyright (C) 2003-2008 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _UCODE_AX_VOICE_H +#define _UCODE_AX_VOICE_H + +#include "UCode_AX_ADPCM.h" + + +template +inline void MixAddVoice(ParamBlockType &pb, int *templbuffer, int *temprbuffer, int _iSize) +{ +#ifdef _WIN32 + float ratioFactor = 32000.0f / (float)DSound::DSound_GetSampleRate(); +#else + float ratioFactor = 32000.0f / 44100.0f; +#endif + + // DoVoiceHacks(pb); + + // ============= + if (pb.running) + { + // ======================================================================================= + // Read initial parameters + // ------------ + //constants + const u32 ratio = (u32)(((pb.src.ratio_hi << 16) + pb.src.ratio_lo) * ratioFactor); + const u32 sampleEnd = (pb.audio_addr.end_addr_hi << 16) | pb.audio_addr.end_addr_lo; + const u32 loopPos = (pb.audio_addr.loop_addr_hi << 16) | pb.audio_addr.loop_addr_lo; + + //variables + u32 samplePos = (pb.audio_addr.cur_addr_hi << 16) | pb.audio_addr.cur_addr_lo; + u32 frac = pb.src.cur_addr_frac; + // ============= + + // ======================================================================================= + // Handle no-src streams - No src streams have pb.src_type == 2 and have pb.src.ratio_hi = 0 + // and pb.src.ratio_lo = 0. We handle that by setting the sampling ratio integer to 1. This + // makes samplePos update in the correct way. I'm unsure how we are actually supposed to + // detect that this setting. Updates did not fix this automatically. + // --------------------------------------------------------------------------------------- + // Stream settings + // src_type = 2 (most other games have src_type = 0) + // ------------ + // Affected games: + // Baten Kaitos - Eternal Wings (2003) + // Baten Kaitos - Origins (2006)? + // Soul Calibur 2: The movie music use src_type 2 but it needs no adjustment, perhaps + // the sound format plays in to, Baten use ADPCM SC2 use PCM16 + // ------------ + if (pb.src_type == 2 && (pb.src.ratio_hi == 0 && pb.src.ratio_lo == 0)) + { + pb.src.ratio_hi = 1; + } + // ============= + + // ======================================================================================= + // Games that use looping to play non-looping music streams - SSBM has info in all + // pb.adpcm_loop_info parameters but has pb.audio_addr.looping = 0. If we treat these streams + // like any other looping streams the music works. I'm unsure how we are actually supposed to + // detect that these kinds of blocks should be looping. It seems like pb.mixer_control == 0 may + // identify these types of blocks. Updates did not write any looping values. + // -------------- + if ( + (pb.adpcm_loop_info.pred_scale || pb.adpcm_loop_info.yn1 || pb.adpcm_loop_info.yn2) + && pb.mixer_control == 0 + + ) + { + pb.audio_addr.looping = 1; + } + // ============== + + // ======================================================================================= + // Walk through _iSize. _iSize = numSamples. If the game goes slow _iSize will be higher to + // compensate for that. _iSize can be as low as 100 or as high as 2000 some cases. + for (int s = 0; s < _iSize; s++) + { + int sample = 0; + frac += ratio; + u32 newSamplePos = samplePos + (frac >> 16); //whole number of frac + + // ======================================================================================= + // Process sample format + // -------------- + switch (pb.audio_addr.sample_format) + { + case AUDIOFORMAT_PCM8: + // TODO - the linear interpolation code below is somewhat suspicious + pb.adpcm.yn2 = pb.adpcm.yn1; //save last sample + pb.adpcm.yn1 = ((s8)g_dspInitialize.pARAM_Read_U8(samplePos)) << 8; + + if (pb.src_type == SRCTYPE_NEAREST) + { + sample = pb.adpcm.yn1; + } + else //linear interpolation + { + sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac)) >> 16; + } + + samplePos = newSamplePos; + break; + + case AUDIOFORMAT_PCM16: + // TODO - the linear interpolation code below is somewhat suspicious + pb.adpcm.yn2 = pb.adpcm.yn1; //save last sample + pb.adpcm.yn1 = (s16)(u16)((g_dspInitialize.pARAM_Read_U8(samplePos * 2) << 8) | (g_dspInitialize.pARAM_Read_U8((samplePos * 2 + 1)))); + if (pb.src_type == SRCTYPE_NEAREST) + sample = pb.adpcm.yn1; + else //linear interpolation + sample = (pb.adpcm.yn1 * (u16)frac + pb.adpcm.yn2 * (u16)(0xFFFF - frac)) >> 16; + + samplePos = newSamplePos; + break; + + case AUDIOFORMAT_ADPCM: + sample = ADPCM_Step(pb.adpcm, samplePos, newSamplePos, frac); + break; + + default: + break; + } + // ================ + + // ======================================================================================= + // Volume control + frac &= 0xffff; + + int vol = pb.vol_env.cur_volume >> 9; + sample = sample * vol >> 8; + + if (pb.mixer_control & MIXCONTROL_RAMPING) + { + int x = pb.vol_env.cur_volume; + x += pb.vol_env.cur_volume_delta; // I'm not sure about this, can anybody find a game + // that use this? Or how does it work? + if (x < 0) x = 0; + if (x >= 0x7fff) x = 0x7fff; + pb.vol_env.cur_volume = x; // maybe not per sample?? :P + } + + int leftmix = pb.mixer.volume_left >> 5; + int rightmix = pb.mixer.volume_right >> 5; + // =============== + int left = sample * leftmix >> 8; + int right = sample * rightmix >> 8; + //adpcm has to walk from oldSamplePos to samplePos here + templbuffer[s] += left; + temprbuffer[s] += right; + + if (samplePos >= sampleEnd) + { + if (pb.audio_addr.looping == 1) + { + samplePos = loopPos; + if (pb.audio_addr.sample_format == AUDIOFORMAT_ADPCM) + { + if (!pb.is_stream) + { + pb.adpcm.yn1 = pb.adpcm_loop_info.yn1; + pb.adpcm.yn2 = pb.adpcm_loop_info.yn2; + pb.adpcm.pred_scale = pb.adpcm_loop_info.pred_scale; + } + } + } + else + { + pb.running = 0; + break; + } + } + } // end of the _iSize loop + + pb.src.cur_addr_frac = (u16)frac; + pb.audio_addr.cur_addr_hi = samplePos >> 16; + pb.audio_addr.cur_addr_lo = (u16)samplePos; + } +} + +#endif // _UCODE_AX_VOICE_H diff --git a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCodes.cpp b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCodes.cpp index 6079db9438..2016e1bca8 100644 --- a/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCodes.cpp +++ b/Source/Plugins/Plugin_DSP_HLE/Src/UCodes/UCodes.cpp @@ -20,6 +20,7 @@ #include "UCodes.h" #include "UCode_AX.h" +#include "UCode_AXWii.h" #include "UCode_Zelda.h" #include "UCode_Jac.h" #include "UCode_ROM.h" @@ -72,8 +73,8 @@ IUCode* UCodeFactory(u32 _CRC, CMailHandler& _rMailHandler) return new CUCode_Zelda(_rMailHandler); case 0x347112ba: // raving rabbits - DebugLog("Wii - AX chosen"); - return new CUCode_AX(_rMailHandler, true); + DebugLog("Wii - AXWii chosen"); + return new CUCode_AXWii(_rMailHandler); default: PanicAlert("Unknown ucode (CRC = %08x) - forcing AX", _CRC);