Progress with player
This commit is contained in:
parent
d310134441
commit
16143afe0d
|
@ -1,3 +1,3 @@
|
|||
[submodule "tests/roms/nes-test-roms"]
|
||||
path = tests/nes-test-roms
|
||||
path = tests/roms/test-roms
|
||||
url = git@github.com:christopherpow/nes-test-roms.git
|
||||
|
|
|
@ -183,45 +183,5 @@ double HQNState::getFPS() const
|
|||
return fps;
|
||||
}
|
||||
|
||||
// Copied from bizinterface.cpp in BizHawk/quicknes
|
||||
void HQNState::blit(int32_t *dest, const int32_t *colors, int cropleft, int croptop, int cropright, int cropbottom) const
|
||||
{
|
||||
// what is the point of the 256 color bitmap and the dynamic color allocation to it?
|
||||
// why not just render directly to a 512 color bitmap with static palette positions?
|
||||
// Nes_Emu *e = m_emu; // e was a parameter but since this is now part of a class, it's just in here
|
||||
// const int srcpitch = e->frame().pitch;
|
||||
// const unsigned char *src = e->frame().pixels;
|
||||
// const unsigned char *const srcend = src + (e->image_height - cropbottom) * srcpitch;
|
||||
//
|
||||
// const short *lut = e->frame().palette;
|
||||
//
|
||||
// const int rowlen = 256 - cropleft - cropright;
|
||||
//
|
||||
// src += cropleft;
|
||||
// src += croptop * srcpitch;
|
||||
//
|
||||
// for (; src < srcend; src += srcpitch)
|
||||
// {
|
||||
// for (int i = 0; i < rowlen; i++)
|
||||
// {
|
||||
// *dest++ = colors[lut[src[i]]];
|
||||
// }
|
||||
// }
|
||||
|
||||
Nes_Emu *e = m_emu; // e was a parameter but since this is now part of a class, it's just in here
|
||||
const unsigned char *in_pixels = e->frame().pixels;
|
||||
int32_t *out_pixels = dest;
|
||||
|
||||
for (unsigned h = 0; h < Nes_Emu::image_height; h++, in_pixels += e->frame().pitch, out_pixels += Nes_Emu::image_width)
|
||||
for (unsigned w = 0; w < Nes_Emu::image_width; w++)
|
||||
{
|
||||
unsigned col = e->frame().palette[in_pixels[w]];
|
||||
const Nes_Emu::rgb_t& rgb = m_emu->nes_colors[col];
|
||||
unsigned r = rgb.red;
|
||||
unsigned g = rgb.green;
|
||||
unsigned b = rgb.blue;
|
||||
out_pixels[w] = (r << 16) | (g << 8) | (b << 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace hqn
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <core/Nes_Emu.h>
|
||||
#include <cstdint>
|
||||
#include <stdio.h>
|
||||
|
||||
#define BLIT_SIZE 65536
|
||||
|
||||
|
@ -104,12 +105,6 @@ public:
|
|||
*/
|
||||
double getFPS() const;
|
||||
|
||||
/**
|
||||
* Blit the contents of the NES screen to dest. Dest should be able to hold 256*240 ints.
|
||||
* Unless you want to change the color palette you should use NES_VIDEO_PALETTE for colors.
|
||||
*/
|
||||
void blit(int32_t *dest, const int32_t *colors, int cropleft, int croptop, int cropright, int cropbottom) const;
|
||||
|
||||
inline HQNListener *getListener() const
|
||||
{ return m_listener; }
|
||||
|
||||
|
@ -152,5 +147,45 @@ void printUsage(const char *filename);
|
|||
|
||||
} // end namespace hqn
|
||||
|
||||
// Copied from bizinterface.cpp in BizHawk/quicknes
|
||||
inline void saveBlit(const Nes_Emu *e, int32_t *dest, const int32_t *colors, int cropleft, int croptop, int cropright, int cropbottom)
|
||||
{
|
||||
// what is the point of the 256 color bitmap and the dynamic color allocation to it?
|
||||
// why not just render directly to a 512 color bitmap with static palette positions?
|
||||
// Nes_Emu *e = m_emu; // e was a parameter but since this is now part of a class, it's just in here
|
||||
// const int srcpitch = e->frame().pitch;
|
||||
// const unsigned char *src = e->frame().pixels;
|
||||
// const unsigned char *const srcend = src + (e->image_height - cropbottom) * srcpitch;
|
||||
//
|
||||
// const short *lut = e->frame().palette;
|
||||
//
|
||||
// const int rowlen = 256 - cropleft - cropright;
|
||||
//
|
||||
// src += cropleft;
|
||||
// src += croptop * srcpitch;
|
||||
//
|
||||
// for (; src < srcend; src += srcpitch)
|
||||
// {
|
||||
// for (int i = 0; i < rowlen; i++)
|
||||
// {
|
||||
// *dest++ = colors[lut[src[i]]];
|
||||
// }
|
||||
// }
|
||||
|
||||
const unsigned char *in_pixels = e->frame().pixels;
|
||||
if (in_pixels == NULL) return;
|
||||
int32_t *out_pixels = dest;
|
||||
|
||||
for (unsigned h = 0; h < Nes_Emu::image_height; h++, in_pixels += e->frame().pitch, out_pixels += Nes_Emu::image_width)
|
||||
for (unsigned w = 0; w < Nes_Emu::image_width; w++)
|
||||
{
|
||||
unsigned col = e->frame().palette[in_pixels[w]];
|
||||
const Nes_Emu::rgb_t& rgb = e->nes_colors[col];
|
||||
unsigned r = rgb.red;
|
||||
unsigned g = rgb.green;
|
||||
unsigned b = rgb.blue;
|
||||
out_pixels[w] = (r << 16) | (g << 8) | (b << 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __HQN_H__ */
|
||||
|
|
|
@ -190,7 +190,6 @@ void GUIController::update(bool readNES)
|
|||
|
||||
if (SDL_LockTexture(m_tex, nullptr, &nesPixels, &pitch) < 0) return;
|
||||
|
||||
m_state.blit((int32_t*)nesPixels, HQNState::NES_VIDEO_PALETTE, 0, 0, 0, 0);
|
||||
SDL_UnlockTexture(m_tex);
|
||||
|
||||
// render to screen
|
||||
|
@ -201,7 +200,7 @@ void GUIController::update(bool readNES)
|
|||
processEvents();
|
||||
}
|
||||
|
||||
void GUIController::update_blit(int32_t* blit, SDL_Surface* base, SDL_Surface* button_a, SDL_Surface* button_b, SDL_Surface* button_select, SDL_Surface* button_start, SDL_Surface* button_up, SDL_Surface* button_down, SDL_Surface* button_left, SDL_Surface* button_right)
|
||||
void GUIController::update_blit(const int32_t* blit, SDL_Surface* base, SDL_Surface* button_a, SDL_Surface* button_b, SDL_Surface* button_select, SDL_Surface* button_start, SDL_Surface* button_up, SDL_Surface* button_down, SDL_Surface* button_left, SDL_Surface* button_right)
|
||||
{
|
||||
void *nesPixels = nullptr;
|
||||
int pitch = 0;
|
||||
|
|
|
@ -99,7 +99,7 @@ public:
|
|||
*/
|
||||
void setCloseOperation(CloseOperation ops);
|
||||
CloseOperation getCloseOperation() const;
|
||||
void update_blit(int32_t* blit, SDL_Surface* base, SDL_Surface* button_a, SDL_Surface* button_b, SDL_Surface* button_select, SDL_Surface* button_start, SDL_Surface* button_up, SDL_Surface* button_down, SDL_Surface* button_left, SDL_Surface* button_right);
|
||||
void update_blit(const int32_t* blit, SDL_Surface* base, SDL_Surface* button_a, SDL_Surface* button_b, SDL_Surface* button_select, SDL_Surface* button_start, SDL_Surface* button_up, SDL_Surface* button_down, SDL_Surface* button_left, SDL_Surface* button_right);
|
||||
|
||||
// Methods overriden from superclass.
|
||||
virtual void onLoadROM(HQNState *state, const char *filename);
|
||||
|
|
|
@ -59,7 +59,7 @@ quickerNESPlayerSrc = [
|
|||
|
||||
quickerNESPlayerIncludeDirs = include_directories([ 'source'])
|
||||
quickerNESPlayerDependencies = [ dependency('sdl2'), dependency('SDL2_image') ]
|
||||
quickerNESPlayerCPPFlags = [ ]
|
||||
quickerNESPlayerCPPFlags = [ '-DNCURSES' ]
|
||||
quickerNESPlayerCFlags = [ ]
|
||||
quickerNESPlayerLinkArgs = [ '-lncurses' ]
|
||||
|
||||
|
|
|
@ -177,7 +177,10 @@ const char * Nes_Emu::emulate_frame( int joypad1, int joypad2 )
|
|||
f->joypad_read_count = emu.joypad_read_count;
|
||||
f->burst_phase = emu.ppu.burst_phase;
|
||||
f->pitch = emu.ppu.host_row_bytes;
|
||||
f->pixels = emu.ppu.host_pixels + f->left;
|
||||
if (emu.ppu.host_pixels != NULL)
|
||||
f->pixels = emu.ppu.host_pixels + f->left;
|
||||
else
|
||||
f->pixels = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -46,6 +46,8 @@ public:
|
|||
enum { image_width = 256 };
|
||||
enum { image_height = 240 };
|
||||
|
||||
const uint8_t* getHostPixels () const { return emu.ppu.host_pixels; }
|
||||
|
||||
// Basic emulation
|
||||
|
||||
// Emulate one video frame using joypad1 and joypad2 as input. Afterwards, image
|
||||
|
@ -265,6 +267,7 @@ inline void Nes_Emu::set_pixels( void* p, long n )
|
|||
emu.ppu.host_row_bytes = n;
|
||||
}
|
||||
|
||||
|
||||
inline uint8_t const* Nes_Emu::chr_mem()
|
||||
{
|
||||
return cart()->chr_size() ? (uint8_t*) cart()->chr() : emu.ppu.impl->chr_ram;
|
||||
|
|
|
@ -32,11 +32,6 @@ class EmuInstance
|
|||
auto result = _nes->load_ines(romFile);
|
||||
if (result != 0) EXIT_WITH_ERROR("Could not initialize emulator with rom file: %s\n", romFilePath.c_str());
|
||||
|
||||
// Setting base memory pointers
|
||||
_baseMem = _nes->low_mem();
|
||||
_ppuNameTableMem = _nes->nametable_mem();
|
||||
_highMem = _nes->high_mem();
|
||||
|
||||
// Getting state size to use
|
||||
_stateSize = getStateSizeImpl();
|
||||
|
||||
|
@ -44,6 +39,10 @@ class EmuInstance
|
|||
if (stateFilePath != "") loadStateFile(stateFilePath);
|
||||
}
|
||||
|
||||
uint8_t* getLowMem() { return _nes->low_mem(); };
|
||||
uint8_t* getNametableMem() { return _nes->nametable_mem(); };
|
||||
uint8_t* getHighMem() { return _nes->high_mem();};
|
||||
|
||||
void loadStateFile(const std::string& stateFilePath)
|
||||
{
|
||||
// Loading state data
|
||||
|
@ -167,11 +166,6 @@ class EmuInstance
|
|||
// Emulator instance
|
||||
Nes_Emu* _nes;
|
||||
|
||||
// Base low-memory pointer
|
||||
uint8_t* _baseMem;
|
||||
uint8_t* _ppuNameTableMem;
|
||||
uint8_t* _highMem;
|
||||
|
||||
// State size for the given rom
|
||||
size_t _stateSize;
|
||||
|
||||
|
|
|
@ -11,39 +11,54 @@
|
|||
|
||||
#define _INVERSE_FRAME_RATE 16667
|
||||
|
||||
struct stepData_t
|
||||
{
|
||||
std::string input;
|
||||
uint8_t* stateData;
|
||||
int32_t curBlit[BLIT_SIZE];
|
||||
};
|
||||
|
||||
class PlaybackInstance
|
||||
{
|
||||
private:
|
||||
|
||||
// Storage for the HQN state
|
||||
hqn::HQNState _hqnState;
|
||||
|
||||
// Storage for the HQN GUI controller
|
||||
hqn::GUIController* _hqnGUI;
|
||||
|
||||
// Pointer to the contained emulator instance
|
||||
EmuInstance* const _emu;
|
||||
|
||||
// Flag to store whether to use the button overlay
|
||||
bool _useOverlay = false;
|
||||
|
||||
// Overlay info
|
||||
std::string _overlayPath;
|
||||
SDL_Surface* _overlayBaseSurface = NULL;
|
||||
SDL_Surface* _overlayButtonASurface;
|
||||
SDL_Surface* _overlayButtonBSurface;
|
||||
SDL_Surface* _overlayButtonSelectSurface;
|
||||
SDL_Surface* _overlayButtonStartSurface;
|
||||
SDL_Surface* _overlayButtonLeftSurface;
|
||||
SDL_Surface* _overlayButtonRightSurface;
|
||||
SDL_Surface* _overlayButtonUpSurface;
|
||||
SDL_Surface* _overlayButtonDownSurface;
|
||||
|
||||
public:
|
||||
|
||||
void addStep(const std::string& input)
|
||||
{
|
||||
stepData_t step;
|
||||
step.input = input;
|
||||
step.stateData = (uint8_t*) calloc(_emu->getStateSize(), 1);
|
||||
_emu->serializeState(step.stateData);
|
||||
saveBlit(_emu->getInternalEmulator(), step.curBlit, hqn::HQNState::NES_VIDEO_PALETTE, 0, 0, 0, 0);
|
||||
|
||||
// Adding the step into the sequence
|
||||
_stepSequence.push_back(step);
|
||||
}
|
||||
|
||||
// Initializes the playback module instance
|
||||
PlaybackInstance(EmuInstance* emu, const std::string& overlayPath = "") : _emu(emu)
|
||||
PlaybackInstance(EmuInstance* emu, const std::string sequenceString, const std::string& overlayPath = "") : _emu(emu)
|
||||
{
|
||||
// Loading Emulator instance HQN
|
||||
_hqnState.m_emu = _emu->getInternalEmulator();
|
||||
static uint8_t video_buffer[Nes_Emu::image_width * Nes_Emu::image_height];
|
||||
_hqnState.m_emu->set_pixels(video_buffer, Nes_Emu::image_width+8);
|
||||
|
||||
// Building sequence information
|
||||
const auto inputSequence = split(sequenceString, ' ');
|
||||
|
||||
// Building sequence information
|
||||
for (const auto& input : inputSequence)
|
||||
{
|
||||
// Adding new step
|
||||
addStep(input);
|
||||
|
||||
// Advance state based on the input received
|
||||
_emu->advanceState(input);
|
||||
}
|
||||
|
||||
// Adding last step with no input
|
||||
addStep("<End Of Sequence>");
|
||||
|
||||
// Loading overlay, if provided
|
||||
if (overlayPath != "")
|
||||
{
|
||||
// Using overlay
|
||||
|
@ -89,11 +104,6 @@ class PlaybackInstance
|
|||
if (_overlayButtonDownSurface == NULL) EXIT_WITH_ERROR("[Error] Could not load image: %s, Reason: %s\n", imagePath.c_str(), SDL_GetError());
|
||||
}
|
||||
|
||||
// Loading Emulator instance HQN
|
||||
_hqnState.m_emu = _emu->getInternalEmulator();
|
||||
static uint8_t video_buffer[Nes_Emu::image_width * Nes_Emu::image_height];
|
||||
_hqnState.m_emu->set_pixels(video_buffer, Nes_Emu::image_width+8);
|
||||
|
||||
// Opening rendering window
|
||||
SDL_SetMainReady();
|
||||
|
||||
|
@ -108,14 +118,13 @@ class PlaybackInstance
|
|||
}
|
||||
|
||||
// Function to render frame
|
||||
void renderFrame(const uint16_t currentStep, const std::string& move)
|
||||
void renderFrame(const size_t stepId)
|
||||
{
|
||||
// Sleeping for the inverse frame rate
|
||||
usleep(_INVERSE_FRAME_RATE);
|
||||
|
||||
// Storing current game state
|
||||
uint8_t emuState[_emu->getStateSize()];
|
||||
_emu->serializeState(emuState);
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
|
||||
// Getting step information
|
||||
const auto& step = _stepSequence[stepId];
|
||||
|
||||
// Pointer to overlay images (NULL if unused)
|
||||
SDL_Surface* overlayButtonASurface = NULL;
|
||||
|
@ -130,24 +139,76 @@ class PlaybackInstance
|
|||
// Load correct overlay images, if using overlay
|
||||
if (_useOverlay == true)
|
||||
{
|
||||
if (move.find("A") != std::string::npos) overlayButtonASurface = _overlayButtonASurface;
|
||||
if (move.find("B") != std::string::npos) overlayButtonBSurface = _overlayButtonBSurface;
|
||||
if (move.find("S") != std::string::npos) overlayButtonSelectSurface = _overlayButtonSelectSurface;
|
||||
if (move.find("T") != std::string::npos) overlayButtonStartSurface = _overlayButtonStartSurface;
|
||||
if (move.find("L") != std::string::npos) overlayButtonLeftSurface = _overlayButtonLeftSurface;
|
||||
if (move.find("R") != std::string::npos) overlayButtonRightSurface = _overlayButtonRightSurface;
|
||||
if (move.find("U") != std::string::npos) overlayButtonUpSurface = _overlayButtonUpSurface;
|
||||
if (move.find("D") != std::string::npos) overlayButtonDownSurface = _overlayButtonDownSurface;
|
||||
if (step.input.find("A") != std::string::npos) overlayButtonASurface = _overlayButtonASurface;
|
||||
if (step.input.find("B") != std::string::npos) overlayButtonBSurface = _overlayButtonBSurface;
|
||||
if (step.input.find("S") != std::string::npos) overlayButtonSelectSurface = _overlayButtonSelectSurface;
|
||||
if (step.input.find("T") != std::string::npos) overlayButtonStartSurface = _overlayButtonStartSurface;
|
||||
if (step.input.find("L") != std::string::npos) overlayButtonLeftSurface = _overlayButtonLeftSurface;
|
||||
if (step.input.find("R") != std::string::npos) overlayButtonRightSurface = _overlayButtonRightSurface;
|
||||
if (step.input.find("U") != std::string::npos) overlayButtonUpSurface = _overlayButtonUpSurface;
|
||||
if (step.input.find("D") != std::string::npos) overlayButtonDownSurface = _overlayButtonDownSurface;
|
||||
}
|
||||
|
||||
// Since renderer is off by one frame, we need to emulate an additional frame
|
||||
_emu->advanceState(0,0);
|
||||
int32_t curImage[BLIT_SIZE];
|
||||
_hqnState.blit(curImage, hqn::HQNState::NES_VIDEO_PALETTE, 0, 0, 0, 0);
|
||||
_hqnGUI->update_blit(curImage, _overlayBaseSurface, overlayButtonASurface, overlayButtonBSurface, overlayButtonSelectSurface, overlayButtonStartSurface, overlayButtonLeftSurface, overlayButtonRightSurface, overlayButtonUpSurface, overlayButtonDownSurface);
|
||||
|
||||
// Reload game state
|
||||
_emu->deserializeState(emuState);
|
||||
// Updating image
|
||||
_hqnGUI->update_blit(step.curBlit, _overlayBaseSurface, overlayButtonASurface, overlayButtonBSurface, overlayButtonSelectSurface, overlayButtonStartSurface, overlayButtonLeftSurface, overlayButtonRightSurface, overlayButtonUpSurface, overlayButtonDownSurface);
|
||||
}
|
||||
|
||||
size_t getSequenceLength() const
|
||||
{
|
||||
return _stepSequence.size();
|
||||
}
|
||||
|
||||
const std::string getInput(const size_t stepId) const
|
||||
{
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
|
||||
// Getting step information
|
||||
const auto& step = _stepSequence[stepId];
|
||||
|
||||
// Returning step input
|
||||
return step.input;
|
||||
}
|
||||
|
||||
const uint8_t* getStateData(const size_t stepId) const
|
||||
{
|
||||
// Checking the required step id does not exceed contents of the sequence
|
||||
if (stepId > _stepSequence.size()) EXIT_WITH_ERROR("[Error] Attempting to render a step larger than the step sequence");
|
||||
|
||||
// Getting step information
|
||||
const auto& step = _stepSequence[stepId];
|
||||
|
||||
// Returning step input
|
||||
return step.stateData;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Internal sequence information
|
||||
std::vector<stepData_t> _stepSequence;
|
||||
|
||||
// Storage for the HQN state
|
||||
hqn::HQNState _hqnState;
|
||||
|
||||
// Storage for the HQN GUI controller
|
||||
hqn::GUIController* _hqnGUI;
|
||||
|
||||
// Pointer to the contained emulator instance
|
||||
EmuInstance* const _emu;
|
||||
|
||||
// Flag to store whether to use the button overlay
|
||||
bool _useOverlay = false;
|
||||
|
||||
// Overlay info
|
||||
std::string _overlayPath;
|
||||
SDL_Surface* _overlayBaseSurface = NULL;
|
||||
SDL_Surface* _overlayButtonASurface;
|
||||
SDL_Surface* _overlayButtonBSurface;
|
||||
SDL_Surface* _overlayButtonSelectSurface;
|
||||
SDL_Surface* _overlayButtonStartSurface;
|
||||
SDL_Surface* _overlayButtonLeftSurface;
|
||||
SDL_Surface* _overlayButtonRightSurface;
|
||||
SDL_Surface* _overlayButtonUpSurface;
|
||||
SDL_Surface* _overlayButtonDownSurface;
|
||||
};
|
||||
|
|
|
@ -50,17 +50,105 @@ int main(int argc, char *argv[])
|
|||
// Getting reproduce flag
|
||||
bool disableRender = program.get<bool>("--disableRender");
|
||||
|
||||
// Loading sequence file
|
||||
std::string inputSequence;
|
||||
auto status = loadStringFromFile(inputSequence, sequenceFilePath.c_str());
|
||||
if (status == false) EXIT_WITH_ERROR("[ERROR] Could not find or read from sequence file: %s\n", sequenceFilePath.c_str());
|
||||
|
||||
// Initializing terminal
|
||||
initializeTerminal();
|
||||
|
||||
// Printing provided parameters
|
||||
printf("Rom File Path: %s\n", romFilePath.c_str());
|
||||
printf("Sequence File Path: %s\n", sequenceFilePath.c_str());
|
||||
printf("State File Path: %s\n", stateFilePath.c_str());
|
||||
printw("[] Rom File Path: '%s'\n", romFilePath.c_str());
|
||||
printw("[] Sequence File Path: '%s'\n", sequenceFilePath.c_str());
|
||||
printw("[] State File Path: '%s'\n", stateFilePath.empty() ? "<Boot Start>" : stateFilePath.c_str());
|
||||
printw("[] Generating Sequence...\n");
|
||||
|
||||
refreshTerminal();
|
||||
|
||||
// Creating emulator instance
|
||||
auto e = EmuInstance(romFilePath, stateFilePath);
|
||||
|
||||
// Creating playback instance
|
||||
auto p = PlaybackInstance(&e);
|
||||
auto p = PlaybackInstance(&e, inputSequence);
|
||||
|
||||
while(true) p.renderFrame(0, "");
|
||||
// Flag to continue running playback
|
||||
bool continueRunning = true;
|
||||
|
||||
// Variable for current step in view
|
||||
ssize_t sequenceLength = p.getSequenceLength();
|
||||
ssize_t currentStep = 0;
|
||||
|
||||
// Flag to display frame information
|
||||
bool showFrameInfo = true;
|
||||
|
||||
// Interactive section
|
||||
while(continueRunning)
|
||||
{
|
||||
// Updating display
|
||||
if (disableRender == false) p.renderFrame(currentStep);
|
||||
|
||||
// Getting input
|
||||
const auto& input = p.getInput(currentStep);
|
||||
|
||||
// Getting state data
|
||||
//const auto stateData = p.getStateData(currentStep);
|
||||
|
||||
// Printing data and commands
|
||||
if (showFrameInfo)
|
||||
{
|
||||
clearTerminal();
|
||||
|
||||
printw("[] ----------------------------------------------------------------\n");
|
||||
printw("[] Current Step #: %lu / %lu\n", currentStep + 1, sequenceLength);
|
||||
printw("[] Input: %s\n", input.c_str());
|
||||
|
||||
// Only print commands if not in reproduce mode
|
||||
if (isReproduce == false) printw("[] Commands: n: -1 m: +1 | h: -10 | j: +10 | y: -100 | u: +100 | k: -1000 | i: +1000 | s: quicksave | p: play | q: quit\n");
|
||||
|
||||
refreshTerminal();
|
||||
}
|
||||
|
||||
// Resetting show frame info flag
|
||||
showFrameInfo = true;
|
||||
|
||||
// Get command
|
||||
auto command = getKeyPress();
|
||||
|
||||
// Advance/Rewind commands
|
||||
if (command == 'n') currentStep = currentStep - 1;
|
||||
if (command == 'm') currentStep = currentStep + 1;
|
||||
if (command == 'h') currentStep = currentStep - 10;
|
||||
if (command == 'j') currentStep = currentStep + 10;
|
||||
if (command == 'y') currentStep = currentStep - 100;
|
||||
if (command == 'u') currentStep = currentStep + 100;
|
||||
if (command == 'k') currentStep = currentStep - 1000;
|
||||
if (command == 'i') currentStep = currentStep + 1000;
|
||||
|
||||
// Correct current step if requested more than possible
|
||||
if (currentStep < 0) currentStep = 0;
|
||||
if (currentStep >= sequenceLength) currentStep = sequenceLength-1;
|
||||
|
||||
// Quicksave creation command
|
||||
if (command == 's')
|
||||
{
|
||||
// Storing state file
|
||||
std::string saveFileName = "quicksave.state";
|
||||
e.saveStateFile(saveFileName);
|
||||
printw("[] Saved state to %s\n", saveFileName.c_str());
|
||||
|
||||
// Do no show frame info again after this action
|
||||
showFrameInfo = false;
|
||||
}
|
||||
|
||||
// Start playback from current point
|
||||
if (command == 'p') isReproduce = true;
|
||||
|
||||
// Start playback from current point
|
||||
if (command == 'q') continueRunning = false;
|
||||
}
|
||||
|
||||
// Ending ncurses window
|
||||
finalizeTerminal();
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
|
||||
// If we use NCurses, we need to use the appropriate printing function
|
||||
|
@ -56,7 +57,33 @@ inline int getKeyPress()
|
|||
return getch();
|
||||
}
|
||||
|
||||
#endif
|
||||
void initializeTerminal()
|
||||
{
|
||||
// Initializing ncurses screen
|
||||
initscr();
|
||||
cbreak();
|
||||
noecho();
|
||||
nodelay(stdscr, TRUE);
|
||||
scrollok(stdscr, TRUE);
|
||||
}
|
||||
|
||||
void clearTerminal()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void finalizeTerminal()
|
||||
{
|
||||
endwin();
|
||||
}
|
||||
|
||||
void refreshTerminal()
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
||||
#endif // NCURSES
|
||||
|
||||
// Function to split a string into a sub-strings delimited by a character
|
||||
// Taken from stack overflow answer to https://stackoverflow.com/questions/236129/how-do-i-iterate-over-the-words-of-a-string
|
||||
|
|
Loading…
Reference in New Issue