BizHawk/waterbox/dsda/BizhawkInterface.c

371 lines
10 KiB
C

#include "BizhawkInterface.h"
bool foundIWAD = false;
bool wipeDone = true;
AllButtons last_buttons[4] = { 0 };
void handle_automap_input(AllButtons buttons, int playerId)
{
static int bigstate = 0;
if (buttons.AutomapToggle && !last_buttons[playerId].AutomapToggle)
{
if (automap_active)
{
AM_Stop(true);
bigstate = 0;
}
else
AM_Start(true);
}
if (buttons.AutomapFollow && !last_buttons[playerId].AutomapFollow)
{
dsda_ToggleConfig(dsda_config_automap_follow, true);
dsda_AddMessage(automap_follow ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF);
}
if (buttons.AutomapGrid && !last_buttons[playerId].AutomapGrid)
{
dsda_ToggleConfig(dsda_config_automap_grid, true);
dsda_AddMessage(automap_grid ? AMSTR_GRIDON : AMSTR_GRIDOFF);
}
if (buttons.AutomapMark && !last_buttons[playerId].AutomapMark)
{
if (!raven)
{
AM_addMark();
doom_printf("%s %d", AMSTR_MARKEDSPOT, markpointnum - 1);
}
}
if (buttons.AutomapClearMarks && !last_buttons[playerId].AutomapClearMarks)
{
AM_clearMarks();
dsda_AddMessage(AMSTR_MARKSCLEARED);
}
if (buttons.AutomapFullZoom && !last_buttons[playerId].AutomapFullZoom)
{
bigstate = !bigstate;
if (bigstate)
{
AM_saveScaleAndLoc();
AM_minOutWindowScale();
}
else
AM_restoreScaleAndLoc();
}
if (buttons.AutomapZoomOut)
{
mtof_zoommul = M_ZOOMOUT;
ftom_zoommul = M_ZOOMIN;
curr_mtof_zoommul = mtof_zoommul;
zoom_leveltime = leveltime;
}
else if (buttons.AutomapZoomIn)
{
mtof_zoommul = M_ZOOMIN;
ftom_zoommul = M_ZOOMOUT;
curr_mtof_zoommul = mtof_zoommul;
zoom_leveltime = leveltime;
}
else
{
stop_zooming = true;
if (leveltime != zoom_leveltime)
AM_StopZooming();
}
if (!automap_follow)
{
if (buttons.AutomapUp) m_paninc.y += FTOM(map_pan_speed);
if (buttons.AutomapDown) m_paninc.y -= FTOM(map_pan_speed);
if (buttons.AutomapRight) m_paninc.x += FTOM(map_pan_speed);
if (buttons.AutomapLeft) m_paninc.x -= FTOM(map_pan_speed);
}
}
void send_input(struct PackedPlayerInput *inputs, int playerId)
{
local_cmds[playerId].forwardmove = inputs->RunSpeed;
local_cmds[playerId].sidemove = inputs->StrafingSpeed;
local_cmds[playerId].lookfly = inputs->FlyLook;
local_cmds[playerId].arti = inputs->ArtifactUse;
local_cmds[playerId].angleturn = inputs->TurningSpeed;
if (inputs->Buttons.Fire) local_cmds[playerId].buttons |= 0b00000001;
if (inputs->Buttons.Use) local_cmds[playerId].buttons |= 0b00000010;
if (inputs->EndPlayer) local_cmds[playerId].arti |= 0b01000000;
if (inputs->Jump) local_cmds[playerId].arti |= 0b10000000;
if (inputs->WeaponSelect)
{
local_cmds[playerId].buttons |= BT_CHANGE;
local_cmds[playerId].buttons |= (inputs->WeaponSelect - 1) << BT_WEAPONSHIFT;
}
if (inputs->Buttons.ChangeGamma && !last_buttons[playerId].ChangeGamma)
{
dsda_CycleConfig(dsda_config_usegamma, true);
dsda_AddMessage(usegamma == 0 ? GAMMALVL0 :
usegamma == 1 ? GAMMALVL1 :
usegamma == 2 ? GAMMALVL2 :
usegamma == 3 ? GAMMALVL3 :
GAMMALVL4);
}
handle_automap_input(inputs->Buttons, playerId);
last_buttons[playerId] = inputs->Buttons;
}
ECL_EXPORT void dsda_get_audio(int *n, void **buffer)
{
int nSamples = 0;
void* audioBuffer = NULL;
audioBuffer = I_CaptureAudio(&nSamples);
// printf("audioBuffer: %p - nSamples: %d\n", audioBuffer, nSamples);
if (n)
*n = nSamples;
if (buffer)
*buffer = audioBuffer;
}
ECL_EXPORT void dsda_get_video(int *w, int *h, int *pitch, uint8_t **buffer, int *paletteSize, uint32_t **paletteBuffer)
{
*buffer = (uint8_t *)headlessGetVideoBuffer();
*w = headlessGetVideoWidth();
*h = headlessGetVideoHeight();
*pitch = headlessGetVideoPitch();
*paletteSize = PALETTE_SIZE;
uint32_t *palette = headlessGetPallette();
for (size_t i = 0; i < PALETTE_SIZE; i++)
{
uint8_t *srcColor = (uint8_t *)&palette[i];
uint8_t *dstColor = (uint8_t *)&_convertedPaletteBuffer[i];
dstColor[0] = srcColor[2];
dstColor[1] = srcColor[1];
dstColor[2] = srcColor[0];
dstColor[3] = srcColor[3];
}
*paletteBuffer = _convertedPaletteBuffer;
}
ECL_EXPORT bool dsda_frame_advance(struct PackedPlayerInput *player1Inputs, struct PackedPlayerInput *player2Inputs, struct PackedPlayerInput *player3Inputs, struct PackedPlayerInput *player4Inputs, struct PackedRenderInfo *renderInfo)
{
// Setting inputs
headlessClearTickCommand();
m_paninc.y = 0;
m_paninc.x = 0;
// Setting Players inputs
send_input(player1Inputs, 0);
send_input(player2Inputs, 1);
send_input(player3Inputs, 2);
send_input(player4Inputs, 3);
// Enabling/Disabling rendering, as required
if ( renderInfo->RenderVideo) headlessEnableVideoRendering();
if ( renderInfo->RenderAudio) headlessEnableAudioRendering();
if (!renderInfo->RenderVideo) headlessDisableVideoRendering();
if (!renderInfo->RenderAudio) headlessDisableAudioRendering();
if ((wipe_Pending() || !wipeDone) && dsda_RenderWipeScreen())
{
wipeDone = wipe_ScreenWipe(1);
I_FinishUpdate();
}
else
{
// Running a single tick
headlessRunSingleTick();
// Move positional sounds
headlessUpdateSounds();
// Updating video
if (renderInfo->RenderVideo)
{
displayplayer = consoleplayer = renderInfo->PlayerPointOfView;
headlessUpdateVideo();
}
}
// Assume wipe is lag
return !wipeDone;
}
ECL_ENTRY void (*input_callback_cb)(void);
void real_input_callback(void)
{
if (input_callback_cb)
input_callback_cb();
}
ECL_EXPORT void dsda_set_input_callback(ECL_ENTRY void (*fecb)(void))
{
input_callback_cb = fecb;
}
ECL_EXPORT int dsda_init(struct InitSettings *settings, int argc, char **argv)
{
printf("Passing arguments: \n");
for (int i = 0; i < argc; i++) printf("%s ", argv[i]);
printf("\n");
// Setting players in game
playeringame[0] = settings->Player1Present;
playeringame[1] = settings->Player2Present;
playeringame[2] = settings->Player3Present;
playeringame[3] = settings->Player4Present;
// Handle class
PlayerClass[0] = (pclass_t)settings->Player1Class;
PlayerClass[1] = (pclass_t)settings->Player2Class;
PlayerClass[2] = (pclass_t)settings->Player3Class;
PlayerClass[3] = (pclass_t)settings->Player4Class;
// Initializing DSDA core
headlessMain(argc, argv);
printf("DSDA Initialized\n");
switch(compatibility_level) {
case prboom_6_compatibility:
longtics = 1;
break;
case mbf21_compatibility:
longtics = 1;
shorttics = !dsda_Flag(dsda_arg_longtics);
break;
default:
longtics = dsda_Flag(dsda_arg_longtics);
break;
}
// Initializing audio
I_SetSoundCap();
I_InitSound();
printf("Audio Initialized\n");
// If required, prevent level exit and game end triggers
preventLevelExit = settings->PreventLevelExit;
preventGameEnd = settings->PreventGameEnd;
printf("Prevent Level Exit: %d\n", preventLevelExit);
printf("Prevent Game End: %d\n", preventGameEnd);
// Enabling DSDA output, for debugging
enableOutput = 1;
return 1;
}
ECL_EXPORT int dsda_add_wad_file(const char *filename, const int size, ECL_ENTRY int (*feload_archive_cb)(const char *filename, uint8_t *buffer, int maxsize))
{
printf("Loading WAD '%s' of size %d...\n", filename, size);
uint8_t *wadFileBuffer = (uint8_t *)alloc_invisible(size);
if (wadFileBuffer == NULL)
{
fprintf(stderr, "Error creating buffer. Do we have enough memory in the waterbox?\n");
return 0;
}
else
printf("Created buffer at address: %p\n", wadFileBuffer);
int loadSize = feload_archive_cb(filename, wadFileBuffer, size);
if (loadSize != size)
{
fprintf(stderr, "Error loading '%s': read %d bytes, but expected %d bytes\n", filename, loadSize, size);
return 0;
}
// Check size is enough
if (size < 5)
{
fprintf(stderr, "Error loading '%s': read %d bytes, which is too small\n", filename, size);
return 0;
}
// Getting wad header
char header[5];
header[0] = wadFileBuffer[0];
header[1] = wadFileBuffer[1];
header[2] = wadFileBuffer[2];
header[3] = wadFileBuffer[3];
header[4] = '\0';
// Safety checks
bool recognizedFormat = false;
// Loading PWAD
if (!strcmp(header, "PWAD"))
{
recognizedFormat = true;
// Loading PWAD
D_AddFile(filename, source_pwad, wadFileBuffer, size);
printf("Loaded PWAD '%s' correctly\n", filename);
}
// Loading IWAD
if (!strcmp(header, "IWAD"))
{
recognizedFormat = true;
// Checking for repeated IWAD
if (foundIWAD == true)
{
fprintf(stderr, "Error with '%s': an IWAD was already loaded before\n", filename);
return 0;
}
foundIWAD = true;
// Loading IWAD
printf("Loading IWAD '%s'...\n", filename);
AddIWAD(filename, wadFileBuffer, size);
printf("Loaded IWAD '%s' correctly\n", filename);
}
// Checking for correct header
if (recognizedFormat == false)
{
fprintf(stderr, "Error with '%s': it contains an unrecognized header '%s'\n", filename, header);
return 0;
}
// All ok
return 1 << gamemode;
}
// the Doom engine doesn't have traditional memory regions because it's not an emulator
// but there's still useful data in memory that we can expose
// so we turn it into artificial memory domains, one for each entity array
// TODO: expose sectors and linedefs like xdre does (but better)
ECL_EXPORT char dsda_read_memory_array(int type, uint32_t addr)
{
char out_of_bounts = 0xFF;
char null_thing = 0x88;
int padded_size = 512; // sizeof(mobj_t) is 464 but we pad for nice representation
if (addr >= numthings * padded_size)
return out_of_bounts;
int index = addr / padded_size;
int offset = addr % padded_size;
mobj_t *mobj = mobj_ptrs[index];
if (mobj == NULL)
return null_thing;
char *data = (char *)mobj + offset;
return *data;
}