Merge branch 'master' into DeviceEmulation-SteelBattalionController

This commit is contained in:
specialfred453@gmail.com 2025-01-26 00:13:57 -05:00
commit 53ed54880a
16 changed files with 813 additions and 9 deletions

View File

@ -32,5 +32,7 @@
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone used for peripheral emulation, e.g. Xbox Live Communicator.</string>
</dict>
</plist>

View File

@ -213,12 +213,14 @@ case "$platform" in # Adjust compilation options based on platform
python3 ./scripts/download-macos-libs.py ${target_arch}
lib_prefix=${PWD}/macos-libs/${target_arch}/opt/local
export CFLAGS="-arch ${target_arch} \
export CFLAGS="${CFLAGS} \
-arch ${target_arch} \
-target ${target_arch}-apple-macos${macos_min_ver} \
-isysroot ${sdk} \
-I${lib_prefix}/include \
-mmacosx-version-min=$macos_min_ver"
export LDFLAGS="-arch ${target_arch} \
export LDFLAGS="${LDFLAGS} \
-arch ${target_arch} \
-isysroot ${sdk}"
if [ "$target_arch" == "x86_64" ]; then
sys_cflags='-march=ivybridge'

BIN
data/controller_mask_s.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -0,0 +1,86 @@
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g class="layer">
<title>Controller S</title>
<!-- Left Thumbstick Head -->
<ellipse cx="33.5" cy="397.5" fill="#000000" id="svg_69" rx="26" ry="26" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2"/>
<ellipse cx="33.5" cy="397.5" fill="#000000" fill-opacity="null" id="svg_70" rx="18.5" ry="18.5" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2"/>
<ellipse cx="33.5" cy="383.5" fill="#ff0000" id="svg_71" rx="1.43" ry="1.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0"/>
<ellipse cx="33.5" cy="411.5" fill="#ff0000" id="svg_72" rx="1.5" ry="1.5" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0"/>
<ellipse cx="19" cy="397.5" fill="#ff0000" id="svg_73" rx="1.43" ry="1.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="48" cy="397.5" fill="#ff0000" id="svg_74" rx="1.43" ry="1.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0"/>
<!-- Right Thumbstick Head -->
<ellipse cx="33.5" cy="464.5" fill="#000000" id="svg_80" rx="26" ry="26" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="33.5" cy="464.5" fill="#000000" fill-opacity="null" id="svg_79" rx="18.5" ry="18.5" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="33.5" cy="450.67" fill="#ff0000" id="svg_78" rx="1.43" ry="1.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="33.5" cy="478.67" fill="#ff0000" id="svg_77" rx="1.5" ry="1.5" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="19" cy="464.5" fill="#ff0000" id="svg_76" rx="1.43" ry="1.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="48" cy="464.5" fill="#ff0000" id="svg_75" rx="1.43" ry="1.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<!-- Controller Body -->
<path d="m63.5,37.5c0,0 30,-15 41,-18.5c11,-3.5 47,-14 47,-14c0,0 19,13 78,14c59,1 83.5,-15 83.5,-15c0,0 42,12.5 49,15c7,2.5 39,20 39,20c0,0 61.2,116.9 63.1,185.8c1.9,68.9 -6.4,118.3 -27.2,126.5c-20.8,8.2 -46.7,-4.7 -67.3,-30.8c-20.6,-26.1 -22.6,-52.5 -22.6,-52.5c0,0 -33.9,-17.3 -115.3,-17.4c-81.4,-0.1 -112.7,16.9 -112.7,16.9c0,0 -5.7,34.4 -23.6,54.8c-17.9,20.4 -44.4,36.7 -63.9,28.7c-19.5,-8 -32.2,-51.7 -29.7,-123.4c2.5,-71.7 61.7,-190.1 61.7,-190.1z" fill="#000000" id="svg_5" stroke="#ff0000" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<path d="m53.5,48c0,0 21.5,-10.3 40,-16.6c18.5,-6.3 40,-9.9 40,-9.9c0,0 6.3,12.2 31.8,28.2c25.5,16 37.2,14.8 37.2,14.8c0,0 -11.7,9.4 -17.3,28.9c-5.6,19.5 3,35.8 11.6,45.7c8.6,9.9 23.6,16.2 37.2,15.9c13.6,-0.3 27.9,-6.4 37.5,-17c9.6,-10.6 15.9,-29.6 9.4,-47.6c-6.5,-18 -14.9,-25.9 -14.9,-25.4c0,0.5 12,-0.6 37,-16.6c25,-16 30.5,-26.9 30.5,-26.9c0,0 20,4.9 38.7,10.6c18.7,5.7 40.8,17.4 40.8,17.4c0,0 32.4,44.6 22.4,119.6c-10,75 -88.4,98.9 -88.4,98.9c0,0 -39.3,-52.2 -115.3,-52.2c-76,0 -112.2,51.2 -112.2,51.2c0,0 -78.2,-17.2 -88.2,-94.7c-10,-77.5 22.2,-124.3 22.2,-124.3z" fill="#000000" fill-opacity="null" id="svg_2" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<!-- Left Thumbstick -->
<ellipse cx="97.6" cy="109.6" fill="#000000" fill-opacity="null" id="svg_53" rx="46.8" ry="44.8" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="101.6" cy="110.4" fill="#000000" fill-opacity="null" id="svg_54" rx="33" ry="33" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<!-- Right Thumbstick -->
<path d="m248.8,196.8c0.81,-32.4 27.15,-50 44.57,-51.2c17.42,-1.2 39.31,9.2 45.38,25.2c6.08,16 4.86,42 -10.54,54c-15.4,12 -27.55,20 -53.08,12.4c-25.53,-7.6 -26.34,-40.4 -26.34,-40.4l0.01,0z" fill="#000000" fill-opacity="null" id="svg_55" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="295" cy="188.8" fill="#000000" fill-opacity="null" id="svg_56" rx="33" ry="33" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<!-- Circle Behind Face Buttons -->
<ellipse cx="362.4" cy="111" fill="#000000" id="svg_31" rx="40" ry="40.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<!-- X Button -->
<ellipse cx="328" cy="111.6" fill="#0000ff" id="svg_32" rx="17" ry="17" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="2"/>
<text fill="#ff0000" font-family="Helvetica" font-size="24" font-weight="bold" id="svg_60" letter-spacing="0" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" text-anchor="middle" x="328" xml:space="preserve" y="120.4">X</text>
<!-- B Button -->
<ellipse cx="398" cy="110.8" fill="#0000ff" id="svg_33" rx="17" ry="17" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="2"/>
<text fill="#ff0000" font-family="Helvetica" font-size="24" font-weight="bold" id="svg_63" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" text-anchor="middle" transform="matrix(1, 0, 0, 1, 0, 0)" x="398.8" xml:space="preserve" y="119.6">B</text>
<!-- A Button -->
<ellipse cx="364" cy="146.4" fill="#0000ff" id="svg_35" rx="17" ry="17" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="2"/>
<text fill="#ff0000" font-family="Helvetica" font-size="24" font-weight="bold" id="svg_64" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" text-anchor="middle" transform="matrix(1, 0, 0, 1, 0, 0)" x="363.6" xml:space="preserve" y="154.8">A</text>
<!-- Y Button -->
<ellipse cx="364" cy="76.4" fill="#0000ff" id="svg_34" rx="17" ry="17" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="2"/>
<text fill="#ff0000" font-family="Helvetica" font-size="24" font-weight="bold" id="svg_65" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="0" text-anchor="middle" transform="matrix(1, 0, 0, 1, 0, 0)" x="363.6" xml:space="preserve" y="85.6">Y</text>
<!-- White Button -->
<ellipse cx="368.6" cy="204.6" fill="#000000" id="svg_39" rx="18.2" ry="18.2" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2"/>
<ellipse cx="368.6" cy="204.6" fill="#0000ff" id="svg_36" rx="14.4" ry="14.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="2"/>
<!-- Black Button -->
<ellipse cx="404" cy="176" fill="#000000" id="svg_38" rx="18" ry="18" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="404" cy="176" fill="#0000ff" id="svg_37" rx="14.4" ry="14.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="2"/>
<!-- Back Button -->
<ellipse cx="55.6" cy="160" fill="#000000" fill-opacity="null" id="svg_40" rx="13.6" ry="13.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="55.6" cy="160" fill="#0000ff" fill-opacity="null" id="svg_43" rx="10" ry="10" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2"/>
<path d="m59.83,155l-0.17,9.33l-10.33,-4.83l10.5,-4.5z" fill="none" fill-opacity="null" id="svg_81" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2"/>
<!-- Start Button -->
<ellipse cx="82" cy="189" fill="#000000" fill-opacity="null" id="svg_41" rx="15.6" ry="16.2" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="82" cy="189" fill="#0000ff" id="svg_42" rx="12" ry="12.4" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<path d="m76.17,183.83l0,10.33l11.5,-4.83l-11.5,-5.5z" fill="none" fill-opacity="null" id="svg_82" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2"/>
<!-- D-Pad -->
<ellipse cx="164" cy="189" fill="#000000" fill-opacity="null" id="svg_52" rx="48.2" ry="49.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="164" cy="188" fill="#000000" id="svg_50" rx="42.6" ry="42.6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<ellipse cx="164" cy="188" fill="#000000" fill-opacity="null" id="svg_51" rx="37.2" ry="37" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)"/>
<line fill="none" fill-opacity="null" id="svg_1" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" x1="151" x2="151" y1="153" y2="176"/>
<line fill="none" fill-opacity="null" id="svg_3" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" x1="176" x2="176" y1="153" y2="177"/>
<line fill="none" fill-opacity="null" id="svg_4" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)" x1="151" x2="151" y1="198.29" y2="223"/>
<line fill="none" fill-opacity="null" id="svg_6" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" x1="176" x2="176" y1="199" y2="223"/>
<line fill="none" fill-opacity="null" id="svg_7" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" x1="176" x2="200" y1="176" y2="176"/>
<line fill="none" fill-opacity="null" id="svg_8" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)" x1="175" x2="200" y1="199" y2="199"/>
<line fill="none" fill-opacity="null" id="svg_9" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" transform="matrix(1, 0, 0, 1, 0, 0)" x1="128" x2="152" y1="199" y2="199"/>
<line fill="none" fill-opacity="null" id="svg_10" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2" x1="128" x2="152" y1="176" y2="176"/>
<!-- Jewel -->
<ellipse cx="233.19" cy="106.15" fill="#0000ff" id="svg_20" rx="40" ry="40" stroke="#ff0000" stroke-dasharray="null" stroke-linecap="null" stroke-linejoin="null" stroke-opacity="null" stroke-width="2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,6 +1,7 @@
pfiles = [
'sb_controller_mask.png',
'controller_mask.png',
'controller_mask_s.png',
'xmu_mask.png',
'logo_sdf.png',
'xemu_64x64.png',

View File

@ -149,8 +149,11 @@ typedef struct AC97DeviceState {
OBJECT_CHECK(AC97DeviceState, (obj), "AC97")
static void po_callback(void *opaque, int free);
#ifndef XBOX
static void pi_callback(void *opaque, int avail);
static void mc_callback(void *opaque, int avail);
#endif
static void fetch_bd(AC97LinkState *s, AC97BusMasterRegs *r)
{
@ -218,7 +221,9 @@ static void voice_set_active(AC97LinkState *s, int bm_index, int on)
{
switch (bm_index) {
case PI_INDEX:
#ifndef XBOX
AUD_set_active_in(s->voice_pi, on);
#endif
break;
case PO_INDEX:
@ -226,7 +231,9 @@ static void voice_set_active(AC97LinkState *s, int bm_index, int on)
break;
case MC_INDEX:
#ifndef XBOX
AUD_set_active_in(s->voice_mc, on);
#endif
break;
case SO_INDEX:
@ -294,6 +301,7 @@ static void open_voice(AC97LinkState *s, int index, int freq)
s->invalid_freq[index] = 0;
switch (index) {
case PI_INDEX:
#ifndef XBOX
s->voice_pi = AUD_open_in(
&s->card,
s->voice_pi,
@ -302,6 +310,7 @@ static void open_voice(AC97LinkState *s, int index, int freq)
pi_callback,
&as
);
#endif
break;
case PO_INDEX:
@ -316,6 +325,7 @@ static void open_voice(AC97LinkState *s, int index, int freq)
break;
case MC_INDEX:
#ifndef XBOX
s->voice_mc = AUD_open_in(
&s->card,
s->voice_mc,
@ -324,6 +334,7 @@ static void open_voice(AC97LinkState *s, int index, int freq)
mc_callback,
&as
);
#endif
break;
case SO_INDEX:
@ -333,7 +344,9 @@ static void open_voice(AC97LinkState *s, int index, int freq)
s->invalid_freq[index] = freq;
switch (index) {
case PI_INDEX:
#ifndef XBOX
AUD_close_in(&s->card, s->voice_pi);
#endif
s->voice_pi = NULL;
break;
@ -343,7 +356,9 @@ static void open_voice(AC97LinkState *s, int index, int freq)
break;
case MC_INDEX:
#ifndef XBOX
AUD_close_in(&s->card, s->voice_mc);
#endif
s->voice_mc = NULL;
break;
@ -357,17 +372,21 @@ static void reset_voices(AC97LinkState *s, uint8_t active[LAST_INDEX])
{
uint16_t freq;
#ifndef XBOX
freq = mixer_load(s, AC97_PCM_LR_ADC_Rate);
open_voice(s, PI_INDEX, freq);
AUD_set_active_in(s->voice_pi, active[PI_INDEX]);
#endif
freq = mixer_load(s, AC97_PCM_Front_DAC_Rate);
open_voice(s, PO_INDEX, freq);
AUD_set_active_out(s->voice_po, active[PO_INDEX]);
#ifndef XBOX
freq = mixer_load(s, AC97_MIC_ADC_Rate);
open_voice(s, MC_INDEX, freq);
AUD_set_active_in(s->voice_mc, active[MC_INDEX]);
#endif
}
static void get_volume(uint16_t vol, uint16_t mask, int inverse,
@ -408,7 +427,9 @@ static void update_volume_in(AC97LinkState *s)
get_volume(mixer_load(s, AC97_Record_Gain_Mute), 0x0f, 0,
&mute, &lvol, &rvol);
#ifndef XBOX
AUD_set_volume_in(s->voice_pi, mute, lvol, rvol);
#endif
}
static void set_volume(AC97LinkState *s, int index, uint32_t val)
@ -972,6 +993,10 @@ static int read_audio(AC97LinkState *s, AC97BusMasterRegs *r,
int to_copy = 0;
SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi;
#ifdef XBOX
return 0;
#endif
temp = MIN(temp, max);
if (!temp) {
@ -1081,6 +1106,7 @@ static void transfer_audio(AC97LinkState *s, int index, int elapsed)
}
}
#ifndef XBOX
static void pi_callback(void *opaque, int avail)
{
transfer_audio(opaque, PI_INDEX, avail);
@ -1090,6 +1116,7 @@ static void mc_callback(void *opaque, int avail)
{
transfer_audio(opaque, MC_INDEX, avail);
}
#endif
static void po_callback(void *opaque, int free)
{

View File

@ -15,6 +15,7 @@ specific_ss.add(files(
'xbox.c',
'xbox_pci.c',
'xid.c',
'xblc.c',
'xid-gamepad.c',
'xid-steel-battalion.c',
))

402
hw/xbox/xblc.c Normal file
View File

@ -0,0 +1,402 @@
/*
* QEMU USB Xbox Live Communicator (XBLC) Device
*
* Copyright (c) 2022 Ryan Wendland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "sysemu/sysemu.h"
#include "hw/hw.h"
#include "ui/console.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
#include "ui/xemu-input.h"
#include "audio/audio.h"
#include "qemu/fifo8.h"
//#define DEBUG_XBLC
#ifdef DEBUG_XBLC
#define DPRINTF printf
#else
#define DPRINTF(...)
#endif
#define TYPE_USB_XBLC "usb-xblc"
#define USB_XBLC(obj) OBJECT_CHECK(USBXBLCState, (obj), TYPE_USB_XBLC)
#define XBLC_STR "Microsoft Xbox Live Communicator"
#define XBLC_INTERFACE_CLASS 0x78
#define XBLC_INTERFACE_SUBCLASS 0x00
#define XBLC_EP_OUT 0x04
#define XBLC_EP_IN 0x05
#define XBLC_SET_SAMPLE_RATE 0x00
#define XBLC_SET_AGC 0x01
#define XBLC_MAX_PACKET 48
#define XBLC_FIFO_SIZE (XBLC_MAX_PACKET * 100) //~100 ms worth of audio at 16bit 24kHz
static const uint8_t silence[256] = {0};
static const uint16_t xblc_sample_rates[5] = {
8000, 11025, 16000, 22050, 24000
};
typedef struct USBXBLCState {
USBDevice dev;
uint8_t device_index;
uint8_t auto_gain_control;
uint16_t sample_rate;
QEMUSoundCard card;
struct audsettings as;
struct {
SWVoiceOut* voice;
uint8_t packet[XBLC_MAX_PACKET];
Fifo8 fifo;
} out;
struct {
SWVoiceIn *voice;
uint8_t packet[XBLC_MAX_PACKET];
Fifo8 fifo;
} in;
} USBXBLCState;
enum {
STR_MANUFACTURER = 1,
STR_PRODUCT,
STR_SERIALNUMBER,
};
static const USBDescStrings desc_strings = {
[STR_MANUFACTURER] = "xemu",
[STR_PRODUCT] = XBLC_STR,
[STR_SERIALNUMBER] = "1",
};
static const USBDescIface desc_iface[]= {
{
.bInterfaceNumber = 0,
.bNumEndpoints = 1,
.bInterfaceClass = XBLC_INTERFACE_CLASS,
.bInterfaceSubClass = XBLC_INTERFACE_SUBCLASS,
.bInterfaceProtocol = 0x00,
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_OUT | XBLC_EP_OUT,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = XBLC_MAX_PACKET,
.is_audio = 1,
.bInterval = 1,
.bRefresh = 0,
.bSynchAddress = 0,
}
},
},
{
.bInterfaceNumber = 1,
.bNumEndpoints = 1,
.bInterfaceClass = XBLC_INTERFACE_CLASS,
.bInterfaceSubClass = XBLC_INTERFACE_SUBCLASS,
.bInterfaceProtocol = 0x00,
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_IN | XBLC_EP_IN,
.bmAttributes = USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = XBLC_MAX_PACKET,
.is_audio = 1,
.bInterval = 1,
.bRefresh = 0,
.bSynchAddress = 0,
}
},
}
};
static const USBDescDevice desc_device = {
.bcdUSB = 0x0110,
.bMaxPacketSize0 = 8,
.bNumConfigurations = 1,
.confs = (USBDescConfig[]) {
{
.bNumInterfaces = 2,
.bConfigurationValue = 1,
.bmAttributes = USB_CFG_ATT_ONE,
.bMaxPower = 100,
.nif = ARRAY_SIZE(desc_iface),
.ifs = desc_iface,
},
},
};
static const USBDesc desc_xblc = {
.id = {
.idVendor = 0x045e,
.idProduct = 0x0283,
.bcdDevice = 0x0110,
.iManufacturer = STR_MANUFACTURER,
.iProduct = STR_PRODUCT,
.iSerialNumber = STR_SERIALNUMBER,
},
.full = &desc_device,
.str = desc_strings,
};
static void usb_xblc_handle_reset(USBDevice *dev)
{
USBXBLCState *s = (USBXBLCState *)dev;
DPRINTF("[XBLC] Reset\n");
fifo8_reset(&s->in.fifo);
fifo8_reset(&s->out.fifo);
}
static void output_callback(void *opaque, int avail)
{
USBXBLCState *s = (USBXBLCState *)opaque;
const uint8_t *data;
uint32_t processed, max_len;
// Not enough data to send, wait a bit longer, fill with silence for now
if (fifo8_num_used(&s->out.fifo) < XBLC_MAX_PACKET) {
do {
processed = AUD_write(s->out.voice, (void *)silence, ARRAY_SIZE(silence));
avail -= processed;
} while (avail > 0 && processed >= XBLC_MAX_PACKET);
return;
}
// Write speaker data into audio backend
while (avail > 0 && !fifo8_is_empty(&s->out.fifo)) {
max_len = MIN(fifo8_num_used(&s->out.fifo), avail);
data = fifo8_pop_bufptr(&s->out.fifo, max_len, &max_len);
processed = AUD_write(s->out.voice, (void *)data, max_len);
avail -= processed;
if (processed < max_len) return;
}
}
static void input_callback(void *opaque, int avail)
{
USBXBLCState *s = (USBXBLCState *)opaque;
uint32_t processed, max_len;
// Get microphone data from audio backend
while (avail > 0 && !fifo8_is_full(&s->in.fifo)) {
max_len = MIN(sizeof(s->in.packet), fifo8_num_free(&s->in.fifo));
processed = AUD_read(s->in.voice, s->in.packet, max_len);
avail -= processed;
fifo8_push_all(&s->in.fifo, s->in.packet, processed);
if (processed < max_len) return;
}
// Flush excess/old data - this can happen if the user program stops the iso transfers after it
// has setup the xblc.
while (avail > 0)
{
processed = AUD_read(s->in.voice, s->in.packet, XBLC_MAX_PACKET);
avail -= processed;
if (processed == 0) break;
}
}
static void xblc_audio_stream_init(USBDevice *dev, uint16_t sample_rate)
{
USBXBLCState *s = (USBXBLCState *)dev;
AUD_set_active_out(s->out.voice, FALSE);
AUD_set_active_in(s->in.voice, FALSE);
fifo8_reset(&s->in.fifo);
fifo8_reset(&s->out.fifo);
s->as.freq = sample_rate;
s->as.nchannels = 1;
s->as.fmt = AUDIO_FORMAT_S16;
s->as.endianness = 0;
s->out.voice = AUD_open_out(&s->card, s->out.voice, TYPE_USB_XBLC "-speaker",
s, output_callback, &s->as);
s->in.voice = AUD_open_in(&s->card, s->in.voice, TYPE_USB_XBLC "-microphone",
s, input_callback, &s->as);
AUD_set_active_out(s->out.voice, TRUE);
AUD_set_active_in(s->in.voice, TRUE);
DPRINTF("[XBLC] Init audio streams at %d Hz\n", sample_rate);
}
static void usb_xblc_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
USBXBLCState *s = (USBXBLCState *)dev;
if (usb_desc_handle_control(dev, p, request, value, index, length, data) >= 0) {
DPRINTF("[XBLC] USB Control request handled by usb_desc_handle_control\n");
return;
}
switch (request) {
case VendorInterfaceOutRequest | USB_REQ_SET_FEATURE:
if (index == XBLC_SET_SAMPLE_RATE)
{
uint8_t rate = value & 0xFF;
assert(rate < ARRAY_SIZE(xblc_sample_rates));
DPRINTF("[XBLC] Set Sample Rate to %04x\n", rate);
s->sample_rate = xblc_sample_rates[rate];
xblc_audio_stream_init(dev, s->sample_rate);
break;
}
else if (index == XBLC_SET_AGC)
{
DPRINTF("[XBLC] Set Auto Gain Control to %d\n", value);
s->auto_gain_control = (value) ? 1 : 0;
break;
}
// Fallthrough
default:
DPRINTF("[XBLC] USB stalled on request 0x%x value 0x%x\n", request, value);
p->status = USB_RET_STALL;
assert(false);
return;
}
}
static void usb_xblc_handle_data(USBDevice *dev, USBPacket *p)
{
USBXBLCState *s = (USBXBLCState *)dev;
uint32_t to_process, chunk_len;
switch (p->pid) {
case USB_TOKEN_IN:
// Microphone Data - Get data from fifo and copy into usb packet
assert(p->ep->nr == XBLC_EP_IN);
to_process = MIN(fifo8_num_used(&s->in.fifo), p->iov.size);
chunk_len = 0;
// fifo may not give us a contiguous packet, so may need multiple calls
while (to_process) {
const uint8_t *packet = fifo8_pop_bufptr(&s->in.fifo, to_process, &chunk_len);
usb_packet_copy(p, (void *)packet, chunk_len);
to_process -= chunk_len;
}
break;
case USB_TOKEN_OUT:
// Speaker data - get data from usb packet then push to fifo.
assert(p->ep->nr == XBLC_EP_OUT);
to_process = MIN(fifo8_num_free(&s->out.fifo), p->iov.size);
usb_packet_copy(p, s->out.packet, to_process);
fifo8_push_all(&s->out.fifo, s->out.packet, to_process);
break;
default:
//Iso cannot report STALL/HALT, but we shouldn't be here anyway.
assert(false);
break;
}
// Ensure we fill the entire packet regardless of if we have audio data so we don't
// cause an underrun error.
if (p->actual_length < p->iov.size)
usb_packet_copy(p, (void *)silence, p->iov.size - p->actual_length);
}
static void usb_xbox_communicator_unrealize(USBDevice *dev)
{
USBXBLCState *s = USB_XBLC(dev);
AUD_set_active_out(s->out.voice, false);
AUD_set_active_in(s->in.voice, false);
fifo8_destroy(&s->out.fifo);
fifo8_destroy(&s->in.fifo);
AUD_close_out(&s->card, s->out.voice);
AUD_close_in(&s->card, s->in.voice);
AUD_remove_card(&s->card);
}
static void usb_xblc_class_initfn(ObjectClass *klass, void *data)
{
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
uc->handle_reset = usb_xblc_handle_reset;
uc->handle_control = usb_xblc_handle_control;
uc->handle_data = usb_xblc_handle_data;
uc->handle_attach = usb_desc_attach;
}
static void usb_xbox_communicator_realize(USBDevice *dev, Error **errp)
{
USBXBLCState *s = USB_XBLC(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
AUD_register_card(TYPE_USB_XBLC, &s->card, errp);
fifo8_create(&s->in.fifo, XBLC_FIFO_SIZE);
fifo8_create(&s->out.fifo, XBLC_FIFO_SIZE);
}
static Property xblc_properties[] = {
DEFINE_PROP_UINT8("index", USBXBLCState, device_index, 0),
DEFINE_PROP_END_OF_LIST(),
};
static const VMStateDescription usb_xblc_vmstate = {
.name = TYPE_USB_XBLC,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_USB_DEVICE(dev, USBXBLCState),
// FIXME
VMSTATE_END_OF_LIST()
},
};
static void usb_xbox_communicator_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
uc->product_desc = XBLC_STR;
uc->usb_desc = &desc_xblc;
uc->realize = usb_xbox_communicator_realize;
uc->unrealize = usb_xbox_communicator_unrealize;
usb_xblc_class_initfn(klass, data);
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
dc->vmsd = &usb_xblc_vmstate;
device_class_set_props(dc, xblc_properties);
dc->desc = XBLC_STR;
}
static const TypeInfo info_xblc = {
.name = TYPE_USB_XBLC,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(USBXBLCState),
.class_init = usb_xbox_communicator_class_initfn,
};
static void usb_xblc_register_types(void)
{
type_register_static(&info_xblc);
}
type_init(usb_xblc_register_types)

View File

@ -35,6 +35,8 @@
#define USB_XID(obj) \
OBJECT_CHECK(USBXIDGamepadState, (obj), TYPE_USB_XID_GAMEPAD)
#define USB_XID_S(obj) \
OBJECT_CHECK(USBXIDGamepadState, (obj), TYPE_USB_XID_GAMEPAD_S)
static const USBDescIface desc_iface_xbox_gamepad = {
.bInterfaceNumber = 0,
@ -89,6 +91,19 @@ static const USBDesc desc_xbox_gamepad = {
.str = desc_strings,
};
static const USBDesc desc_xbox_gamepad_s = {
.id = {
.idVendor = USB_VENDOR_MICROSOFT,
.idProduct = 0x0289,
.bcdDevice = 0x0100,
.iManufacturer = STR_MANUFACTURER,
.iProduct = STR_PRODUCT,
.iSerialNumber = STR_SERIALNUMBER,
},
.full = &desc_device_xbox_gamepad,
.str = desc_strings,
};
static const XIDDesc desc_xid_xbox_gamepad = {
.bLength = 0x10,
.bDescriptorType = USB_DT_XID,
@ -100,6 +115,17 @@ static const XIDDesc desc_xid_xbox_gamepad = {
.wAlternateProductIds = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF },
};
static const XIDDesc desc_xid_xbox_gamepad_s = {
.bLength = 0x10,
.bDescriptorType = USB_DT_XID,
.bcdXid = 0x100,
.bType = XID_DEVICETYPE_GAMEPAD,
.bSubType = XID_DEVICESUBTYPE_GAMEPAD_S,
.bMaxInputReportSize = 20,
.bMaxOutputReportSize = 6,
.wAlternateProductIds = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF },
};
static void usb_xid_gamepad_handle_data(USBDevice *dev, USBPacket *p)
{
USBXIDGamepadState *s = DO_UPCAST(USBXIDGamepadState, dev, dev);
@ -166,6 +192,30 @@ static void usb_xbox_gamepad_realize(USBDevice *dev, Error **errp)
s->out_state_capabilities.report_id = 0;
}
static void usb_xbox_gamepad_s_realize(USBDevice *dev, Error **errp)
{
USBXIDGamepadState *s = USB_XID_S(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 2);
s->in_state.bLength = sizeof(s->in_state);
s->in_state.bReportId = 0;
s->out_state.length = sizeof(s->out_state);
s->out_state.report_id = 0;
s->xid_desc = &desc_xid_xbox_gamepad_s;
memset(&s->in_state_capabilities, 0xFF, sizeof(s->in_state_capabilities));
s->in_state_capabilities.bLength = sizeof(s->in_state_capabilities);
s->in_state_capabilities.bReportId = 0;
memset(&s->out_state_capabilities, 0xFF, sizeof(s->out_state_capabilities));
s->out_state_capabilities.length = sizeof(s->out_state_capabilities);
s->out_state_capabilities.report_id = 0;
}
static Property xid_properties[] = {
DEFINE_PROP_UINT8("index", USBXIDGamepadState, device_index, 0),
DEFINE_PROP_END_OF_LIST(),
@ -180,6 +230,14 @@ static const VMStateDescription vmstate_usb_xbox = {
VMSTATE_END_OF_LIST() },
};
static const VMStateDescription vmstate_usb_xbox_s = {
.name = TYPE_USB_XID_GAMEPAD_S,
.minimum_version_id = 1,
.fields = (VMStateField[]){ VMSTATE_USB_DEVICE(dev, USBXIDGamepadState),
// FIXME
VMSTATE_END_OF_LIST() },
};
static void usb_xbox_gamepad_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@ -196,6 +254,22 @@ static void usb_xbox_gamepad_class_initfn(ObjectClass *klass, void *data)
dc->desc = "Microsoft Xbox Controller";
}
static void usb_xbox_gamepad_s_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
uc->product_desc = "Microsoft Xbox Controller S";
uc->usb_desc = &desc_xbox_gamepad_s;
uc->realize = usb_xbox_gamepad_s_realize;
uc->unrealize = usb_xbox_gamepad_unrealize;
usb_xid_gamepad_class_initfn(klass, data);
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
dc->vmsd = &vmstate_usb_xbox_s;
device_class_set_props(dc, xid_properties);
dc->desc = "Microsoft Xbox Controller S";
}
static const TypeInfo usb_xbox_gamepad_info = {
.name = TYPE_USB_XID_GAMEPAD,
.parent = TYPE_USB_DEVICE,
@ -203,9 +277,17 @@ static const TypeInfo usb_xbox_gamepad_info = {
.class_init = usb_xbox_gamepad_class_initfn,
};
static const TypeInfo usb_xbox_gamepad_s_info = {
.name = TYPE_USB_XID_GAMEPAD_S,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(USBXIDGamepadState),
.class_init = usb_xbox_gamepad_s_class_initfn,
};
static void usb_xid_register_types(void)
{
type_register_static(&usb_xbox_gamepad_info);
type_register_static(&usb_xbox_gamepad_s_info);
}
type_init(usb_xid_register_types)

View File

@ -54,6 +54,7 @@
#define XID_DEVICESUBTYPE_GAMEPAD_S 0x02
#define TYPE_USB_XID_GAMEPAD "usb-xbox-gamepad"
#define TYPE_USB_XID_GAMEPAD_S "usb-xbox-gamepad-s"
#define TYPE_USB_XID_STEEL_BATTALION "usb-steel-battalion"
#define GAMEPAD_A 0

View File

@ -2406,8 +2406,7 @@ if have_system
# Default to native drivers first, OSS second, SDL third
audio_drivers_priority = \
[ 'pa', 'coreaudio', 'dsound', 'sndio', 'oss' ] + \
(host_os == 'linux' ? [] : [ 'sdl' ])
[ 'pa', 'coreaudio', 'dsound', 'sndio', 'oss', 'sdl' ]
audio_drivers_default = []
foreach k: audio_drivers_priority
if audio_drivers_available[k]

View File

@ -2996,9 +2996,6 @@ void qemu_init(int argc, char **argv)
fake_argv[fake_argc++] = strdup("-device");
fake_argv[fake_argc++] = strdup("usb-hub,port=1,ports=4");
fake_argv[fake_argc++] = strdup("-audio");
fake_argv[fake_argc++] = strdup("none");
for (int i = 1; i < argc; i++) {
if (argv[i] != NULL) {
fake_argv[fake_argc++] = argv[i];

View File

@ -152,6 +152,25 @@ static const char *get_bound_driver(int port)
return DRIVER_DUKE;
}
static const char *get_bound_driver(int port)
{
assert(port >= 0 && port <= 3);
const char *driver = *port_index_to_driver_settings_key_map[port];
// If the driver in the config is NULL, empty, or unrecognized
// then default to DRIVER_DUKE
if (driver == NULL)
return DRIVER_DUKE;
if (strlen(driver) == 0)
return DRIVER_DUKE;
if (strcmp(driver, DRIVER_DUKE) == 0)
return DRIVER_DUKE;
if (strcmp(driver, DRIVER_S) == 0)
return DRIVER_S;
return DRIVER_DUKE;
}
static const int port_map[4] = { 3, 4, 1, 2 };
void xemu_input_init(void)
@ -324,6 +343,11 @@ void xemu_input_init(void)
bound_drivers[2] = get_bound_driver(2);
bound_drivers[3] = get_bound_driver(3);
bound_drivers[0] = get_bound_driver(0);
bound_drivers[1] = get_bound_driver(1);
bound_drivers[2] = get_bound_driver(2);
bound_drivers[3] = get_bound_driver(3);
// Check to see if we should auto-bind the keyboard
int port = xemu_input_get_controller_default_bind_port(new_con, 0);
if (port >= 0) {

View File

@ -31,9 +31,11 @@
#include "qemu/queue.h"
#define DRIVER_DUKE "usb-xbox-gamepad"
#define DRIVER_S "usb-xbox-gamepad-s"
#define DRIVER_STEEL_BATTALION "usb-steel-battalion"
#define DRIVER_DUKE_DISPLAY_NAME "Xbox Controller"
#define DRIVER_S_DISPLAY_NAME "Xbox Controller S"
#define DRIVER_STEEL_BATTALION_DISPLAY_NAME "Steel Battalion Controller"
enum controller_state_buttons_mask {

View File

@ -20,6 +20,7 @@
#include "gl-helpers.hh"
#include "common.hh"
#include "data/controller_mask.png.h"
#include "data/controller_mask_s.png.h"
#include "data/sb_controller_mask.png.h"
#include "data/logo_sdf.png.h"
#include "data/xemu_64x64.png.h"
@ -36,7 +37,7 @@
extern int viewport_coords[4];
Fbo *controller_fbo, *xmu_fbo, *logo_fbo;
GLuint g_controller_duke_tex, g_sb_controller_tex, g_logo_tex, g_icon_tex, g_xmu_tex;
GLuint g_controller_duke_tex, g_controller_s_tex, g_sb_controller_duke_tex, g_logo_tex, g_icon_tex, g_xmu_tex;
enum class ShaderType {
Blit,
@ -469,6 +470,8 @@ void InitCustomRendering(void)
glActiveTexture(GL_TEXTURE0);
g_controller_duke_tex =
LoadTextureFromMemory(controller_mask_data, controller_mask_size);
g_controller_s_tex =
LoadTextureFromMemory(controller_mask_s_data, controller_mask_s_size);
g_sb_controller_tex =
LoadTextureFromMemory(sb_controller_mask_data, sb_controller_mask_size);
g_decal_shader = NewDecalShader(ShaderType::Mask);
@ -653,6 +656,173 @@ static void RenderDukeController(float frame_x, float frame_y, uint32_t primary_
glUseProgram(0);
}
static void RenderControllerS(float frame_x, float frame_y, uint32_t primary_color,
uint32_t secondary_color, ControllerState *state)
{
// Location within the controller texture of masked button locations,
// relative to the origin of the controller
const struct rect jewel = { 194, 213, 84, 84 };
const struct rect lstick_ctr = { 103, 254, 0, 0 };
const struct rect rstick_ctr = { 295, 176, 0, 0 };
const struct rect buttons[12] = {
{ 347, 200, 34, 34 }, // A
{ 381, 235, 34, 34 }, // B
{ 313, 235, 34, 34 }, // X
{ 347, 270, 34, 34 }, // Y
{ 123, 165, 31, 26 }, // D-Left
{ 150, 187, 26, 31 }, // D-Up
{ 173, 165, 31, 26 }, // D-Right
{ 150, 135, 26, 31 }, // D-Down
{ 45, 195, 20, 24 }, // Back
{ 70, 163, 26, 26 }, // Start
{ 352, 145, 30, 30 }, // White
{ 388, 172, 30, 30 }, // Black
};
uint8_t alpha = 0;
uint32_t now = SDL_GetTicks();
float t;
glUseProgram(g_decal_shader->prog);
glBindVertexArray(g_decal_shader->vao);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_controller_s_tex);
// Add a 5 pixel space around the controller so we can wiggle the controller
// around to visualize rumble in action
frame_x += 5;
frame_y += 5;
float original_frame_x = frame_x;
float original_frame_y = frame_y;
// Floating point versions that will get scaled
float rumble_l = 0;
float rumble_r = 0;
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ZERO);
uint32_t jewel_color = secondary_color;
// Check to see if the guide button is pressed
const uint32_t animate_guide_button_duration = 2000;
if (state->buttons & CONTROLLER_BUTTON_GUIDE) {
state->animate_guide_button_end =
now + animate_guide_button_duration;
}
if (now < state->animate_guide_button_end) {
t = 1.0f - (float)(state->animate_guide_button_end - now) /
(float)animate_guide_button_duration;
float sin_wav = (1 - sin(M_PI * t / 2.0f));
// Animate guide button by highlighting logo jewel and fading out over
// time
alpha = sin_wav * 255.0f;
jewel_color = primary_color + alpha;
// Add a little extra flare: wiggle the frame around while we rumble
frame_x += ((float)(rand() % 5) - 2.5) * (1 - t);
frame_y += ((float)(rand() % 5) - 2.5) * (1 - t);
rumble_l = rumble_r = sin_wav;
}
// Render controller texture
RenderDecal(g_decal_shader, frame_x + 0, frame_y + 0,
tex_items[obj_controller].w, tex_items[obj_controller].h,
tex_items[obj_controller].x, tex_items[obj_controller].y,
tex_items[obj_controller].w, tex_items[obj_controller].h,
primary_color, secondary_color, 0);
glBlendFunc(GL_ONE_MINUS_DST_ALPHA,
GL_ONE); // Blend with controller cutouts
RenderDecal(g_decal_shader, frame_x + jewel.x, frame_y + jewel.y, jewel.w,
jewel.h, 0, 0, 1, 1, 0, 0, jewel_color);
// The controller has alpha cutouts where the buttons are. Draw a surface
// behind the buttons if they are activated
for (int i = 0; i < 12; i++) {
if (state->buttons & (1 << i)) {
RenderDecal(g_decal_shader, frame_x + buttons[i].x,
frame_y + buttons[i].y, buttons[i].w, buttons[i].h, 0,
0, 1, 1, 0, 0, primary_color + 0xff);
}
}
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Blend with controller
// Render left thumbstick
float w = tex_items[obj_lstick].w;
float h = tex_items[obj_lstick].h;
float c_x = frame_x + lstick_ctr.x;
float c_y = frame_y + lstick_ctr.y;
float lstick_x = (float)state->axis[CONTROLLER_AXIS_LSTICK_X] / 32768.0;
float lstick_y = (float)state->axis[CONTROLLER_AXIS_LSTICK_Y] / 32768.0;
RenderDecal(
g_decal_shader, (int)(c_x - w / 2.0f + 10.0f * lstick_x),
(int)(c_y - h / 2.0f + 10.0f * lstick_y), w, h, tex_items[obj_lstick].x,
tex_items[obj_lstick].y, w, h,
(state->buttons & CONTROLLER_BUTTON_LSTICK) ? secondary_color :
primary_color,
(state->buttons & CONTROLLER_BUTTON_LSTICK) ? primary_color :
secondary_color,
0);
// Render right thumbstick
w = tex_items[obj_rstick].w;
h = tex_items[obj_rstick].h;
c_x = frame_x + rstick_ctr.x;
c_y = frame_y + rstick_ctr.y;
float rstick_x = (float)state->axis[CONTROLLER_AXIS_RSTICK_X] / 32768.0;
float rstick_y = (float)state->axis[CONTROLLER_AXIS_RSTICK_Y] / 32768.0;
RenderDecal(
g_decal_shader, (int)(c_x - w / 2.0f + 10.0f * rstick_x),
(int)(c_y - h / 2.0f + 10.0f * rstick_y), w, h, tex_items[obj_rstick].x,
tex_items[obj_rstick].y, w, h,
(state->buttons & CONTROLLER_BUTTON_RSTICK) ? secondary_color :
primary_color,
(state->buttons & CONTROLLER_BUTTON_RSTICK) ? primary_color :
secondary_color,
0);
glBlendFunc(GL_ONE,
GL_ZERO); // Don't blend, just overwrite values in buffer
// Render trigger bars
float ltrig = state->axis[CONTROLLER_AXIS_LTRIG] / 32767.0;
float rtrig = state->axis[CONTROLLER_AXIS_RTRIG] / 32767.0;
const uint32_t animate_trigger_duration = 1000;
if ((ltrig > 0) || (rtrig > 0)) {
state->animate_trigger_end = now + animate_trigger_duration;
rumble_l = fmax(rumble_l, ltrig);
rumble_r = fmax(rumble_r, rtrig);
}
// Animate trigger alpha down after a period of inactivity
alpha = 0x80;
if (state->animate_trigger_end > now) {
t = 1.0f - (float)(state->animate_trigger_end - now) /
(float)animate_trigger_duration;
float sin_wav = (1 - sin(M_PI * t / 2.0f));
alpha += fmin(sin_wav * 0x40, 0x80);
}
RenderMeter(g_decal_shader, original_frame_x + 10,
original_frame_y + tex_items[obj_controller].h + 20, 150, 5,
ltrig, primary_color + alpha, primary_color + 0xff);
RenderMeter(g_decal_shader,
original_frame_x + tex_items[obj_controller].w - 160,
original_frame_y + tex_items[obj_controller].h + 20, 150, 5,
rtrig, primary_color + alpha, primary_color + 0xff);
// Apply rumble updates
state->rumble_l = (int)(rumble_l * (float)0xffff);
state->rumble_r = (int)(rumble_r * (float)0xffff);
glBindVertexArray(0);
glUseProgram(0);
}
void RenderSteelBattalionController(float frame_x, float frame_y, uint32_t primary_color,
uint32_t secondary_color, ControllerState *state)
{
@ -948,12 +1118,15 @@ void RenderSteelBattalionController(float frame_x, float frame_y, uint32_t prima
void RenderController(float frame_x, float frame_y, uint32_t primary_color,
uint32_t secondary_color, ControllerState *state)
{
if (strcmp(bound_drivers[state->bound], DRIVER_S) == 0)
RenderControllerS(frame_x, frame_y, primary_color, secondary_color,
state);
if (strcmp(bound_drivers[state->bound], DRIVER_STEEL_BATTALION) == 0)
RenderSteelBattalionController(frame_x, frame_y, primary_color, secondary_color,
state);
else if (strcmp(bound_drivers[state->bound], DRIVER_DUKE) == 0)
RenderDukeController(frame_x, frame_y, primary_color, secondary_color,
state);
state);
}
void RenderControllerPort(float frame_x, float frame_y, int i,

View File

@ -167,6 +167,8 @@ void MainMenuInputView::Draw()
if (strcmp(driver, DRIVER_DUKE) == 0)
driver = DRIVER_DUKE_DISPLAY_NAME;
else if (strcmp(driver, DRIVER_S) == 0)
driver = DRIVER_S_DISPLAY_NAME;
else if (strcmp(driver, DRIVER_STEEL_BATTALION) == 0)
driver = DRIVER_STEEL_BATTALION_DISPLAY_NAME;
@ -175,11 +177,14 @@ void MainMenuInputView::Draw()
ImGuiComboFlags_NoArrowButton)) {
const char *available_drivers[] = {
DRIVER_DUKE,
DRIVER_S,
DRIVER_STEEL_BATTALION
};
const char *driver_display_names[] = {
DRIVER_DUKE_DISPLAY_NAME,
DRIVER_S_DISPLAY_NAME,
DRIVER_STEEL_BATTALION_DISPLAY_NAME
};
bool is_selected = false;
int num_drivers = sizeof(driver_display_names) / sizeof(driver_display_names[0]);