mirror of
https://github.com/mupen64plus/mupen64plus-core.git
synced 2024-06-02 19:27:51 -04:00
Merge pull request #873 from loganmc10/vru
VRU support and Hey You Pikachu audio fix
This commit is contained in:
commit
6efa95f5dd
|
@ -6294,6 +6294,7 @@ CRC=D3F10E5D 052EA579
|
|||
Players=1
|
||||
SaveType=Eeprom 4KB
|
||||
Rumble=Yes
|
||||
AiDmaModifier=88
|
||||
|
||||
[DCC316EFFC4928F5B0AE8D273D8024BF]
|
||||
GoodName=HiRes CFB Demo (PD)
|
||||
|
@ -11893,6 +11894,7 @@ CRC=3F245305 FC0B74AA
|
|||
Players=1
|
||||
SaveType=Eeprom 4KB
|
||||
Rumble=Yes
|
||||
AiDmaModifier=88
|
||||
|
||||
[D0AE6C07AC0481EBA5FF9CE798A69574]
|
||||
GoodName=Pikachu Genki Dechu (J) [b1]
|
||||
|
|
|
@ -128,4 +128,6 @@ This is the most complicated interface, because it involves 3 components: the vi
|
|||
* '''FRONTEND_API_VERSION''' version 2.1.4:
|
||||
** added "M64CMD_ROM_SET_SETTINGS" command to allow setting ROM settings for the currently opened ROM until the ROM is closed.
|
||||
* '''CONFIG_API_VERSION''' version 2.3.2:
|
||||
** add ConfigOverrideUserPaths() function to allow front-ends to override user paths.
|
||||
** add ConfigOverrideUserPaths() function to allow front-ends to override user paths.
|
||||
* '''INPUT_API_VERSION''' version 2.1.1:
|
||||
** add optional functions: SendVRUWord(), SetMicState(), ReadVRUResults(), ClearVRUWords(), SetVRUWordMask(). These functions add support for the VRU (Voice Recognition Unit). Also added a new int to the CONTROL struct: Type. Type can be CONT_TYPE_STANDARD (0) or CONT_TYPE_VRU (1).
|
||||
|
|
|
@ -224,6 +224,21 @@ These functions are present in all of the plugins.
|
|||
|-
|
||||
|<tt>void SDL_KeyUp(int keymod, int keysym);</tt>
|
||||
|Pass a SDL_KEYUP-style message to the input plugin
|
||||
|-
|
||||
|<tt>void SendVRUWord(uint16_t length, uint16_t *word, uint8_t lang);</tt>
|
||||
|Load a word/phrase into the VRU dictionary. length specifies the length of the buffer. lang is 0 for English and 1 for Japanese. Japanese words are encoding using S-JIS. English words are encoded using a phonetic alphabet explained here: https://pastebin.com/ajLzRLze
|
||||
|-
|
||||
|<tt>void SetMicState(int state);</tt>
|
||||
|A state of 1 tells the input plugin to enable the microphone. A state of 0 tells the input plugin to disable the microphone
|
||||
|-
|
||||
|<tt>void ReadVRUResults(uint16_t *error_flags, uint16_t *num_results, uint16_t *mic_level, uint16_t *voice_level, uint16_t *voice_length, uint16_t *matches);</tt>
|
||||
|Refer to https://pastebin.com/6UiErk5h and https://ultra64.ca/files/documentation/online-manuals/man/pro-man/pro26/index26.8.html for an explanation of these variables. This function is called after the player has finished speaking. The purpose is for the VRU/input plugin to tell the core if any matches were found against the dictionary that was provided earlier
|
||||
|-
|
||||
|<tt>void ClearVRUWords(uint8_t length);</tt>
|
||||
|Clears and initializes the word/phrase dictionary with a max length as provided by the variable
|
||||
|-
|
||||
|<tt>void SetVRUWordMask(uint8_t length, uint8_t *mask);</tt>
|
||||
|Assign a mask to the words in the dictionary. A bit of 1 means to recognize the word, 0 means to ignore the word. See https://ultra64.ca/files/documentation/online-manuals/man/pro-man/pro26/index26.8.html
|
||||
|}
|
||||
|
||||
=== Remove From Older Input API ===
|
||||
|
|
|
@ -136,6 +136,7 @@
|
|||
<ClCompile Include="..\..\src\device\cart\is_viewer.c" />
|
||||
<ClCompile Include="..\..\src\device\cart\sram.c" />
|
||||
<ClCompile Include="..\..\src\device\controllers\game_controller.c" />
|
||||
<ClCompile Include="..\..\src\device\controllers\vru_controller.c" />
|
||||
<ClCompile Include="..\..\src\device\controllers\paks\biopak.c" />
|
||||
<ClCompile Include="..\..\src\device\controllers\paks\mempak.c" />
|
||||
<ClCompile Include="..\..\src\device\controllers\paks\rumblepak.c" />
|
||||
|
@ -440,6 +441,7 @@
|
|||
<ClInclude Include="..\..\src\device\cart\is_viewer.h" />
|
||||
<ClInclude Include="..\..\src\device\cart\sram.h" />
|
||||
<ClInclude Include="..\..\src\device\controllers\game_controller.h" />
|
||||
<ClInclude Include="..\..\src\device\controllers\vru_controller.h" />
|
||||
<ClInclude Include="..\..\src\device\controllers\paks\biopak.h" />
|
||||
<ClInclude Include="..\..\src\device\controllers\paks\mempak.h" />
|
||||
<ClInclude Include="..\..\src\device\controllers\paks\rumblepak.h" />
|
||||
|
|
|
@ -360,6 +360,9 @@
|
|||
<ClCompile Include="..\..\src\device\controllers\game_controller.c">
|
||||
<Filter>device\controllers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\device\controllers\vru_controller.c">
|
||||
<Filter>device\controllers</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\device\rdram\rdram.c">
|
||||
<Filter>device\rdram</Filter>
|
||||
</ClCompile>
|
||||
|
@ -704,6 +707,9 @@
|
|||
<ClInclude Include="..\..\src\device\controllers\game_controller.h">
|
||||
<Filter>device\controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\device\controllers\vru_controller.h">
|
||||
<Filter>device\controllers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\device\rdram\rdram.h">
|
||||
<Filter>device\rdram</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -540,6 +540,7 @@ SOURCE = \
|
|||
$(SRCDIR)/device/cart/is_viewer.c \
|
||||
$(SRCDIR)/device/cart/sram.c \
|
||||
$(SRCDIR)/device/controllers/game_controller.c \
|
||||
$(SRCDIR)/device/controllers/vru_controller.c \
|
||||
$(SRCDIR)/device/controllers/paks/biopak.c \
|
||||
$(SRCDIR)/device/controllers/paks/mempak.c \
|
||||
$(SRCDIR)/device/controllers/paks/rumblepak.c \
|
||||
|
|
|
@ -428,6 +428,7 @@ EXPORT m64p_error CALL CoreGetRomSettings(m64p_rom_settings *RomSettings, int Ro
|
|||
RomSettings->countperop = entry->countperop;
|
||||
RomSettings->savetype = entry->savetype;
|
||||
RomSettings->sidmaduration = entry->sidmaduration;
|
||||
RomSettings->aidmamodifier = entry->aidmamodifier;
|
||||
|
||||
return M64ERR_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -136,10 +136,15 @@ typedef struct {
|
|||
void (*CheckInterrupts)(void);
|
||||
} AUDIO_INFO;
|
||||
|
||||
/*** Controller types ****/
|
||||
#define CONT_TYPE_STANDARD 0
|
||||
#define CONT_TYPE_VRU 1
|
||||
|
||||
typedef struct {
|
||||
int Present;
|
||||
int RawData;
|
||||
int Plugin;
|
||||
int Type;
|
||||
} CONTROL;
|
||||
|
||||
typedef union {
|
||||
|
@ -260,6 +265,11 @@ typedef void (*ptr_ReadController)(int Control, unsigned char *Command);
|
|||
typedef void (*ptr_SDL_KeyDown)(int keymod, int keysym);
|
||||
typedef void (*ptr_SDL_KeyUp)(int keymod, int keysym);
|
||||
typedef void (*ptr_RenderCallback)(void);
|
||||
typedef void (*ptr_SendVRUWord)(uint16_t length, uint16_t *word, uint8_t lang);
|
||||
typedef void (*ptr_SetMicState)(int state);
|
||||
typedef void (*ptr_ReadVRUResults)(uint16_t *error_flags, uint16_t *num_results, uint16_t *mic_level, uint16_t *voice_level, uint16_t *voice_length, uint16_t *matches);
|
||||
typedef void (*ptr_ClearVRUWords)(uint8_t length);
|
||||
typedef void (*ptr_SetVRUWordMask)(uint8_t length, uint8_t *mask);
|
||||
#if defined(M64P_PLUGIN_PROTOTYPES)
|
||||
EXPORT void CALL ControllerCommand(int Control, unsigned char *Command);
|
||||
EXPORT void CALL GetKeys(int Control, BUTTONS *Keys);
|
||||
|
@ -268,6 +278,11 @@ EXPORT void CALL ReadController(int Control, unsigned char *Command);
|
|||
EXPORT void CALL SDL_KeyDown(int keymod, int keysym);
|
||||
EXPORT void CALL SDL_KeyUp(int keymod, int keysym);
|
||||
EXPORT void CALL RenderCallback(void);
|
||||
EXPORT void CALL SendVRUWord(uint16_t length, uint16_t *word, uint8_t lang);
|
||||
EXPORT void CALL SetMicState(int state);
|
||||
EXPORT void CALL ReadVRUResults(uint16_t *error_flags, uint16_t *num_results, uint16_t *mic_level, uint16_t *voice_level, uint16_t *voice_length, uint16_t *matches);
|
||||
EXPORT void CALL ClearVRUWords(uint8_t length);
|
||||
EXPORT void CALL SetVRUWordMask(uint8_t length, uint8_t *mask);
|
||||
#endif
|
||||
|
||||
/* RSP plugin function pointers */
|
||||
|
|
|
@ -282,6 +282,7 @@ typedef struct
|
|||
unsigned char disableextramem; /* 0 - No, 1 - Yes boolean for disabling 4MB expansion RAM pack */
|
||||
unsigned int countperop; /* Number of CPU cycles per instruction. */
|
||||
unsigned int sidmaduration; /* Default SI DMA duration */
|
||||
unsigned int aidmamodifier; /* Percentage modifier for AI DMA duration */
|
||||
} m64p_rom_settings;
|
||||
|
||||
/* ----------------------------------------- */
|
||||
|
|
|
@ -35,6 +35,11 @@ enum joybus_commands
|
|||
JCMD_AF_RTC_STATUS = 0x06,
|
||||
JCMD_AF_RTC_READ = 0x07,
|
||||
JCMD_AF_RTC_WRITE = 0x08,
|
||||
JCMD_VRU_READ = 0x09,
|
||||
JCMD_VRU_WRITE = 0x0A,
|
||||
JCMD_VRU_READ_STATUS = 0x0B,
|
||||
JCMD_VRU_WRITE_CONFIG = 0x0C,
|
||||
JCMD_VRU_WRITE_INIT = 0x0D,
|
||||
JCMD_RESET = 0xff,
|
||||
};
|
||||
|
||||
|
@ -44,6 +49,7 @@ enum joybus_device_types
|
|||
JDT_JOY_ABS_COUNTERS = 0x0001, /* joystick with absolute coordinates */
|
||||
JDT_JOY_REL_COUNTERS = 0x0002, /* joystick with relative coordinates (= mouse) */
|
||||
JDT_JOY_PORT = 0x0004, /* has port for external paks */
|
||||
JDT_VRU = 0x0100, /* VRU */
|
||||
JDT_AF_RTC = 0x1000, /* RTC */
|
||||
JDT_EEPROM_4K = 0x8000, /* 4k EEPROM */
|
||||
JDT_EEPROM_16K = 0xc000, /* 16k EEPROM */
|
||||
|
|
|
@ -155,7 +155,6 @@ const struct game_controller_flavor g_mouse_controller_flavor =
|
|||
mouse_controller_reset
|
||||
};
|
||||
|
||||
|
||||
void init_game_controller(struct game_controller* cont,
|
||||
const struct game_controller_flavor* flavor,
|
||||
void* cin, const struct controller_input_backend_interface* icin,
|
||||
|
|
|
@ -59,6 +59,12 @@ struct game_controller
|
|||
|
||||
void* pak;
|
||||
const struct pak_interface* ipak;
|
||||
|
||||
/* VRU */
|
||||
uint8_t voice_state;
|
||||
uint8_t load_offset;
|
||||
uint8_t voice_init;
|
||||
uint16_t word[40];
|
||||
};
|
||||
|
||||
void init_game_controller(struct game_controller* cont,
|
||||
|
|
254
src/device/controllers/vru_controller.c
Normal file
254
src/device/controllers/vru_controller.c
Normal file
|
@ -0,0 +1,254 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus - vru_controller.c *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* 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 "game_controller.h"
|
||||
#include "vru_controller.h"
|
||||
|
||||
#include "api/callbacks.h"
|
||||
#include "api/m64p_types.h"
|
||||
#include "backends/api/controller_input_backend.h"
|
||||
#include "backends/api/joybus.h"
|
||||
#include "plugin/plugin.h"
|
||||
#include "main/rom.h"
|
||||
|
||||
#ifdef COMPARE_CORE
|
||||
#include "api/debugger.h"
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
enum voice_status
|
||||
{
|
||||
VOICE_STATUS_READY = 0x00,
|
||||
VOICE_STATUS_START = 0x01,
|
||||
VOICE_STATUS_CANCEL = 0x03,
|
||||
VOICE_STATUS_BUSY = 0x05,
|
||||
VOICE_STATUS_END = 0x07
|
||||
};
|
||||
|
||||
static uint8_t vru_data_crc(const uint8_t* data, size_t size)
|
||||
{
|
||||
size_t i;
|
||||
uint8_t crc = 0;
|
||||
|
||||
for(i = 0; i <= size; ++i)
|
||||
{
|
||||
int mask;
|
||||
for (mask = 0x80; mask >= 1; mask >>= 1)
|
||||
{
|
||||
uint8_t xor_tap = (crc & 0x80) ? 0x85 : 0x00;
|
||||
crc <<= 1;
|
||||
if (i != size && (data[i] & mask)) crc |= 1;
|
||||
crc ^= xor_tap;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* VRU controller */
|
||||
static void vru_controller_reset(struct game_controller* cont)
|
||||
{
|
||||
cont->status = 0x00;
|
||||
if (ROM_HEADER.Country_code == 0x4A /* Japan */ || ROM_HEADER.Country_code == 0x00 /* Demo */)
|
||||
cont->voice_state = VOICE_STATUS_READY;
|
||||
else
|
||||
cont->voice_state = VOICE_STATUS_START;
|
||||
cont->load_offset = 0;
|
||||
cont->voice_init = 1;
|
||||
memset(cont->word, 0, 80);
|
||||
}
|
||||
|
||||
const struct game_controller_flavor g_vru_controller_flavor =
|
||||
{
|
||||
"VRU controller",
|
||||
JDT_VRU,
|
||||
vru_controller_reset
|
||||
};
|
||||
|
||||
static void poweron_vru_controller(void* jbd)
|
||||
{
|
||||
struct game_controller* cont = (struct game_controller*)jbd;
|
||||
|
||||
cont->flavor->reset(cont);
|
||||
}
|
||||
|
||||
static void process_vru_command(void* jbd,
|
||||
const uint8_t* tx, const uint8_t* tx_buf,
|
||||
uint8_t* rx, uint8_t* rx_buf)
|
||||
{
|
||||
struct game_controller* cont = (struct game_controller*)jbd;
|
||||
uint32_t input_ = 0;
|
||||
uint8_t cmd = tx_buf[0];
|
||||
|
||||
/* if controller can't successfully be polled, consider it to be absent */
|
||||
if (cont->icin->get_input(cont->cin, &input_) != M64ERR_SUCCESS) {
|
||||
*rx |= 0x80;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case JCMD_RESET:
|
||||
cont->flavor->reset(cont);
|
||||
/* fall through */
|
||||
case JCMD_STATUS: {
|
||||
JOYBUS_CHECK_COMMAND_FORMAT(1, 3)
|
||||
|
||||
if (cont->voice_init == 2)
|
||||
{
|
||||
/* words have been loaded, we can change the state from READY to START */
|
||||
cont->voice_state = VOICE_STATUS_START;
|
||||
cont->voice_init = 1;
|
||||
}
|
||||
else if ((input_ & 0x0020) && (cont->voice_state == VOICE_STATUS_START))
|
||||
{
|
||||
/* HACK: The Z input on the VRU controller is used to indicate that someone is talking */
|
||||
/* On Densha de Go, if the player is talking for more than ~2.5 seconds, the input is ignored */
|
||||
cont->voice_state = VOICE_STATUS_BUSY;
|
||||
cont->status = 0; /* setting the status to 0 tells the game to check the voice_status */
|
||||
}
|
||||
else if (!(input_ & 0x0020) && (cont->voice_state == VOICE_STATUS_BUSY))
|
||||
{
|
||||
cont->voice_state = VOICE_STATUS_READY;
|
||||
cont->status = 0; /* setting the status to 0 tells the game to check the voice_status */
|
||||
}
|
||||
|
||||
rx_buf[0] = (uint8_t)(cont->flavor->type >> 0);
|
||||
rx_buf[1] = (uint8_t)(cont->flavor->type >> 8);
|
||||
rx_buf[2] = cont->status;
|
||||
} break;
|
||||
|
||||
case JCMD_CONTROLLER_READ: {
|
||||
JOYBUS_CHECK_COMMAND_FORMAT(1, 4)
|
||||
#ifdef COMPARE_CORE
|
||||
CoreCompareDataSync(4, rx_buf);
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case JCMD_VRU_READ_STATUS: {
|
||||
JOYBUS_CHECK_COMMAND_FORMAT(3, 3)
|
||||
rx_buf[0] = cont->voice_init ? cont->voice_state : 0;
|
||||
rx_buf[1] = 0;
|
||||
rx_buf[2] = vru_data_crc(&rx_buf[0], 2);
|
||||
if (cont->load_offset > 0)
|
||||
{
|
||||
uint8_t offset = 0;
|
||||
while (cont->word[offset] == 0 && offset < 40)
|
||||
++offset;
|
||||
if (offset == 40)
|
||||
{
|
||||
DebugMessage(M64MSG_WARNING, "Empty JCMD_VRU_WRITE.");
|
||||
}
|
||||
else if (cont->word[offset] == 3)
|
||||
{
|
||||
offset += 3;
|
||||
uint16_t length = cont->word[offset];
|
||||
if (ROM_HEADER.Country_code == 0x4A /* Japan */ || ROM_HEADER.Country_code == 0x00 /* Demo */)
|
||||
{
|
||||
offset -= 1;
|
||||
length = 0;
|
||||
while (cont->word[offset + length] != 0)
|
||||
{
|
||||
++length;
|
||||
}
|
||||
input.sendVRUWord(length, &cont->word[offset], 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
++offset;
|
||||
input.sendVRUWord(length, &cont->word[offset], 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Unhandled command, could be a string/word mask.
|
||||
For a mask:
|
||||
"Data is right-aligned and padded with zeroes to an even length, followed with command 0004. Set bits allow strings, unset ignores."
|
||||
I haven't seen Hey You Pikachu or Densha de GO use the mask command, so I wasn't able to test.
|
||||
TODO: Call input.SetVRUWordMask() to tell the input plugin about the mask settings */
|
||||
DebugMessage(M64MSG_WARNING, "Unknown command in JCMD_VRU_WRITE.");
|
||||
}
|
||||
cont->load_offset = 0;
|
||||
}
|
||||
cont->status = 1;
|
||||
} break;
|
||||
|
||||
case JCMD_VRU_WRITE_CONFIG: {
|
||||
JOYBUS_CHECK_COMMAND_FORMAT(7, 1)
|
||||
rx_buf[0] = vru_data_crc(&tx_buf[3], 4);
|
||||
if (rx_buf[0] == 0x4E)
|
||||
{
|
||||
input.setMicState(1);
|
||||
cont->voice_init = 2;
|
||||
}
|
||||
else if (rx_buf[0] == 0xEF)
|
||||
{
|
||||
input.setMicState(0);
|
||||
}
|
||||
else if (tx_buf[3] == 0x2)
|
||||
{
|
||||
cont->voice_init = 0;
|
||||
input.clearVRUWords(tx_buf[5]);
|
||||
}
|
||||
cont->status = 0; /* status is always set to 0 after a write */
|
||||
} break;
|
||||
|
||||
case JCMD_VRU_WRITE_INIT: {
|
||||
JOYBUS_CHECK_COMMAND_FORMAT(3, 1)
|
||||
if (*((uint16_t*)(&tx_buf[1])) == 0)
|
||||
input.setMicState(0);
|
||||
rx_buf[0] = 0;
|
||||
} break;
|
||||
|
||||
case JCMD_VRU_READ: {
|
||||
JOYBUS_CHECK_COMMAND_FORMAT(3, 37)
|
||||
*((uint16_t*)(&rx_buf[0])) = 0x8000; /* as per zoinkity https://pastebin.com/6UiErk5h */
|
||||
*((uint16_t*)(&rx_buf[2])) = 0x0F00; /* as per zoinkity https://pastebin.com/6UiErk5h */
|
||||
*((uint16_t*)(&rx_buf[34])) = 0x0040; /* as per zoinkity https://pastebin.com/6UiErk5h */
|
||||
input.readVRUResults((uint16_t*)&rx_buf[4] /*error flags*/, (uint16_t*)&rx_buf[6] /*number of results*/, (uint16_t*)&rx_buf[8] /*mic level*/, \
|
||||
(uint16_t*)&rx_buf[10] /*voice level*/, (uint16_t*)&rx_buf[12] /*voice length*/, (uint16_t*)&rx_buf[14] /*matches*/);
|
||||
rx_buf[36] = vru_data_crc(&rx_buf[0], 36);
|
||||
cont->voice_state = VOICE_STATUS_START;
|
||||
} break;
|
||||
|
||||
case JCMD_VRU_WRITE: {
|
||||
JOYBUS_CHECK_COMMAND_FORMAT(23, 1)
|
||||
rx_buf[0] = vru_data_crc(&tx_buf[3], 20);
|
||||
if (cont->load_offset == 0)
|
||||
memset(cont->word, 0, 80);
|
||||
memcpy(&cont->word[cont->load_offset], &tx_buf[3], 20);
|
||||
cont->load_offset += 10;
|
||||
cont->status = 0; /* status is always set to 0 after a write */
|
||||
} break;
|
||||
|
||||
default:
|
||||
DebugMessage(M64MSG_WARNING, "cont: Unknown command %02x %02x %02x",
|
||||
*tx, *rx, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
const struct joybus_device_interface g_ijoybus_vru_controller =
|
||||
{
|
||||
poweron_vru_controller,
|
||||
process_vru_command,
|
||||
NULL
|
||||
};
|
37
src/device/controllers/vru_controller.h
Normal file
37
src/device/controllers/vru_controller.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus - vru_controller.h *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* 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. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef M64P_DEVICE_SI_VRU_CONTROLLER_H
|
||||
#define M64P_DEVICE_SI_VRU_CONTROLLER_H
|
||||
|
||||
#include "backends/api/joybus.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Controller Joybus interface */
|
||||
extern const struct joybus_device_interface
|
||||
g_ijoybus_vru_controller;
|
||||
|
||||
/* Controller flavors */
|
||||
extern const struct game_controller_flavor g_vru_controller_flavor;
|
||||
|
||||
#endif
|
|
@ -88,7 +88,7 @@ void init_device(struct device* dev,
|
|||
int randomize_interrupt,
|
||||
uint32_t start_address,
|
||||
/* ai */
|
||||
void* aout, const struct audio_out_backend_interface* iaout,
|
||||
void* aout, const struct audio_out_backend_interface* iaout, float dma_modifier,
|
||||
/* si */
|
||||
unsigned int si_dma_duration,
|
||||
/* rdram */
|
||||
|
@ -182,7 +182,7 @@ void init_device(struct device* dev,
|
|||
emumode, count_per_op, count_per_op_denom_pot, no_compiled_jump, randomize_interrupt, start_address);
|
||||
init_rdp(&dev->dp, &dev->sp, &dev->mi, &dev->mem, &dev->rdram, &dev->r4300);
|
||||
init_rsp(&dev->sp, mem_base_u32(base, MM_RSP_MEM), &dev->mi, &dev->dp, &dev->ri);
|
||||
init_ai(&dev->ai, &dev->mi, &dev->ri, &dev->vi, aout, iaout);
|
||||
init_ai(&dev->ai, &dev->mi, &dev->ri, &dev->vi, aout, iaout, dma_modifier);
|
||||
init_mi(&dev->mi, &dev->r4300);
|
||||
init_pi(&dev->pi,
|
||||
get_pi_dma_handler,
|
||||
|
|
|
@ -126,7 +126,7 @@ void init_device(struct device* dev,
|
|||
int randomize_interrupt,
|
||||
uint32_t start_address,
|
||||
/* ai */
|
||||
void* aout, const struct audio_out_backend_interface* iaout,
|
||||
void* aout, const struct audio_out_backend_interface* iaout, float dma_modifier,
|
||||
/* si */
|
||||
unsigned int si_dma_duration,
|
||||
/* rdram */
|
||||
|
|
|
@ -100,7 +100,7 @@ static void do_dma(struct ai_controller* ai, struct ai_dma* dma)
|
|||
|
||||
static void fifo_push(struct ai_controller* ai)
|
||||
{
|
||||
unsigned int duration = get_dma_duration(ai);
|
||||
unsigned int duration = get_dma_duration(ai) * ai->dma_modifier;
|
||||
|
||||
if (ai->regs[AI_STATUS_REG] & AI_STATUS_BUSY)
|
||||
{
|
||||
|
@ -144,13 +144,15 @@ void init_ai(struct ai_controller* ai,
|
|||
struct ri_controller* ri,
|
||||
struct vi_controller* vi,
|
||||
void* aout,
|
||||
const struct audio_out_backend_interface* iaout)
|
||||
const struct audio_out_backend_interface* iaout,
|
||||
float dma_modifier)
|
||||
{
|
||||
ai->mi = mi;
|
||||
ai->ri = ri;
|
||||
ai->vi = vi;
|
||||
ai->aout = aout;
|
||||
ai->iaout = iaout;
|
||||
ai->dma_modifier = dma_modifier;
|
||||
}
|
||||
|
||||
void poweron_ai(struct ai_controller* ai)
|
||||
|
|
|
@ -59,6 +59,7 @@ struct ai_controller
|
|||
unsigned int samples_format_changed;
|
||||
uint32_t last_read;
|
||||
uint32_t delayed_carry;
|
||||
float dma_modifier;
|
||||
|
||||
struct mi_controller* mi;
|
||||
struct ri_controller* ri;
|
||||
|
@ -78,7 +79,8 @@ void init_ai(struct ai_controller* ai,
|
|||
struct ri_controller* ri,
|
||||
struct vi_controller* vi,
|
||||
void* aout,
|
||||
const struct audio_out_backend_interface* iaout);
|
||||
const struct audio_out_backend_interface* iaout,
|
||||
float dma_modifier);
|
||||
|
||||
void poweron_ai(struct ai_controller* ai);
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "cheat.h"
|
||||
#include "device/device.h"
|
||||
#include "device/dd/disk.h"
|
||||
#include "device/controllers/vru_controller.h"
|
||||
#include "device/controllers/paks/biopak.h"
|
||||
#include "device/controllers/paks/mempak.h"
|
||||
#include "device/controllers/paks/rumblepak.h"
|
||||
|
@ -1641,6 +1642,27 @@ m64p_error main_run(void)
|
|||
joybus_devices[i] = &control_ids[i];
|
||||
ijoybus_devices[i] = &g_ijoybus_device_plugin_compat;
|
||||
}
|
||||
else if (Controls[i].Type == CONT_TYPE_VRU) {
|
||||
const struct game_controller_flavor* cont_flavor =
|
||||
&g_vru_controller_flavor;
|
||||
joybus_devices[i] = &g_dev.controllers[i];
|
||||
ijoybus_devices[i] = &g_ijoybus_vru_controller;
|
||||
|
||||
cin_compats[i].control_id = (int)i;
|
||||
cin_compats[i].cont = &g_dev.controllers[i];
|
||||
cin_compats[i].last_pak_type = Controls[i].Plugin;
|
||||
cin_compats[i].last_input = 0;
|
||||
cin_compats[i].netplay_count = 0;
|
||||
cin_compats[i].event_first = NULL;
|
||||
|
||||
Controls[i].Plugin = PLUGIN_NONE;
|
||||
|
||||
/* init vru_controller */
|
||||
init_game_controller(&g_dev.controllers[i],
|
||||
cont_flavor,
|
||||
&cin_compats[i], &g_icontroller_input_backend_plugin_compat,
|
||||
NULL, NULL);
|
||||
}
|
||||
/* otherwise let the core do the processing */
|
||||
else {
|
||||
/* select appropriate controller
|
||||
|
@ -1761,7 +1783,6 @@ m64p_error main_run(void)
|
|||
ijoybus_devices[i] = &g_ijoybus_device_cart;
|
||||
}
|
||||
|
||||
|
||||
init_device(&g_dev,
|
||||
g_mem_base,
|
||||
emumode,
|
||||
|
@ -1770,7 +1791,7 @@ m64p_error main_run(void)
|
|||
no_compiled_jump,
|
||||
randomize_interrupt,
|
||||
g_start_address,
|
||||
&g_dev.ai, &g_iaudio_out_backend_plugin_compat,
|
||||
&g_dev.ai, &g_iaudio_out_backend_plugin_compat, ((float)ROM_SETTINGS.aidmamodifier / 100.0),
|
||||
si_dma_duration,
|
||||
rdram_size,
|
||||
joybus_devices, ijoybus_devices,
|
||||
|
@ -1845,7 +1866,7 @@ m64p_error main_run(void)
|
|||
#endif
|
||||
/* release gb_carts */
|
||||
for(i = 0; i < GAME_CONTROLLERS_COUNT; ++i) {
|
||||
if (!Controls[i].RawData && g_dev.gb_carts[i].read_gb_cart != NULL) {
|
||||
if (!Controls[i].RawData && (Controls[i].Type == CONT_TYPE_STANDARD) && g_dev.gb_carts[i].read_gb_cart != NULL) {
|
||||
release_gb_rom(&l_gb_carts_data[i]);
|
||||
release_gb_ram(&l_gb_carts_data[i]);
|
||||
}
|
||||
|
@ -1883,7 +1904,7 @@ on_audio_open_failure:
|
|||
on_gfx_open_failure:
|
||||
/* release gb_carts */
|
||||
for(i = 0; i < GAME_CONTROLLERS_COUNT; ++i) {
|
||||
if (!Controls[i].RawData && g_dev.gb_carts[i].read_gb_cart != NULL) {
|
||||
if (!Controls[i].RawData && (Controls[i].Type == CONT_TYPE_STANDARD) && g_dev.gb_carts[i].read_gb_cart != NULL) {
|
||||
release_gb_rom(&l_gb_carts_data[i]);
|
||||
release_gb_ram(&l_gb_carts_data[i]);
|
||||
}
|
||||
|
|
|
@ -52,6 +52,8 @@ enum { DEFAULT_COUNT_PER_OP = 2 };
|
|||
enum { DEFAULT_DISABLE_EXTRA_MEM = 0 };
|
||||
/* Default SI DMA duration */
|
||||
enum { DEFAULT_SI_DMA_DURATION = 0x900 };
|
||||
/* Default AI DMA modifier */
|
||||
enum { DEFAULT_AI_DMA_MODIFIER = 100 };
|
||||
|
||||
static romdatabase_entry* ini_search_by_md5(md5_byte_t* md5);
|
||||
|
||||
|
@ -188,6 +190,7 @@ m64p_error open_rom(const unsigned char* romimage, unsigned int size)
|
|||
ROM_SETTINGS.countperop = entry->countperop;
|
||||
ROM_SETTINGS.disableextramem = entry->disableextramem;
|
||||
ROM_SETTINGS.sidmaduration = entry->sidmaduration;
|
||||
ROM_SETTINGS.aidmamodifier = entry->aidmamodifier;
|
||||
ROM_PARAMS.cheats = entry->cheats;
|
||||
}
|
||||
else
|
||||
|
@ -205,6 +208,7 @@ m64p_error open_rom(const unsigned char* romimage, unsigned int size)
|
|||
ROM_SETTINGS.countperop = DEFAULT_COUNT_PER_OP;
|
||||
ROM_SETTINGS.disableextramem = DEFAULT_DISABLE_EXTRA_MEM;
|
||||
ROM_SETTINGS.sidmaduration = DEFAULT_SI_DMA_DURATION;
|
||||
ROM_SETTINGS.aidmamodifier = DEFAULT_AI_DMA_MODIFIER;
|
||||
ROM_PARAMS.cheats = NULL;
|
||||
}
|
||||
|
||||
|
@ -373,6 +377,12 @@ static size_t romdatabase_resolve_round(void)
|
|||
entry->entry.set_flags |= ROMDATABASE_ENTRY_SIDMADURATION;
|
||||
}
|
||||
|
||||
if (!isset_bitmask(entry->entry.set_flags, ROMDATABASE_ENTRY_AIDMAMODIFIER) &&
|
||||
isset_bitmask(ref->set_flags, ROMDATABASE_ENTRY_AIDMAMODIFIER)) {
|
||||
entry->entry.aidmamodifier = ref->aidmamodifier;
|
||||
entry->entry.set_flags |= ROMDATABASE_ENTRY_AIDMAMODIFIER;
|
||||
}
|
||||
|
||||
free(entry->entry.refmd5);
|
||||
entry->entry.refmd5 = NULL;
|
||||
}
|
||||
|
@ -469,6 +479,7 @@ void romdatabase_open(void)
|
|||
search->entry.mempak = 1;
|
||||
search->entry.biopak = 0;
|
||||
search->entry.sidmaduration = DEFAULT_SI_DMA_DURATION;
|
||||
search->entry.aidmamodifier = DEFAULT_AI_DMA_MODIFIER;
|
||||
search->entry.set_flags = ROMDATABASE_ENTRY_NONE;
|
||||
|
||||
search->next_entry = NULL;
|
||||
|
@ -665,6 +676,15 @@ void romdatabase_open(void)
|
|||
DebugMessage(M64MSG_WARNING, "ROM Database: Invalid SiDmaDuration on line %i", lineno);
|
||||
}
|
||||
}
|
||||
else if(!strcmp(l.name, "AiDmaModifier"))
|
||||
{
|
||||
if (string_to_int(l.value, &value) && value >= 0 && value <= 200) {
|
||||
search->entry.aidmamodifier = value;
|
||||
search->entry.set_flags |= ROMDATABASE_ENTRY_AIDMAMODIFIER;
|
||||
} else {
|
||||
DebugMessage(M64MSG_WARNING, "ROM Database: Invalid AiDmaModifier on line %i", lineno);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugMessage(M64MSG_WARNING, "ROM Database: Unknown property on line %i", lineno);
|
||||
|
|
|
@ -113,6 +113,7 @@ typedef struct
|
|||
unsigned char mempak; /* 0 - No, 1 - Yes boolean for mempak support. */
|
||||
unsigned char biopak; /* 0 - No, 1 - Yes boolean for biopak support. */
|
||||
unsigned int sidmaduration;
|
||||
unsigned int aidmamodifier;
|
||||
uint32_t set_flags;
|
||||
} romdatabase_entry;
|
||||
|
||||
|
@ -130,6 +131,7 @@ typedef struct
|
|||
#define ROMDATABASE_ENTRY_MEMPAK BIT(10)
|
||||
#define ROMDATABASE_ENTRY_BIOPAK BIT(11)
|
||||
#define ROMDATABASE_ENTRY_SIDMADURATION BIT(12)
|
||||
#define ROMDATABASE_ENTRY_AIDMAMODIFIER BIT(13)
|
||||
|
||||
typedef struct _romdatabase_search
|
||||
{
|
||||
|
|
|
@ -565,7 +565,7 @@ static int savestates_load_m64p(struct device* dev, char *filepath)
|
|||
COPYARRAY(cam_regs, curr, uint8_t, POCKET_CAM_REGS_COUNT);
|
||||
}
|
||||
|
||||
if (ROM_SETTINGS.transferpak && !Controls[i].RawData) {
|
||||
if (ROM_SETTINGS.transferpak && !Controls[i].RawData && (Controls[i].Type == CONT_TYPE_STANDARD)) {
|
||||
|
||||
/* init transferpak state if enabled and not controlled by input plugin */
|
||||
dev->transferpaks[i].enabled = enabled;
|
||||
|
@ -675,7 +675,7 @@ static int savestates_load_m64p(struct device* dev, char *filepath)
|
|||
uint8_t rpk_state = GETDATA(curr, uint8_t);
|
||||
|
||||
/* init rumble pak state if enabled and not controlled by the input plugin */
|
||||
if (ROM_SETTINGS.rumble && !Controls[i].RawData) {
|
||||
if (ROM_SETTINGS.rumble && !Controls[i].RawData && (Controls[i].Type == CONT_TYPE_STANDARD)) {
|
||||
set_rumble_reg(&dev->rumblepaks[i], rpk_state);
|
||||
}
|
||||
}
|
||||
|
@ -710,7 +710,7 @@ static int savestates_load_m64p(struct device* dev, char *filepath)
|
|||
COPYARRAY(cam_regs, curr, uint8_t, POCKET_CAM_REGS_COUNT);
|
||||
}
|
||||
|
||||
if (ROM_SETTINGS.transferpak && !Controls[i].RawData) {
|
||||
if (ROM_SETTINGS.transferpak && !Controls[i].RawData && (Controls[i].Type == CONT_TYPE_STANDARD)) {
|
||||
|
||||
/* init transferpak state if enabled and not controlled by input plugin */
|
||||
dev->transferpaks[i].enabled = enabled;
|
||||
|
@ -892,10 +892,10 @@ static int savestates_load_m64p(struct device* dev, char *filepath)
|
|||
|
||||
dev->controllers[i].flavor->reset(&dev->controllers[i]);
|
||||
|
||||
if (ROM_SETTINGS.rumble) {
|
||||
if (ROM_SETTINGS.rumble && (Controls[i].Type == CONT_TYPE_STANDARD)) {
|
||||
poweron_rumblepak(&dev->rumblepaks[i]);
|
||||
}
|
||||
if (ROM_SETTINGS.transferpak) {
|
||||
if (ROM_SETTINGS.transferpak && (Controls[i].Type == CONT_TYPE_STANDARD)) {
|
||||
poweron_transferpak(&dev->transferpaks[i]);
|
||||
}
|
||||
}
|
||||
|
@ -1263,10 +1263,10 @@ static int savestates_load_pj64(struct device* dev,
|
|||
|
||||
dev->controllers[i].flavor->reset(&dev->controllers[i]);
|
||||
|
||||
if (ROM_SETTINGS.rumble) {
|
||||
if (ROM_SETTINGS.rumble && (Controls[i].Type == CONT_TYPE_STANDARD)) {
|
||||
poweron_rumblepak(&dev->rumblepaks[i]);
|
||||
}
|
||||
if (ROM_SETTINGS.transferpak) {
|
||||
if (ROM_SETTINGS.transferpak && (Controls[i].Type == CONT_TYPE_STANDARD)) {
|
||||
poweron_transferpak(&dev->transferpaks[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -394,6 +394,15 @@ static m64p_error plugin_connect_input(m64p_dynlib_handle plugin_handle)
|
|||
return M64ERR_INPUT_INVALID;
|
||||
}
|
||||
|
||||
if (!GET_FUNC(ptr_SendVRUWord, input.sendVRUWord, "SendVRUWord") ||
|
||||
!GET_FUNC(ptr_SetMicState, input.setMicState, "SetMicState") ||
|
||||
!GET_FUNC(ptr_ReadVRUResults, input.readVRUResults, "ReadVRUResults") ||
|
||||
!GET_FUNC(ptr_ClearVRUWords, input.clearVRUWords, "ClearVRUWords") ||
|
||||
!GET_FUNC(ptr_SetVRUWordMask, input.setVRUWordMask, "SetVRUWordMask"))
|
||||
{
|
||||
DebugMessage(M64MSG_WARNING, "Input plugin does not contain VRU support.");
|
||||
}
|
||||
|
||||
/* check the version info */
|
||||
(*input.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL);
|
||||
if (PluginType != M64PLUGIN_INPUT || (APIVersion & 0xffff0000) != (INPUT_API_VERSION & 0xffff0000) || APIVersion < 0x020100)
|
||||
|
@ -427,6 +436,7 @@ static m64p_error plugin_start_input(void)
|
|||
Controls[i].Present = 0;
|
||||
Controls[i].RawData = 0;
|
||||
Controls[i].Plugin = PLUGIN_NONE;
|
||||
Controls[i].Type = CONT_TYPE_STANDARD;
|
||||
}
|
||||
|
||||
/* call the input plugin */
|
||||
|
|
|
@ -38,7 +38,7 @@ extern CONTROL Controls[NUM_CONTROLLER];
|
|||
#define RSP_API_VERSION 0x20000
|
||||
#define GFX_API_VERSION 0x20200
|
||||
#define AUDIO_API_VERSION 0x20000
|
||||
#define INPUT_API_VERSION 0x20100
|
||||
#define INPUT_API_VERSION 0x20101
|
||||
|
||||
/* video plugin function pointers */
|
||||
typedef struct _gfx_plugin_functions
|
||||
|
@ -101,6 +101,11 @@ typedef struct _input_plugin_functions
|
|||
ptr_SDL_KeyDown keyDown;
|
||||
ptr_SDL_KeyUp keyUp;
|
||||
ptr_RenderCallback renderCallback;
|
||||
ptr_SendVRUWord sendVRUWord;
|
||||
ptr_SetMicState setMicState;
|
||||
ptr_ReadVRUResults readVRUResults;
|
||||
ptr_ClearVRUWords clearVRUWords;
|
||||
ptr_SetVRUWordMask setVRUWordMask;
|
||||
} input_plugin_functions;
|
||||
|
||||
extern input_plugin_functions input;
|
||||
|
|
Loading…
Reference in a new issue