More cleanups

This commit is contained in:
Ilari Liusvaara 2011-09-16 21:09:22 +03:00
parent 1e0d57b503
commit 366336d560
11 changed files with 566 additions and 1050 deletions

View file

@ -6,7 +6,7 @@ OBJECTS = controllerdata.o fieldsplit.o memorymanip.o misc.o movie.o moviefile.o
#AVI dumper
OBJECTS += avidump/avidump-control.o avidump/avidump.o avidump/sox.o
PROGRAMS = lsnes.exe movietrunctest.exe
PROGRAMS = lsnes.exe
CFLAGS = -I. $(shell sdl-config --cflags) $(USER_CFLAGS)
HOSTCCFLAGS = $(USER_HOSTCCFLAGS)

View file

@ -9,7 +9,6 @@
#define FLAG_SYNC CONTROL_FRAME_SYNC
std::string global_rerecord_count = "0";
//std::ofstream debuglog("movie-debugging-log", std::ios::out | std::ios::app);
namespace
@ -194,7 +193,7 @@ void movie::set_controls(controls_t controls) throw()
current_controls = controls;
}
uint32_t movie::count_changes(uint64_t first_subframe)
uint32_t movie::count_changes(uint64_t first_subframe) throw()
{
if(first_subframe >= movie_data.size())
return 0;
@ -609,7 +608,7 @@ long movie::get_reset_status() throw()
return hi * 10000 + lo;
}
uint64_t movie::frame_subframes(uint64_t frame)
uint64_t movie::frame_subframes(uint64_t frame) throw()
{
if(frame < cached_frame)
clear_caches();
@ -621,13 +620,13 @@ uint64_t movie::frame_subframes(uint64_t frame)
return count_changes(p);
}
void movie::clear_caches()
void movie::clear_caches() throw()
{
cached_frame = 1;
cached_subframe = 0;
}
controls_t movie::read_subframe(uint64_t frame, uint64_t subframe)
controls_t movie::read_subframe(uint64_t frame, uint64_t subframe) throw()
{
if(frame < cached_frame)
clear_caches();

219
movie.hpp
View file

@ -7,245 +7,237 @@
#include "controllerdata.hpp"
/**
* \brief Movie being played back or recorded
* Movie being played back or recorded
*/
class movie
{
public:
/**
* \brief Construct new empty movie.
* \throws std::bad_alloc Not enough memory.
* Construct new empty movie.
*
* throws std::bad_alloc: Not enough memory.
*/
movie() throw(std::bad_alloc);
/**
* \brief Is the movie in readonly mode?
* Is the movie in readonly mode?
*
* \return True if in read-only mode, false if in read-write mode.
* returns: True if in read-only mode, false if in read-write mode.
*/
bool readonly_mode() throw();
/**
* \brief Switch between modes
*
* Switches movie to read-only or read-write mode. If switching to read-write mode, the movie is truncated.
*
* \param enable If true, switch to read-only mode, else to read-write mode.
* \throws std::bad_alloc Not enough memory.
* parameter enable: If true, switch to read-only mode, else to read-write mode.
* throws std::bad_alloc: Not enough memory.
*/
void readonly_mode(bool enable) throw(std::bad_alloc);
/**
* \brief Get movie rerecord count
*
* Returns the movie rerecord count (this is not the same thing as global rerecord count).
*
* \return The movie rerecord count
* \throws std::bad_alloc Not enough memory.
* returns: The movie rerecord count
* throws std::bad_alloc: Not enough memory.
*/
std::string rerecord_count() throw(std::bad_alloc);
/**
* \brief Set movie rerecord count
*
* Sets the movie rerecord count (this is not the same thing as global rerecord count).
*
* \param count The new rerecord count
* \throws std::bad_alloc Not enough memory.
* parameter count: The new rerecord count
* throws std::bad_alloc: Not enough memory.
*/
void rerecord_count(const std::string& count) throw(std::bad_alloc);
/**
* \brief Read project ID
* \return The project ID
* \throws std::bad_alloc Not enough memory.
* Read project ID
*
* returns: The project ID
* throws std::bad_alloc: Not enough memory.
*/
std::string project_id() throw(std::bad_alloc);
/**
* \brief Set project ID
* \param id New project ID.
* \throws std::bad_alloc Not enough memory.
* brief Set project ID
*
* parameter id: New project ID.
* throws std::bad_alloc: Not enough memory.
*/
void project_id(const std::string& id) throw(std::bad_alloc);
/**
* \brief Get number of frames in movie
* \return The number of frames.
* Get number of frames in movie
*
* returns: The number of frames.
*/
uint64_t get_frame_count() throw();
/**
* \brief Get number of currnet frame in movie
* Get number of current frame in movie
*
* The first frame in movie is 1. 0 is "before first frame" value.
*
* \return The number of frame
* returns: The number of frame
*/
uint64_t get_current_frame() throw();
/**
* \brief Get number of lag frames so far
* \return The number of lag frames.
* Get number of lag frames so far
*
* returns: The number of lag frames.
*/
uint64_t get_lag_frames() throw();
/**
* \brief Advance to next frame.
*
* This function advances to next frame in movie, discarding subframes not used. If the frame is lag frame, it is
* counted as lag frame and subframe entry for it is made (if in readwrite mode).
*
* \throw std::bad_alloc Not enough memory.
* throws std::bad_alloc: Not enough memory.
*/
void next_frame() throw(std::bad_alloc);
/**
* \brief Get data ready flag for control index
*
* Reads the data ready flag. On new frame, all data ready flags are unset. On reading control, its data ready
* flag is unset.
*
* \param controlindex The index of control to read it for.
* \return The read value.
* \throws std::logic_error Invalid control index.
* parameter controlindex: The index of control to read it for.
* returns: The read value.
* throws std::logic_error: Invalid control index.
*/
bool get_DRDY(unsigned controlindex) throw(std::logic_error);
/**
* \brief Get data ready flag for given (port,controller,index) index
*
* Reads the data ready flag. On new frame, all data ready flags are unset. On reading control, its data ready
* flag is unset.
*
* This differs from get_DRDY(unsigned) in that this takes (port, controller,index) tuple.
*
* \param port The port controller is connected to (0 or 1)
* \param controller The controller number within port (0 to 3)
* \param index The index of control in controller (0 to 11)
* \return The read value.
* \throws std::logic_error Invalid control index.
* parameter port: The port controller is connected to (0 or 1)
* parameter controller: The controller number within port (0 to 3)
* parameter index: The index of control in controller (0 to 11)
* returns: The read value.
* throws std::logic_error: Invalid control index.
*/
bool get_DRDY(unsigned port, unsigned controller, unsigned index) throw(std::logic_error);
/**
* \brief Set all data ready flags
* Set all data ready flags
*/
void set_all_DRDY() throw();
/**
* \brief Poll next value for given control index
*
* Poll a control. Note that index 0 (sync flag) always reads as released.
*
* \param controlindex The index
* \return The read value
* \throws std::bad_alloc Not enough memory.
* \throws std::logic_error Invalid control index or before movie start.
* parameter controlindex: The index
* returns: The read value
* throws std::bad_alloc: Not enough memory.
* throws std::logic_error: Invalid control index or before movie start.
*/
short next_input(unsigned controlindex) throw(std::bad_alloc, std::logic_error);
/**
* \brief Poll next value for given (port, controller, index) index
* Poll a control by (port, controller, index) tuple.
*
* Poll a control.
*
* \param port The port controller is connected to (0 or 1)
* \param controller The controller number within port (0 to 3)
* \param index The index of control in controller (0 to 11)
* \return The read value
* \throws std::bad_alloc Not enough memory.
* \throws std::logic_error Invalid port, controller or index or before movie start.
* parameter port: The port controller is connected to (0 or 1)
* parameter controller: The controller number within port (0 to 3)
* parameter index: The index of control in controller (0 to 11)
* returns: The read value
* throws std::bad_alloc: Not enough memory.
* throws std::logic_error: Invalid port, controller or index or before movie start.
*/
short next_input(unsigned port, unsigned controller, unsigned index) throw(std::bad_alloc, std::logic_error);
/**
* \brief Set values of current controls
*
* Set current control values. These are read in readwrite mode.
*
* \param controls The new controls.
* parameter controls: The new controls.
*/
void set_controls(controls_t controls) throw();
/**
* \brief Get values of current controls
*
* Get current control values in effect.
*
* \return Controls
* returns: Controls
*/
controls_t get_controls() throw();
/**
* \brief Load a movie.
*
* Loads a movie plus some other parameters. The playback pointer is positioned to start of movie and readonly
* mode is enabled.
*
* \param rerecs Movie rerecord count.
* \param project_id Project ID of movie.
* \param input The input track.
* \throws std::bad_alloc Not enough memory.
* \throws std::runtime_error Bad movie data.
* parameter rerecs: Movie rerecord count.
* parameter project_id: Project ID of movie.
* parameter input: The input track.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Bad movie data.
*/
void load(const std::string& rerecs, const std::string& project_id, const std::vector<controls_t>& input)
throw(std::bad_alloc, std::runtime_error);
/**
* \brief Save a movie.
*
* Saves the movie data.
*
* \return The movie data.
* \throws std::bad_alloc Not enough memory.
* returns: The movie data.
* throws std::bad_alloc: Not enough memory.
*/
std::vector<controls_t> save() throw(std::bad_alloc);
/**
* \brief Save the state of movie code
*
* This method serializes the state of movie code.
*
* \return The serialized state.
* \throws std::bad_alloc Not enough memory.
* returns: The serialized state.
* throws std::bad_alloc: Not enough memory.
*/
std::vector<uint8_t> save_state() throw(std::bad_alloc);
/**
* \brief Restore the state of movie code
*
* Given previous serialized state from this movie, restore the state.
*
* \param state The state to restore.
* \param ro If true, restore in readonly mode, otherwise in readwrite mode.
* \throw std::bad_alloc Not enough memory.
* \throw std::runtime_error State is not from this movie or states is corrupt.
* parameter state: The state to restore.
* parameter ro: If true, restore in readonly mode, otherwise in readwrite mode.
* throw std::bad_alloc: Not enough memory.
* throw std::runtime_error: State is not from this movie or states is corrupt.
*/
size_t restore_state(const std::vector<uint8_t>& state, bool ro) throw(std::bad_alloc, std::runtime_error);
/**
* \brief Get reset status for current frame.
* Get reset status for current frame.
*
* \return -1 if current frame doesn't have a reset. Otherwise number of cycles to wait for delayed reset (0 is
* returns: -1 if current frame doesn't have a reset. Otherwise number of cycles to wait for delayed reset (0 is
* immediate reset).
*/
long get_reset_status() throw();
/**
* \brief Commit a reset.
* Commit a reset (writes a reset into current frame in readwrite mode).
*
* parameter delay: The number of cycles to delay the reset.
*/
void commit_reset(long delay) throw(std::bad_alloc);
/**
* \brief Get how manyth poll in the frame next poll would be.
* Get how manyth poll in the frame next poll would be?
*
* \return Poll number.
* returns: Poll number.
*/
unsigned next_poll_number();
uint64_t frame_subframes(uint64_t frame);
controls_t read_subframe(uint64_t frame, uint64_t subframe);
/**
* Get how many subframes there are in specified frame.
*
* parameter frame: The frame number.
* returns: Number of subframes (0 if outside movie).
*/
uint64_t frame_subframes(uint64_t frame) throw();
/**
* Read controls from specified subframe of specified frame.
*
* parameter frame: The frame number.
* parameter subframe: Subframe within frame (first is 0).
* returns: The controls for subframe. If subframe is too great, reads last present subframe. If frame is outside
* movie, reads all released.
*/
controls_t read_subframe(uint64_t frame, uint64_t subframe) throw();
private:
//TRUE if readonly mode is active.
bool readonly;
@ -268,59 +260,58 @@ private:
//Number of frames in movie.
uint64_t frames_in_movie;
//Cached subframes.
void clear_caches();
void clear_caches() throw();
uint64_t cached_frame;
uint64_t cached_subframe;
//Count present subframes in frame starting from first_subframe (returns 0 if out of movie).
uint32_t count_changes(uint64_t first_subframe);
uint32_t count_changes(uint64_t first_subframe) throw();
};
/**
* \brief Class encapsulating bridge logic between bsnes interface and movie code.
* Class encapsulating bridge logic between bsnes interface and movie code.
*/
class movie_logic
{
public:
/**
* \brief Create new bridge.
*
* \param m The movie to manipulate.
* Create new bridge.
*/
movie_logic() throw();
/**
* \brief Get the movie instance associated.
* Get the movie instance associated.
*
* returns: The movie instance.
*/
movie& get_movie() throw();
/**
* \brief Notify about new frame starting.
* Notify about new frame starting.
*
* \return Reset status for the new frame.
* returns: Reset status for the new frame.
*/
long new_frame_starting(bool dont_poll) throw(std::bad_alloc, std::runtime_error);
/**
* \brief Poll for input.
* Poll for input.
*
* \return Value for polled input.
* parameter port: The port number.
* parameter dev: The controller index.
* parameter id: Control id.
* returns: Value for polled input.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Error polling for input.
*/
short input_poll(bool port, unsigned dev, unsigned id) throw(std::bad_alloc, std::runtime_error);
/**
* \brief Called when movie code needs new controls snapshot.
* Called when movie code needs new controls snapshot.
*
* \param subframe True if this is for subframe update, false if for frame update.
* parameter subframe: True if this is for subframe update, false if for frame update.
*/
controls_t update_controls(bool subframe) throw(std::bad_alloc, std::runtime_error);
private:
movie mov;
};
/**
* \brief Global rerecord count.
*/
extern std::string global_rerecord_count;
#endif

View file

@ -10,144 +10,136 @@
/**
* \brief Parsed representation of movie file
*
* This structure gives parsed representationg of movie file, as result of decoding or for encoding.
*/
struct moviefile
{
/**
* \brief Construct empty movie
*
* This constructor construct movie structure with default settings.
*
* \throws std::bad_alloc Not enough memory.
* throws std::bad_alloc: Not enough memory.
*/
moviefile() throw(std::bad_alloc);
/**
* \brief Load a movie/savestate file
*
* This constructor loads a movie/savestate file and fills structure accordingly.
*
* \param filename The file to load.
* \throws std::bad_alloc Not enough memory.
* \throws std::runtime_error Can't load the movie file
* parameter filename: The file to load.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Can't load the movie file
*/
moviefile(const std::string& filename) throw(std::bad_alloc, std::runtime_error);
/**
* \brief Save a movie or savestate.
*
* Reads this movie structure and saves it into file.
*
* \param filename The file to save to.
* \param compression The compression level 0-9. 0 is uncompressed.
* \throws std::bad_alloc Not enough memory.
* \throws std::runtime_error Can't save the movie file.
* parameter filename: The file to save to.
* parameter compression: The compression level 0-9. 0 is uncompressed.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Can't save the movie file.
*/
void save(const std::string& filename, unsigned compression) throw(std::bad_alloc, std::runtime_error);
/**
* \brief What is the ROM type and region?
* What is the ROM type and region?
*/
gametype_t gametype;
/**
* \brief What's in port #1?
* What's in port #1?
*/
porttype_t port1;
/**
* \brief What's in port #2?
* What's in port #2?
*/
porttype_t port2;
/**
* \brief Emulator Core version string.
* Emulator Core version string.
*/
std::string coreversion;
/**
* \brief Name of the game
* Name of the game
*/
std::string gamename;
/**
* \brief Project ID (used to identify if two movies are from the same project).
* Project ID (used to identify if two movies are from the same project).
*/
std::string projectid;
/**
* \brief Rerecord count (only loaded).
* Rerecord count (only saved).
*/
std::string rerecords;
/**
* \brief SHA-256 of main ROM (empty string if none).
* SHA-256 of main ROM (empty string if none).
*/
std::string rom_sha256; //SHA-256 of main ROM.
/**
* \brief SHA-256 of main ROM XML (empty string if none).
* SHA-256 of main ROM XML (empty string if none).
*/
std::string romxml_sha256; //SHA-256 of main ROM XML.
/**
* \brief SHA-256 of slot A ROM (empty string if none).
* SHA-256 of slot A ROM (empty string if none).
*/
std::string slota_sha256; //SHA-256 of SLOT A ROM.
/**
* \brief SHA-256 of slot A XML (empty string if none).
* SHA-256 of slot A XML (empty string if none).
*/
std::string slotaxml_sha256; //SHA-256 of SLOT A XML.
/**
* \brief SHA-256 of slot B ROM (empty string if none).
* SHA-256 of slot B ROM (empty string if none).
*/
std::string slotb_sha256; //SHA-256 of SLOT B ROM.
/**
* \brief SHA-256 of slot B XML (empty string if none).
* SHA-256 of slot B XML (empty string if none).
*/
std::string slotbxml_sha256; //SHA-256 of SLOT B XML.
/**
* \brief Authors of the run, first in each pair is full name, second is nickname.
* Authors of the run, first in each pair is full name, second is nickname.
*/
std::vector<std::pair<std::string, std::string>> authors;
/**
* \brief Contents of SRAM on time of initial powerup.
* Contents of SRAM on time of initial powerup.
*/
std::map<std::string, std::vector<char>> movie_sram;
/**
* \brief True if savestate, false if movie.
* True if savestate, false if movie.
*/
bool is_savestate;
/**
* \brief Contents of SRAM on time of savestate (if is_savestate is true).
* Contents of SRAM on time of savestate (if is_savestate is true).
*/
std::map<std::string, std::vector<char>> sram;
/**
* \brief Core savestate (if is_savestate is true).
* Core savestate (if is_savestate is true).
*/
std::vector<char> savestate; //Savestate to load (if is_savestate is true).
/**
* \brief Host memory (if is_savestate is true).
* Host memory (if is_savestate is true).
*/
std::vector<char> host_memory;
/**
* \brief Screenshot (if is_savestate is true).
* Screenshot (if is_savestate is true).
*/
std::vector<char> screenshot;
/**
* \brief State of movie code (if is_savestate is true).
* State of movie code (if is_savestate is true).
*/
std::vector<char> movie_state;
/**
* \brief Input for each (sub)frame.
* Input for each (sub)frame.
*/
std::vector<controls_t> input; //Input for each frame.
/**
* \brief Get number of frames in movie.
* Get number of frames in movie.
*
* \return Number of frames.
* returns: Number of frames.
*/
uint64_t get_frame_count() throw();
/**
* \brief Get length of the movie
* Get length of the movie
*
* \return Length of the movie in nanoseconds.
* returns: Length of the movie in nanoseconds.
*/
uint64_t get_movie_length() throw();
};

View file

@ -1,637 +0,0 @@
#include "movie.hpp"
#include <iostream>
void truncate_past_complete_frame()
{
short in1, in2;
std::cerr << "Test #1: Truncate past complete frame" << std::endl;
movie m;
std::vector<uint8_t> state;
controls_t c;
m.readonly_mode(false);
m.next_frame();
c(4) = 0x1;
c(5) = 0x2;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x1) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (1)" << std::endl;
std::cerr << "Expected 1, got " << in1 << "." << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x2) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (1)" << std::endl;
return;
}
m.next_frame();
c(4) = 0x3;
c(5) = 0x4;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x3) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (2)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x4) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (2)" << std::endl;
return;
}
c(4) = 0x5;
c(5) = 0x6;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x5) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (3)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x6) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (3)" << std::endl;
return;
}
m.next_frame();
state = m.save_state();
c(4) = 0x7;
c(5) = 0x8;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x7) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (4)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x8) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (4)" << std::endl;
return;
}
m.next_frame();
c(4) = 0x9;
c(5) = 0xa;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x9) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (5)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0xa) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (5)" << std::endl;
return;
}
m.restore_state(state, false);
std::vector<controls_t> v = m.save();
if(v.size() != 3) {
std::cerr << "FAIL: Unexpected size for movie" << std::endl;
return;
}
if(v[0](0) != 1 || v[0](4) != 0x1 || v[0](5) != 0x2) {
std::cerr << "FAIL: Wrong input for first frame" << std::endl;
return;
}
if(v[1](0) != 1 || v[1](4) != 0x3 || v[1](5) != 0x4) {
std::cerr << "FAIL: Wrong input for second frame first subframe" << std::endl;
return;
}
if(v[2](0) != 0 || v[2](4) != 0x5 || v[2](5) != 0x6) {
std::cerr << "FAIL: Wrong input for second frame second subframe" << std::endl;
return;
}
std::cerr << "PASS!" << std::endl;
}
void truncate_past_incomplete_frame()
{
short in1, in2;
std::cerr << "Test #2: Truncate past incomplete frame" << std::endl;
movie m;
std::vector<uint8_t> state;
controls_t c;
m.readonly_mode(false);
m.next_frame();
c(4) = 0x1;
c(5) = 0x2;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x1) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (1)" << std::endl;
std::cerr << "Expected 1, got " << in1 << "." << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x2) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (1)" << std::endl;
return;
}
m.next_frame();
c(4) = 0x3;
c(5) = 0x4;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x3) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (2)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x4) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (2)" << std::endl;
return;
}
c(4) = 0x5;
c(5) = 0x6;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x5) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (3)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x6) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (3)" << std::endl;
return;
}
c(5) = 0x7;
c(6) = 0x8;
m.set_controls(c);
in1 = m.next_input(5);
if(in1 != 0x7) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (4)" << std::endl;
return;
}
in2 = m.next_input(6);
if(in2 != 0x8) {
std::cerr << "FAIL: Unexpected return for m.next_input(6) (4)" << std::endl;
return;
}
state = m.save_state();
//Now we have 2 subframes on 4, 3 on 5 and 1 on 6. Add 1 subframe for 4, 5 and 7.
c(4) = 0x9;
c(5) = 0xa;
c(7) = 0xb;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x9) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (5)" << std::endl;
return;
}
in1 = m.next_input(5);
if(in1 != 0xa) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (5)" << std::endl;
return;
}
in2 = m.next_input(7);
if(in2 != 0xb) {
std::cerr << "FAIL: Unexpected return for m.next_input(7) (5)" << std::endl;
return;
}
m.next_frame();
c(4) = 0xc;
c(5) = 0xd;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0xc) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (6)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0xd) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (6)" << std::endl;
return;
}
m.next_frame();
c(4) = 0xe;
c(5) = 0xf;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0xe) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (7)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0xf) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (7)" << std::endl;
return;
}
m.restore_state(state, false);
std::vector<controls_t> v = m.save();
if(v.size() != 4) {
std::cerr << "FAIL: Unexpected size for movie" << std::endl;
return;
}
if(v[0](0) != 1 || v[0](4) != 0x1 || v[0](5) != 0x2) {
std::cerr << "FAIL: Wrong input for first frame" << std::endl;
return;
}
if(v[1](0) != 1 || v[1](4) != 0x3 || v[1](5) != 0x4 || v[1](6) != 0x8 || v[1](7) != 0xb) {
std::cerr << "FAIL: Wrong input for second frame first subframe" << std::endl;
return;
}
if(v[2](0) != 0 || v[2](4) != 0x5 || v[2](5) != 0x6 || v[2](6) != 0x8 || v[2](7) != 0xb) {
std::cerr << "FAIL: Wrong input for second frame second subframe" << std::endl;
std::cerr << v[2](0) << " " << v[2](4) << " " << v[2](5) << " " << v[2](6) << " " << v[2](7) << std::endl;
return;
}
if(v[3](0) != 0 || v[3](4) != 0x5 || v[3](5) != 0x7 || v[3](6) != 0x8 || v[3](7) != 0xb) {
std::cerr << "FAIL: Wrong input for second frame third subframe" << std::endl;
return;
}
std::cerr << "PASS!" << std::endl;
}
void truncate_current_complete_frame()
{
short in1, in2;
std::cerr << "Test #3: Truncate current complete frame" << std::endl;
movie m;
std::vector<uint8_t> state;
controls_t c;
m.readonly_mode(false);
m.next_frame();
c(4) = 0x1;
c(5) = 0x2;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x1) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (1)" << std::endl;
std::cerr << "Expected 1, got " << in1 << "." << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x2) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (1)" << std::endl;
return;
}
m.next_frame();
c(4) = 0x3;
c(5) = 0x4;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x3) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (2)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x4) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (2)" << std::endl;
return;
}
c(4) = 0x5;
c(5) = 0x6;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x5) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (3)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x6) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (3)" << std::endl;
return;
}
m.next_frame();
state = m.save_state();
c(4) = 0x7;
c(5) = 0x8;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x7) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (4)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x8) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (4)" << std::endl;
return;
}
m.restore_state(state, false);
std::vector<controls_t> v = m.save();
if(v.size() != 3) {
std::cerr << "FAIL: Unexpected size for movie" << std::endl;
return;
}
if(v[0](0) != 1 || v[0](4) != 0x1 || v[0](5) != 0x2) {
std::cerr << "FAIL: Wrong input for first frame" << std::endl;
return;
}
if(v[1](0) != 1 || v[1](4) != 0x3 || v[1](5) != 0x4) {
std::cerr << "FAIL: Wrong input for second frame first subframe" << std::endl;
return;
}
if(v[2](0) != 0 || v[2](4) != 0x5 || v[2](5) != 0x6) {
std::cerr << "FAIL: Wrong input for second frame second subframe" << std::endl;
return;
}
std::cerr << "PASS!" << std::endl;
}
void truncate_current_incomplete_frame()
{
short in1, in2;
std::cerr << "Test #4: Truncate current incomplete frame" << std::endl;
movie m;
std::vector<uint8_t> state;
controls_t c;
m.readonly_mode(false);
m.next_frame();
c(4) = 0x1;
c(5) = 0x2;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x1) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (1)" << std::endl;
std::cerr << "Expected 1, got " << in1 << "." << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x2) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (1)" << std::endl;
return;
}
m.next_frame();
c(4) = 0x3;
c(5) = 0x4;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x3) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (2)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x4) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (2)" << std::endl;
return;
}
c(4) = 0x5;
c(5) = 0x6;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x5) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (3)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x6) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (3)" << std::endl;
return;
}
c(5) = 0x7;
c(6) = 0x8;
m.set_controls(c);
in1 = m.next_input(5);
if(in1 != 0x7) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (4)" << std::endl;
return;
}
in2 = m.next_input(6);
if(in2 != 0x8) {
std::cerr << "FAIL: Unexpected return for m.next_input(6) (4)" << std::endl;
return;
}
state = m.save_state();
//Now we have 2 subframes on 4, 3 on 5 and 1 on 6. Add 1 subframe for 4, 5 and 7.
c(4) = 0x9;
c(5) = 0xa;
c(7) = 0xb;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x9) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (5)" << std::endl;
return;
}
in1 = m.next_input(5);
if(in1 != 0xa) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (5)" << std::endl;
return;
}
in2 = m.next_input(7);
if(in2 != 0xb) {
std::cerr << "FAIL: Unexpected return for m.next_input(7) (5)" << std::endl;
return;
}
m.restore_state(state, false);
std::vector<controls_t> v = m.save();
if(v.size() != 4) {
std::cerr << "FAIL: Unexpected size for movie" << std::endl;
return;
}
if(v[0](0) != 1 || v[0](4) != 0x1 || v[0](5) != 0x2) {
std::cerr << "FAIL: Wrong input for first frame" << std::endl;
return;
}
if(v[1](0) != 1 || v[1](4) != 0x3 || v[1](5) != 0x4 || v[1](6) != 0x8 || v[1](7) != 0xb) {
std::cerr << "FAIL: Wrong input for second frame first subframe" << std::endl;
return;
}
if(v[2](0) != 0 || v[2](4) != 0x5 || v[2](5) != 0x6 || v[2](6) != 0x8 || v[2](7) != 0xb) {
std::cerr << "FAIL: Wrong input for second frame second subframe" << std::endl;
std::cerr << v[2](0) << " " << v[2](4) << " " << v[2](5) << " " << v[2](6) << " " << v[2](7) << std::endl;
return;
}
if(v[3](0) != 0 || v[3](4) != 0x5 || v[3](5) != 0x7 || v[3](6) != 0x8 || v[3](7) != 0xb) {
std::cerr << "FAIL: Wrong input for second frame third subframe" << std::endl;
return;
}
std::cerr << "PASS!" << std::endl;
}
void truncate_future_complete_frame()
{
short in1, in2;
std::cerr << "Test #5: Truncate future complete frame" << std::endl;
movie m;
std::vector<uint8_t> state;
controls_t c;
m.readonly_mode(false);
m.next_frame();
c(4) = 0x1;
c(5) = 0x2;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x1) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (1)" << std::endl;
std::cerr << "Expected 1, got " << in1 << "." << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x2) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (1)" << std::endl;
return;
}
m.next_frame();
c(4) = 0x3;
c(5) = 0x4;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x3) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (2)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x4) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (2)" << std::endl;
return;
}
c(4) = 0x5;
c(5) = 0x6;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x5) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (3)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x6) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (3)" << std::endl;
return;
}
m.next_frame();
m.readonly_mode(true);
m.next_frame();
m.next_frame();
m.next_frame();
m.readonly_mode(false);
std::vector<controls_t> v = m.save();
if(v.size() != 6) {
std::cerr << "FAIL: Unexpected size for movie" << std::endl;
return;
}
if(v[0](0) != 1 || v[0](4) != 0x1 || v[0](5) != 0x2) {
std::cerr << "FAIL: Wrong input for first frame" << std::endl;
return;
}
if(v[1](0) != 1 || v[1](4) != 0x3 || v[1](5) != 0x4) {
std::cerr << "FAIL: Wrong input for second frame first subframe" << std::endl;
return;
}
if(v[2](0) != 0 || v[2](4) != 0x5 || v[2](5) != 0x6) {
std::cerr << "FAIL: Wrong input for second frame second subframe" << std::endl;
return;
}
if(v[3](0) != 1 || v[3](4) != 0 || v[3](5) != 0) {
std::cerr << "FAIL: Wrong input for third frame" << std::endl;
return;
}
if(v[4](0) != 1 || v[4](4) != 0 || v[4](5) != 0) {
std::cerr << "FAIL: Wrong input for fourth frame" << std::endl;
return;
}
if(v[5](0) != 1 || v[5](4) != 0 || v[5](5) != 0) {
std::cerr << "FAIL: Wrong input for fifth frame" << std::endl;
return;
}
std::cerr << "PASS!" << std::endl;
}
void truncate_future_incomplete_frame()
{
short in1, in2;
std::cerr << "Test #6: Truncate future incomplete frame" << std::endl;
movie m;
std::vector<uint8_t> state;
controls_t c;
m.readonly_mode(false);
m.next_frame();
c(4) = 0x1;
c(5) = 0x2;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x1) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (1)" << std::endl;
std::cerr << "Expected 1, got " << in1 << "." << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x2) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (1)" << std::endl;
return;
}
m.next_frame();
c(4) = 0x3;
c(5) = 0x4;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x3) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (2)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x4) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (2)" << std::endl;
return;
}
c(4) = 0x5;
c(5) = 0x6;
m.set_controls(c);
in1 = m.next_input(4);
if(in1 != 0x5) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (3)" << std::endl;
return;
}
in2 = m.next_input(5);
if(in2 != 0x6) {
std::cerr << "FAIL: Unexpected return for m.next_input(5) (3)" << std::endl;
return;
}
m.next_frame();
m.readonly_mode(true);
m.next_frame();
m.next_frame();
m.next_frame();
in1 = m.next_input(4);
if(in1 != 0x0) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (4)" << std::endl;
return;
}
in1 = m.next_input(4);
if(in1 != 0x0) {
std::cerr << "FAIL: Unexpected return for m.next_input(4) (5)" << std::endl;
return;
}
m.readonly_mode(false);
std::vector<controls_t> v = m.save();
if(v.size() != 7) {
std::cerr << "FAIL: Unexpected size for movie (got " << v.size() << ")" << std::endl;
return;
}
if(v[0](0) != 1 || v[0](4) != 0x1 || v[0](5) != 0x2) {
std::cerr << "FAIL: Wrong input for first frame" << std::endl;
return;
}
if(v[1](0) != 1 || v[1](4) != 0x3 || v[1](5) != 0x4) {
std::cerr << "FAIL: Wrong input for second frame first subframe" << std::endl;
return;
}
if(v[2](0) != 0 || v[2](4) != 0x5 || v[2](5) != 0x6) {
std::cerr << "FAIL: Wrong input for second frame second subframe" << std::endl;
return;
}
if(v[3](0) != 1 || v[3](4) != 0 || v[3](5) != 0) {
std::cerr << "FAIL: Wrong input for third frame" << std::endl;
return;
}
if(v[4](0) != 1 || v[4](4) != 0 || v[4](5) != 0) {
std::cerr << "FAIL: Wrong input for fourth frame" << std::endl;
return;
}
if(v[5](0) != 1 || v[5](4) != 0 || v[5](5) != 0) {
std::cerr << "FAIL: Wrong input for fifth frame" << std::endl;
return;
}
if(v[6](0) != 1 || v[6](4) != 0 || v[6](5) != 0) {
std::cerr << "FAIL: Wrong input for sixth frame" << std::endl;
return;
}
std::cerr << "PASS!" << std::endl;
}
int main()
{
truncate_past_complete_frame();
truncate_past_incomplete_frame();
truncate_current_complete_frame();
truncate_current_incomplete_frame();
truncate_future_complete_frame();
truncate_future_incomplete_frame();
return 0;
}

12
png.hpp
View file

@ -5,7 +5,17 @@
#include <cstdint>
#include <string>
/**
* Save a PNG.
*
* parameter file: Filename to save to.
* parameter data24: 3 elements per pixel (r,g, b) per pixel, left-to-right, top-to-bottom order.
* parameter width: Width of the image.
* parameter height: Height of the image.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Error saving PNG.
*/
void save_png_data(const std::string& file, uint8_t* data24, uint32_t width, uint32_t height) throw(std::bad_alloc,
std::runtime_error);
#endif
#endif

View file

@ -8,211 +8,246 @@
#include <stdexcept>
/**
* \brief Low color (32768 colors) screen from buffer.
* Low color (32768 colors) screen from buffer.
*/
struct lcscreen
{
/**
* \brief Create new screen from bsnes output data.
* Create new screen from bsnes output data.
*
* \param mem The output buffer from bsnes.
* \param hires True if in hires mode (512-wide lines instead of 256-wide).
* \param interlace True if in interlace mode.
* \param overscan True if overscan is enabled.
* \param region True if PAL, false if NTSC.
* parameter mem The output buffer from bsnes.
* parameter hires True if in hires mode (512-wide lines instead of 256-wide).
* parameter interlace True if in interlace mode.
* parameter overscan True if overscan is enabled.
* parameter region True if PAL, false if NTSC.
*/
lcscreen(const uint16_t* mem, bool hires, bool interlace, bool overscan, bool region) throw();
/**
* Create new memory-backed screen. The resulting screen can be written to.
*/
lcscreen() throw();
/**
* Create new screen with specified contents and size.
*
* parameter mem: Memory to use as frame data. 1 element per pixel. Left-to-Right, top-to-bottom order.
* parameter _width: Width of the screen to create.
* parameter _height: Height of the screen to create.
*/
lcscreen(const uint16_t* mem, uint32_t _width, uint32_t _height) throw();
/**
* Copy the screen.
*
* The assigned copy is always writable.
*
* parameter ls: The source screen.
* throws std::bad_alloc: Not enough memory.
*/
lcscreen(const lcscreen& ls) throw(std::bad_alloc);
/**
* Assign the screen.
*
* parameter ls: The source screen.
* returns: Reference to target screen.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: The target screen is not writable.
*/
lcscreen& operator=(const lcscreen& ls) throw(std::bad_alloc, std::runtime_error);
/**
* Load contents of screen.
*
* parameter data: The data to load.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: The target screen is not writable.
*/
void load(const std::vector<char>& data) throw(std::bad_alloc, std::runtime_error);
/**
* Save contents of screen.
*
* parameter data: The vector to write the data to (in format compatible with load()).
* throws std::bad_alloc: Not enough memory.
*/
void save(std::vector<char>& data) throw(std::bad_alloc);
/**
* Save contents of screen as a PNG.
*
* parameter file: The filename to save to.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Can't save the PNG.
*/
void save_png(const std::string& file) throw(std::bad_alloc, std::runtime_error);
/**
* \brief Destructor.
* Destructor.
*/
~lcscreen();
/**
* \brief True if memory is allocated by new[] and should be freed by the destructor., false otherwise.
* True if memory is allocated by new[] and should be freed by the destructor., false otherwise. Also signals
* writablity.
*/
bool user_memory;
/**
* \brief Memory, 1 element per pixel in left-to-right, top-to-bottom order, 15 low bits of each element used.
* Memory, 1 element per pixel in left-to-right, top-to-bottom order, 15 low bits of each element used.
*/
const uint16_t* memory;
/**
* \brief Number of elements (not bytes) between two successive scanlines.
* Number of elements (not bytes) between two successive scanlines.
*/
uint32_t pitch;
/**
* \brief Width of image.
* Width of image.
*/
uint32_t width;
/**
* \brief Height of image.
* Height of image.
*/
uint32_t height;
/**
* \brief Image allocated size (only valid for user_memory).
* Image allocated size (only valid for user_memory=true).
*/
size_t allocated;
};
/**
* \brief Truecolor modifiable screen.
* Truecolor modifiable screen.
*/
struct screen
{
/**
* \brief Create new screen
*
* Creates screen. The screen dimensions are initially 0x0.
*/
screen() throw();
/**
* \brief Destructor.
* Destructor.
*/
~screen() throw();
/**
* \brief Set the screen to use specified backing memory.
*
* Sets the backing memory for screen. The specified memory is not freed if screen is reallocated or destroyed.
*
* \param _memory The memory buffer.
* \param _width Width of screen.
* \param _height Height of screen.
* \param _originx X coordinate for origin.
* \param _originy Y coordinate for origin.
* \param _pitch Distance in bytes between successive scanlines.
* parameter _memory: The memory buffer.
* parameter _width: Width of screen.
* parameter _height: Height of screen.
* parameter _originx: X coordinate for origin.
* parameter _originy: Y coordinate for origin.
* parameter _pitch: Distance in bytes between successive scanlines.
*/
void set(uint32_t* _memory, uint32_t _width, uint32_t _height, uint32_t _originx, uint32_t _originy,
uint32_t _pitch) throw();
/**
* \brief Set new size for screen, reallocating backing memory.
*
* Sets the size of the screen. The memory is freed if screen is reallocated or destroyed.
*
* \param _width Width of screen.
* \param _height Height of screen.
* \param _originx X coordinate for origin.
* \param _originy Y coordinate for origin.
* \param upside_down If true, image is upside down in memory.
* \throws std::bad_alloc Not enough memory.
* parameter _width: Width of screen.
* parameter _height: Height of screen.
* parameter _originx: X coordinate for origin.
* parameter _originy: Y coordinate for origin.
* parameter upside_down: If true, image is upside down in memory.
* throws std::bad_alloc: Not enough memory.
*/
void reallocate(uint32_t _width, uint32_t _height, uint32_t _originx, uint32_t _originy,
bool upside_down = false) throw(std::bad_alloc);
/**
* \brief Paint low-color screen into screen.
*
* Paints low-color screen into screen. The upper-left of image will be at origin. Scales the image by given factors.
* If the image does not fit with specified scale factors, it is clipped.
*
* \param scr The screen to paint.
* \param hscale Horizontal scale factor.
* \param vscale Vertical scale factor.
* parameter scr The screen to paint.
* parameter hscale Horizontal scale factor.
* parameter vscale Vertical scale factor.
*/
void copy_from(lcscreen& scr, uint32_t hscale, uint32_t vscale) throw();
/**
* \brief Get pointer into specified row.
* Get pointer into specified row.
*
* \param row Number of row (must be less than height).
* parameter row: Number of row (must be less than height).
*/
uint32_t* rowptr(uint32_t row) throw();
/**
* \brief Backing memory for this screen.
* Backing memory for this screen.
*/
uint32_t* memory;
/**
* \brief True if memory is given by user and must not be freed.
* True if memory is given by user and must not be freed.
*/
bool user_memory;
/**
* \brief Width of screen.
* Width of screen.
*/
uint32_t width;
/**
* \brief Height of screen.
* Height of screen.
*/
uint32_t height;
/**
* \brief Distance between lines in bytes.
* Distance between lines in bytes.
*/
size_t pitch;
/**
* \brief True if image is upside down in memory.
* True if image is upside down in memory.
*/
bool flipped;
/**
* \brief X-coordinate of origin.
* X-coordinate of origin.
*/
uint32_t originx;
/**
* \brief Y-coordinate of origin.
* Y-coordinate of origin.
*/
uint32_t originy;
/**
* \brief Palette.
* Palette.
*/
uint32_t palette[32768];
/**
* \brief Set the palette shifts.
*
* Sets the palette shifts, converting the existing image.
*
* \param rshift Shift for red component.
* \param gshift Shift for green component.
* \param bshift Shift for blue component.
* parameter rshift Shift for red component.
* parameter gshift Shift for green component.
* parameter bshift Shift for blue component.
*/
void set_palette(uint32_t rshift, uint32_t gshift, uint32_t bshift) throw();
/**
* \brief Return a color value.
*
* Returns color value with specified (r,g,b) values (scale 0-255).
*
* \param r Red component.
* \param g Green component.
* \param b Blue component.
* \return color element value.
* parameter r: Red component.
* parameter g: Green component.
* parameter b: Blue component.
* returns: color element value.
*/
uint32_t make_color(uint8_t r, uint8_t g, uint8_t b) throw();
/**
* \brief Current red component shift.
* Current red component shift.
*/
uint32_t active_rshift;
/**
* \brief Current green component shift.
* Current green component shift.
*/
uint32_t active_gshift;
/**
* \brief Current blue component shift.
* Current blue component shift.
*/
uint32_t active_bshift;
private:
@ -221,57 +256,50 @@ private:
};
/**
* \brief Base class for objects to render.
* Base class for objects to render.
*/
struct render_object
{
/**
* \brief Destructor.
* Destructor.
*/
virtual ~render_object() throw();
/**
* \brief Draw the object.
* Draw the object.
*
* \param scr The screen to draw it on.
* parameter scr: The screen to draw it on.
*/
virtual void operator()(struct screen& scr) throw() = 0;
};
/**
* \brief Queue of render operations.
* Queue of render operations.
*/
struct render_queue
{
/**
* \brief Add object to render queue.
*
* Adds new object to render queue. The object must be allocated by new.
*
* \param obj The object to add
* \throws std::bad_alloc Not enough memory.
* parameter obj: The object to add
* throws std::bad_alloc: Not enough memory.
*/
void add(struct render_object& obj) throw(std::bad_alloc);
/**
* \brief Apply all objects in order.
*
* Applies all objects in the queue in order, freeing them in progress.
*
* \param scr The screen to apply queue to.
* parameter scr: The screen to apply queue to.
*/
void run(struct screen& scr) throw();
/**
* \brief Clear the queue.
*
* Frees all objects in the queue.
*
* Frees all objects in the queue without applying them.
*/
void clear() throw();
/**
* \brief Destructor.
* Destructor.
*/
~render_queue() throw();
private:
@ -279,30 +307,30 @@ private:
};
/**
* \brief Read font data for glyph.
* Read font data for glyph.
*
* \param codepoint Code point of glyph.
* \param x X position to render into.
* \param y Y position to render into.
* \param orig_x X position at start of row.
* \param next_x X position for next glyph is written here.
* \param next_y Y position for next glyph is written here.
* \return Two components: First is width of character, second is its offset in font data (0 if blank glyph).
* parameter codepoint: Code point of glyph.
* parameter x: X position to render into.
* parameter y: Y position to render into.
* parameter orig_x: X position at start of row.
* parameter next_x: X position for next glyph is written here.
* parameter next_y: Y position for next glyph is written here.
* returns: Two components: First is width of character, second is its offset in font data (0 if blank glyph).
*/
std::pair<uint32_t, size_t> find_glyph(uint32_t codepoint, int32_t x, int32_t y, int32_t orig_x,
int32_t& next_x, int32_t& next_y) throw();
/**
* \brief Render text into screen.
* Render text into screen.
*
* \param _x The x position to render to (relative to origin).
* \param _y The y position to render to (relative to origin).
* \param _text The text to render.
* \param _fg Foreground color.
* \param _fgalpha Foreground alpha (0-256).
* \param _bg Background color.
* \param _bgalpha Background alpha (0-256).
* parameter _x: The x position to render to (relative to origin).
* parameter _y: The y position to render to (relative to origin).
* parameter _text: The text to render (UTF-8).
* parameter _fg: Foreground color.
* parameter _fgalpha: Foreground alpha (0-256).
* parameter _bg: Background color.
* parameter _bgalpha: Background alpha (0-256).
* throws std::bad_alloc: Not enough memory.
*/
void render_text(struct screen& scr, int32_t _x, int32_t _y, const std::string& _text, uint32_t _fg = 0xFFFFFFFFU,
uint16_t _fgalpha = 255, uint32_t _bg = 0, uint16_t _bgalpha = 0) throw(std::bad_alloc);

10
rom.cpp
View file

@ -655,16 +655,6 @@ std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector
return ret;
}
void emulate_frame() throw()
{
SNES::system.run();
}
void reset_snes() throw()
{
SNES::system.reset();
}
std::vector<char> save_core_state() throw(std::bad_alloc)
{
SNES::system.runtosave();

335
rom.hpp
View file

@ -9,259 +9,315 @@
#include "misc.hpp"
/**
* \brief Region of ROM.
* Region of ROM.
*/
enum rom_region
{
/**
* \brief Autodetect region
* Autodetect region
*/
REGION_AUTO = 0,
/**
* \brief (force) PAL region
* (force) PAL region
*/
REGION_PAL,
/**
* \brief (force) NTSC region
* (force) NTSC region
*/
REGION_NTSC
};
/**
* \brief Major type of ROM
* Major type of ROM
*/
enum rom_type
{
/**
* \brief Ordinary SNES ROM
* Ordinary SNES ROM
*/
ROMTYPE_SNES, //ROM is Ordinary SNES ROM.
ROMTYPE_SNES,
/**
* \brief BS-X Slotted ROM.
* BS-X Slotted ROM.
*/
ROMTYPE_BSXSLOTTED,
/**
* \brief BS-X (non-slotted) ROM.
* BS-X (non-slotted) ROM.
*/
ROMTYPE_BSX,
/**
* \brief Sufami Turbo ROM.
* Sufami Turbo ROM.
*/
ROMTYPE_SUFAMITURBO,
/**
* \brief Super Game Boy ROM.
* Super Game Boy ROM.
*/
ROMTYPE_SGB,
/**
* \brief No ROM.
* No ROM.
*/
ROMTYPE_NONE
};
/**
* \brief Type of ROM and region
*
* This enumeration enumerates possible ROM types and regions for those.
*/
enum gametype_t
{
/**
* \brief NTSC-region SNES game
* NTSC-region SNES game
*/
GT_SNES_NTSC = 0,
/**
* \brief PAL-region SNES game
* PAL-region SNES game
*/
GT_SNES_PAL = 1,
/**
* \brief NTSC-region BSX slotted game
* NTSC-region BSX slotted game
*/
GT_BSX_SLOTTED = 2,
/**
* \brief NTSC-region BSX (non-slotted) game
* NTSC-region BSX (non-slotted) game
*/
GT_BSX = 3,
/**
* \brief NTSC-region sufami turbo game
* NTSC-region sufami turbo game
*/
GT_SUFAMITURBO = 4,
/**
* \brief NTSC-region Super Game Boy game
* NTSC-region Super Game Boy game
*/
GT_SGB_NTSC = 5,
/**
* \brief PAL-region Super Game Boy game
* PAL-region Super Game Boy game
*/
GT_SGB_PAL = 6,
/**
* \brief Invalid game type
* Invalid game type
*/
GT_INVALID = 7
};
/**
* \brief Translations between diffrent representations of type.
* Translations between diffrent representations of type.
*/
class gtype
{
public:
/**
* Translate from major ROM type and region to string representation of the type.
*
* parameter rtype: The major ROM type.
* parameter region: Region.
* returns: String representation of combined type/region.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Invalid type.
*/
static std::string tostring(rom_type rtype, rom_region region) throw(std::bad_alloc, std::runtime_error);
/**
* Translate major/region combination to string representation.
*
* This function produces the same IDs as the other tostring(), except that it can't produce arbitrary-region ones.
*
* parameter gametype: Type of the game.
* returns: String representation of the type.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Invalid type.
*/
static std::string tostring(gametype_t gametype) throw(std::bad_alloc, std::runtime_error);
/**
* Combine major/region into game type.
*
* For arbitrary-region types, this gives NTSC types.
*
* parameter rtype: Major type.
* parameter region: The region.
* returns: The combined game type.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Invalid type.
*/
static gametype_t togametype(rom_type rtype, rom_region region) throw(std::bad_alloc, std::runtime_error);
/**
* Parse string representation to game type.
*
* parameter gametype: The game type to parse.
* returns: The parsed game type.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Invalid type.
*/
static gametype_t togametype(const std::string& gametype) throw(std::bad_alloc, std::runtime_error);
/**
* Parse string representation into major type.
*
* parameter gametype: The game type to parse.
* returns: The major type.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Invalid type.
*/
static rom_type toromtype(const std::string& gametype) throw(std::bad_alloc, std::runtime_error);
/**
* Extract major type out of game type.
*
* parameter gametype: the game type to parse.
* returns: The major type.
*/
static rom_type toromtype(gametype_t gametype) throw();
/**
* Extract region out of game type.
*
* parameter gametype: the game type to parse.
* returns: The region.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Invalid type.
*/
static rom_region toromregion(const std::string& gametype) throw(std::bad_alloc, std::runtime_error);
/**
* Extract region out of game type.
*
* parameter gametype: the game type to parse.
* returns: The region.
*/
static rom_region toromregion(gametype_t gametype) throw();
};
/**
* \brief Filenames associated with ROM.
*
* This structure gives all files associated with given ROM image.
*/
struct rom_files
{
/**
* \brief Construct defaults
* Construct defaults
*/
rom_files() throw();
/**
* \brief Read files from command line arguments.
* Reads the filenames out of command line arguments given.
*
* Reads the filenames out of command line arguments given. Also supports bundle files.
*
* \param cmdline The commmand line
* \throws std::bad_alloc Not enough memory
* \throws std::runtime_error Failed to load ROM filenames.
* parameter cmdline: The commmand line
* parameter win: Window system handle.
* throws std::bad_alloc: Not enough memory
* throws std::runtime_error: Failed to load ROM filenames.
*/
rom_files(const std::vector<std::string>& cmdline, window* win) throw(std::bad_alloc, std::runtime_error);
/**
* \brief Resolve relative references.
* Resolve relative references.
*
* throws std::bad_alloc: Not enough memory
* throws std::runtime_error: Bad path.
*/
void resolve_relative() throw(std::bad_alloc, std::runtime_error);
/**
* \brief Base ROM image
*
* The file to look other ROM files relative to. May be blank.
*/
std::string base_file;
/**
* \brief Major ROM type.
* Major ROM type.
*/
enum rom_type rtype;
/**
* \brief Game region
* Game region (the region ROM is to be loaded as)
*/
enum rom_region region;
/**
* \brief Relative filename of main ROM file.
* Relative filename of main ROM file.
*/
std::string rom;
/**
* \brief Relative filename of main ROM XML file.
* Relative filename of main ROM XML file.
*/
std::string rom_xml;
/**
* \brief Relative filename of slot A ROM file (non-SNES only).
* Relative filename of slot A ROM file (non-SNES only).
*/
std::string slota;
/**
* \brief Relative filename of slot A XML file (non-SNES only).
* Relative filename of slot A XML file (non-SNES only).
*/
std::string slota_xml;
/**
* \brief Relative filename of slot B ROM file (Sufami Turbo only).
* Relative filename of slot B ROM file (Sufami Turbo only).
*/
std::string slotb;
/**
* \brief Relative filename of slot B XML file (Sufami Turbo only).
* Relative filename of slot B XML file (Sufami Turbo only).
*/
std::string slotb_xml;
};
/**
* \brief Loaded data
*
* Some loaded data or indication of no data.
*/
struct loaded_slot
{
/**
* \brief Construct empty slot.
* \throws std::bad_alloc Not enough memory.
* Construct empty slot.
*
* throws std::bad_alloc: Not enough memory.
*/
loaded_slot() throw(std::bad_alloc);
/**
* \brief Read a slot
*
* This constructor construct slot by reading data from file. If filename is "", constructs an empty slot.
*
* \param filename The filename to read. If "", empty slot is constructed.
* \param base Base filename to interpret the filename against. If "", no base filename is used.
* \param xml_flag If set, always keep trailing NUL.
* \throws std::bad_alloc Not enough memory.
* \throws std::runtime_error Can't load the data.
* parameter filename: The filename to read. If "", empty slot is constructed.
* parameter base: Base filename to interpret the filename against. If "", no base filename is used.
* parameter xml_flag: If set, always keep trailing NUL.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Can't load the data.
*/
loaded_slot(const std::string& filename, const std::string& base, bool xml_flag = false) throw(std::bad_alloc,
std::runtime_error);
/**
* \brief IPS-patch a slot
*
* This method patches this slot using specified IPS patch.
*
* \param patch The patch to apply
* \param offset The amount to add to the offsets in the IPS file. Parts with offsets below zero are not patched.
* \throws std::bad_alloc Not enough memory.
* \throws std::runtime_error Bad IPS patch.
* parameter patch: The patch to apply
* parameter offset: The amount to add to the offsets in the IPS file. Parts with offsets below zero are not patched.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Bad IPS patch.
*/
void patch(const std::vector<char>& patch, int32_t offset) throw(std::bad_alloc, std::runtime_error);
/**
* \brief Is this slot XML slot?
* Is this slot XML slot?
*/
bool xml;
/**
* \brief True if this slot has valid data
*
* If this slot is blank, this is set to false, data is undefined and sha256 is "". Otherwise this is set to true,
* data to apporiate data, and sha256 to hash of data.
*/
bool valid;
/**
* \brief The actual data for this slot.
* The actual data for this slot.
*/
std::vector<char> data;
/**
* \brief SHA-256 for the data in this slot.
*
* SHA-256 for the data in this slot if data is valid. If no valid data, this field is "".
*/
std::string sha256;
/**
* \brief Get pointer to loaded data
* \return Pointer to loaded data, or NULL if slot is blank.
* Get pointer to loaded data
*
* returns: Pointer to loaded data, or NULL if slot is blank.
*/
operator const char*() const throw()
{
return valid ? reinterpret_cast<const char*>(&data[0]) : NULL;
}
/**
* \brief Get pointer to loaded data
* \return Pointer to loaded data, or NULL if slot is blank.
* Get pointer to loaded data
*
* returns: Pointer to loaded data, or NULL if slot is blank.
*/
operator const uint8_t*() const throw()
{
return valid ? reinterpret_cast<const uint8_t*>(&data[0]) : NULL;
}
/**
* \brief Get size of slot
* \return The number of bytes in slot, or 0 if slot is blank.
* Get size of slot
*
* returns: The number of bytes in slot, or 0 if slot is blank.
*/
operator unsigned() const throw()
{
@ -270,174 +326,177 @@ struct loaded_slot
};
/**
* \brief ROM loaded into memory.
* ROM loaded into memory.
*/
struct loaded_rom
{
/**
* \brief Create blank ROM
* Create blank ROM
*/
loaded_rom() throw();
/**
* \brief Load specified ROM files into memory.
*
* Takes in collection of ROM filenames and loads them into memory.
*
* \throws std::bad_alloc Not enough memory.
* \throws std::runtime_error Loading ROM files failed.
* parameter files: The files to load
* parameter win: Window system handle.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Loading ROM files failed.
*/
loaded_rom(const rom_files& files, window* win) throw(std::bad_alloc, std::runtime_error);
/**
* \brief ROM type
* ROM type
*/
enum rom_type rtype;
/**
* \brief ROM region
* ROM region (this is the currently active region).
*/
enum rom_region region;
/**
* \brief ROM original region
* ROM original region (this is the region ROM is loaded as).
*/
enum rom_region orig_region;
/**
* \brief Loaded main ROM
* Loaded main ROM
*/
loaded_slot rom;
/**
* \brief Loaded main ROM XML
* Loaded main ROM XML
*/
loaded_slot rom_xml;
/**
* \brief Loaded slot A ROM
* Loaded slot A ROM (.bs, .st or .dmg)
*/
loaded_slot slota;
/**
* \brief Loaded slot A XML
* Loaded slot A XML (.bs, .st or .dmg)
*/
loaded_slot slota_xml;
/**
* \brief Loaded slot B ROM
* Loaded slot B ROM (.st)
*/
loaded_slot slotb;
/**
* \brief Loaded slot B XML
* Loaded slot B XML (.st).
*/
loaded_slot slotb_xml;
/**
* \brief Patch the ROM.
* Patch the ROM.
*
* parameter cmdline: The command line.
* parameter win: Graphics system handle.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Failed to patch the ROM.
*/
void do_patch(const std::vector<std::string>& cmdline, window* win) throw(std::bad_alloc, std::runtime_error);
/**
* \brief Load this ROM into "SNES".
*
* Switches the active cartridge to this cartridge. The compatiblity between selected region and original region
* is checked. Region is updated after cartridge has been loaded.
*
* \throw std::bad_alloc Not enough memory
* \throw std::runtime_error Switching cartridges failed.
* throws std::bad_alloc: Not enough memory
* throws std::runtime_error: Switching cartridges failed.
*/
void load() throw(std::bad_alloc, std::runtime_error);
};
/**
* Recognize the slot this ROM goes to.
*
* parameter major: The major type.
* parameter romname: Name of the ROM type.
* returns: Even if this is main rom, odd if XML. 0/1 for main slot, 2/3 for slot A, 4/5 for slot B. -1 if not valid
* rom type.
* throws std::bad_alloc: Not enough memory
*/
int recognize_commandline_rom(enum rom_type major, const std::string& romname) throw(std::bad_alloc);
/**
* Recognize major type from flags.
*
* parameter flags: Flags telling what ROM parameters are present.
* returns: The recognzed major type.
* throws std::bad_alloc: Not enough memory
* throws std::runtime_error: Illegal flags.
*/
rom_type recognize_platform(unsigned long flags) throw(std::bad_alloc, std::runtime_error);
/**
* \brief Name a sub-ROM.
* Name a sub-ROM.
*
* parameter major: The major type.
* parameter romnumber: ROM number to name (as returned by recognize_commandline_rom).
* throws std::bad_alloc: Not enough memory
*/
std::string name_subrom(enum rom_type major, unsigned romnumber) throw(std::bad_alloc);
/**
* \brief Get major type and region of loaded ROM.
* \return rom type and region of current ROM.
* Get major type and region of loaded ROM.
*
* returns: Tuple (ROM type, ROM region) of currently loaded ROM.
*/
std::pair<enum rom_type, enum rom_region> get_current_rom_info() throw();
/**
* \brief Save all SRAMs
*
* Take current values of all SRAMs in current system and save their contents.
*
* \return Saved SRAM contents.
* \throws std::bad_alloc Out of memory.
* returns: Saved SRAM contents.
* throws std::bad_alloc: Out of memory.
*/
std::map<std::string, std::vector<char>> save_sram() throw(std::bad_alloc);
/**
* \brief Load all SRAMs
*
* Write contents of saved SRAMs into current system SRAMs.
*
* \param sram Saved SRAM contents.
* \throws std::bad_alloc Out of memory.
* parameter sram: Saved SRAM contents.
* parameter win: Window system handle.
* throws std::bad_alloc: Out of memory.
*/
void load_sram(std::map<std::string, std::vector<char>>& sram, window* win) throw(std::bad_alloc);
/**
* \brief Load SRAMs specified on command-line
*
* Read SRAMs from command-line and and load the files.
*
* \param cmdline Command line
* \return The loaded SRAM contents.
* \throws std::bad_alloc Out of memory.
* \throws std::runtime_error Failed to load.
* parameter cmdline: Command line
* returns: The loaded SRAM contents.
* throws std::bad_alloc: Out of memory.
* throws std::runtime_error: Failed to load.
*/
std::map<std::string, std::vector<char>> load_sram_commandline(const std::vector<std::string>& cmdline)
throw(std::bad_alloc, std::runtime_error);
/**
* \brief Emulate a frame
*/
void emulate_frame() throw();
/**
* \brief Reset the SNES
*/
void reset_snes() throw();
/**
* \brief Save core state into buffer
*
* Saves core state into buffer. WARNING: This takes emulated time.
*
* \return The saved state.
* \throws std::bad_alloc Not enough memory.
* returns: The saved state.
* throws std::bad_alloc: Not enough memory.
*/
std::vector<char> save_core_state() throw(std::bad_alloc);
/**
* \brief Restore core state from buffer.
*
* Loads core state from buffer.
*
* \param buf The buffer containing the state.
* \throws std::runtime_error Loading state failed.
* parameter buf: The buffer containing the state.
* throws std::runtime_error: Loading state failed.
*/
void load_core_state(const std::vector<char>& buf) throw(std::runtime_error);
/**
* \brief Read index file.
*
* Read index of ROMs and add ROMs found to content-searchable storage.
*
* \param filename The filename of index file.
* \throws std::bad_alloc Not enough memory.
* \throws std::runtime_error Loading index failed.
* parameter filename: The filename of index file.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Loading index failed.
*/
void load_index_file(const std::string& filename) throw(std::bad_alloc, std::runtime_error);
/**
* \brief Lookup absolute filename by hash.
*
* Search all indices, looking for file with specified SHA-256 (specifying hash of "" results "").
*
* \param hash The hash of file.
* \return Absolute filename.
* \throws std::bad_alloc Not enough memory.
* \throws std::runtime_error Not found.
* parameter hash: The hash of file.
* returns: Absolute filename.
* throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Not found.
*/
std::string lookup_file_by_sha256(const std::string& hash) throw(std::bad_alloc, std::runtime_error);

View file

@ -55,7 +55,7 @@ const struct rrdata::instance rrdata::instance::operator++(int) throw()
return i;
}
const struct rrdata::instance& rrdata::instance::operator++() throw()
struct rrdata::instance& rrdata::instance::operator++() throw()
{
unsigned carry = 1;
for(unsigned i = 31; i < 32; i--) {

View file

@ -5,30 +5,114 @@
#include <cstdint>
#include <stdexcept>
/**
* Set of load IDs
*/
class rrdata
{
public:
/**
* One load ID.
*/
struct instance
{
/**
* Create new random load ID.
*
* throws std::bad_alloc: Not enough memory
*/
instance() throw(std::bad_alloc);
/**
* Create new load id from bytes.
*
* parameter b: 32 byte array containing the new ID.
*/
instance(unsigned char* b) throw();
/**
* The load ID.
*/
unsigned char bytes[RRDATA_BYTES];
/**
* Is this ID before another one?
*
* parameter i: Another ID.
* returns: True if this ID is before another one, false otherwise.
*/
bool operator<(const struct instance& i) const throw();
/**
* Is this ID equal to another one?
*
* parameter i: Another ID.
* returns: True if this ID is equal to another one, false otherwise.
*/
bool operator==(const struct instance& i) const throw();
/**
* Increment this ID.
*
* returns: Copy of the ID before the increment.
*/
const struct instance operator++(int) throw();
const struct instance& operator++() throw();
/**
* Increment this ID.
*
* returns: Reference to this.
*/
struct instance& operator++() throw();
};
/**
* Read the saved set of load IDs for specified project and switch to that project.
*
* parameter project: The name of project.
* throws std::bad_alloc: Not enough memory
*/
static void read_base(const std::string& project) throw(std::bad_alloc);
/**
* Switch to no project, closing the load IDs.
*/
static void close() throw();
/**
* Add new specified instance to current project.
*
* Not allowed if there is no project open.
*
* parameter i: The load ID to add.
*/
static void add(const struct instance& i) throw(std::bad_alloc);
/**
* Generate new load ID and add it to the current proejct.
*
* Not allowed if there is no project open.
*
* throws std::bad_alloc: Not enough memory.
*/
static void add_internal() throw(std::bad_alloc);
/**
* Write compressed representation of current load ID set to stream.
*
* parameter strm: The stream to write to.
* throws std::bad_alloc: Not enough memory.
*/
static uint64_t write(std::ostream& strm) throw(std::bad_alloc);
/**
* Load compressed representation of load ID set from stream and union it with current set to form new current
* set.
*
* parameter strm: The stream to read from.
* throws std::bad_alloc: Not enough memory.
*/
static uint64_t read(std::istream& strm) throw(std::bad_alloc);
/**
* Internal pointer used by add_internal.
*/
static struct instance* internal;
};
/**
* Print load ID. Mainly useful for deubugging.
*
* parameter os: Stream to print to.
* parameter i: load ID to print.
*/
std::ostream& operator<<(std::ostream& os, const struct rrdata::instance& i);
#endif