quicknes: clean up unneeded files

This commit is contained in:
goyuken 2014-01-16 23:08:25 +00:00
parent ce36a94894
commit f76ceaad8b
51 changed files with 0 additions and 8383 deletions

View File

@ -1,77 +0,0 @@
// File_Extractor 1.0.0. http://www.slack.net/~ant/
#include "Binary_Extractor.h"
/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
// TODO: could close file once data has been read into memory
static File_Extractor* new_binary()
{
return BLARGG_NEW Binary_Extractor;
}
fex_type_t_ const fex_bin_type [1] = {{
"",
&new_binary,
"file",
NULL
}};
Binary_Extractor::Binary_Extractor() :
File_Extractor( fex_bin_type )
{ }
Binary_Extractor::~Binary_Extractor()
{
close();
}
blargg_err_t Binary_Extractor::open_path_v()
{
set_name( arc_path() );
return blargg_ok;
}
blargg_err_t Binary_Extractor::open_v()
{
set_name( arc_path() );
set_info( arc().remain(), 0, 0 );
return blargg_ok;
}
void Binary_Extractor::close_v()
{ }
blargg_err_t Binary_Extractor::next_v()
{
return blargg_ok;
}
blargg_err_t Binary_Extractor::rewind_v()
{
return open_path_v();
}
blargg_err_t Binary_Extractor::stat_v()
{
RETURN_ERR( open_arc_file() );
RETURN_ERR( arc().seek( 0 ) );
return open_v();
}
blargg_err_t Binary_Extractor::extract_v( void* p, int n )
{
return arc().read( p, n );
}

View File

@ -1,26 +0,0 @@
// Presents a single file as an "archive" of just that file.
// File_Extractor 1.0.0
#ifndef BINARY_EXTRACTOR_H
#define BINARY_EXTRACTOR_H
#include "File_Extractor.h"
class Binary_Extractor : public File_Extractor {
public:
Binary_Extractor();
virtual ~Binary_Extractor();
protected:
virtual blargg_err_t open_path_v();
virtual blargg_err_t open_v();
virtual void close_v();
virtual blargg_err_t next_v();
virtual blargg_err_t rewind_v();
virtual blargg_err_t stat_v();
virtual blargg_err_t extract_v( void*, int );
};
#endif

View File

@ -1,341 +0,0 @@
// File_Extractor 1.0.0. http://www.slack.net/~ant/
#include "File_Extractor.h"
/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
File_Extractor::fex_t( fex_type_t t ) :
type_( t )
{
own_file_ = NULL;
close_();
}
// Open
blargg_err_t File_Extractor::set_path( const char* path )
{
if ( !path )
path = "";
RETURN_ERR( path_.resize( strlen( path ) + 1 ) );
memcpy( path_.begin(), path, path_.size() );
return blargg_ok;
}
blargg_err_t File_Extractor::open( const char path [] )
{
close();
RETURN_ERR( set_path( path ) );
blargg_err_t err = open_path_v();
if ( err )
close();
else
opened_ = true;
return err;
}
blargg_err_t File_Extractor::open_path_v()
{
RETURN_ERR( open_arc_file() );
return open_v();
}
inline
static void make_unbuffered( Std_File_Reader* r )
{
r->make_unbuffered();
}
inline
static void make_unbuffered( void* )
{ }
blargg_err_t File_Extractor::open_arc_file( bool unbuffered )
{
if ( reader_ )
return blargg_ok;
FEX_FILE_READER* in = BLARGG_NEW FEX_FILE_READER;
CHECK_ALLOC( in );
blargg_err_t err = in->open( arc_path() );
if ( err )
{
delete in;
}
else
{
reader_ = in;
own_file();
if ( unbuffered )
make_unbuffered( in );
}
return err;
}
blargg_err_t File_Extractor::open( File_Reader* input, const char* path )
{
close();
RETURN_ERR( set_path( path ) );
RETURN_ERR( input->seek( 0 ) );
reader_ = input;
blargg_err_t err = open_v();
if ( err )
close();
else
opened_ = true;
return err;
}
// Close
void File_Extractor::close()
{
close_v();
close_();
}
void File_Extractor::close_()
{
delete own_file_;
own_file_ = NULL;
tell_ = 0;
reader_ = NULL;
opened_ = false;
path_.clear();
clear_file();
}
File_Extractor::~fex_t()
{
check( !opened() ); // fails if derived destructor didn't call close()
delete own_file_;
}
// Scanning
void File_Extractor::clear_file()
{
name_ = NULL;
wname_ = NULL;
done_ = true;
stat_called = false;
data_ptr_ = NULL;
set_info( 0 );
own_data_.clear();
clear_file_v();
}
void File_Extractor::set_name( const char new_name [], const blargg_wchar_t* new_wname )
{
name_ = new_name;
wname_ = new_wname;
done_ = false;
}
void File_Extractor::set_info( BOOST::uint64_t new_size, unsigned date, unsigned crc )
{
size_ = new_size;
date_ = (date != 0xFFFFFFFF ? date : 0);
crc32_ = crc;
set_remain( new_size );
}
blargg_err_t File_Extractor::next_()
{
tell_++;
clear_file();
blargg_err_t err = next_v();
if ( err )
clear_file();
return err;
}
blargg_err_t File_Extractor::next()
{
assert( !done() );
return next_();
}
blargg_err_t File_Extractor::rewind()
{
assert( opened() );
tell_ = 0;
clear_file();
blargg_err_t err = rewind_v();
if ( err )
clear_file();
return err;
}
blargg_err_t File_Extractor::stat()
{
assert( !done() );
if ( !stat_called )
{
RETURN_ERR( stat_v() );
stat_called = true;
}
return blargg_ok;
}
// Tell/seek
int const pos_offset = 1;
fex_pos_t File_Extractor::tell_arc() const
{
assert( opened() );
fex_pos_t pos = tell_arc_v();
assert( pos >= 0 );
return pos + pos_offset;
}
blargg_err_t File_Extractor::seek_arc( fex_pos_t pos )
{
assert( opened() );
assert( pos != 0 );
clear_file();
blargg_err_t err = seek_arc_v( pos - pos_offset );
if ( err )
clear_file();
return err;
}
fex_pos_t File_Extractor::tell_arc_v() const
{
return tell_;
}
blargg_err_t File_Extractor::seek_arc_v( fex_pos_t pos )
{
// >= because seeking to current file should always reset read pointer etc.
if ( tell_ >= pos )
RETURN_ERR( rewind() );
while ( tell_ < pos )
{
RETURN_ERR( next_() );
if ( done() )
{
assert( false );
return blargg_err_caller;
}
}
assert( tell_ == pos );
return blargg_ok;
}
// Extraction
blargg_err_t File_Extractor::rewind_file()
{
RETURN_ERR( stat() );
if ( tell() > 0 )
{
if ( data_ptr_ )
{
set_remain( size() );
}
else
{
RETURN_ERR( seek_arc( tell_arc() ) );
RETURN_ERR( stat() );
}
}
return blargg_ok;
}
blargg_err_t File_Extractor::data( const void** data_out )
{
assert( !done() );
*data_out = NULL;
if ( !data_ptr_ )
{
BOOST::uint64_t old_tell = tell();
RETURN_ERR( rewind_file() );
void const* ptr;
RETURN_ERR( data_v( &ptr ) );
data_ptr_ = ptr;
// Now that data is in memory, we can seek by simply setting remain
set_remain( size() - old_tell );
}
*data_out = data_ptr_;
return blargg_ok;
}
blargg_err_t File_Extractor::data_v( void const** out )
{
RETURN_ERR( own_data_.resize( size() ) );
*out = own_data_.begin();
blargg_err_t err = extract_v( own_data_.begin(), own_data_.size() );
if ( err )
own_data_.clear();
return err;
}
blargg_err_t File_Extractor::extract_v( void* out, int count )
{
void const* p;
RETURN_ERR( data( &p ) );
memcpy( out, STATIC_CAST(char const*,p) + (size() - remain()), count );
return blargg_ok;
}
blargg_err_t File_Extractor::read_v( void* out, int count )
{
if ( data_ptr_ )
return File_Extractor::extract_v( out, count );
return extract_v( out, count );
}

View File

@ -1,191 +0,0 @@
// Compressed file archive interface
// File_Extractor 1.0.0
#ifndef FILE_EXTRACTOR_H
#define FILE_EXTRACTOR_H
#include "blargg_common.h"
#include "Data_Reader.h"
#include "fex.h"
struct fex_t : private Data_Reader {
public:
virtual ~fex_t();
// Open/close
// Opens archive from custom data source. Keeps pointer until close().
blargg_err_t open( File_Reader* input, const char* path = NULL );
// Takes ownership of File_Reader* passed to open(), so that close()
// will delete it.
void own_file() { own_file_ = reader_; }
// See fex.h
blargg_err_t open( const char path [] );
fex_type_t type() const { return type_; }
void close();
// Scanning
// See fex.h
bool done() const { return done_; }
blargg_err_t next();
blargg_err_t rewind();
fex_pos_t tell_arc() const;
blargg_err_t seek_arc( fex_pos_t );
// Info
// See fex.h
const char* name() const { return name_; }
const blargg_wchar_t* wname() const { return wname_; }
blargg_err_t stat();
BOOST::uint64_t size() const { assert( stat_called ); return size_; }
unsigned int dos_date() const { return date_; }
unsigned int crc32() const { return crc32_; }
// Extraction
// Data_Reader to current file's data, so standard Data_Reader interface can
// be used, rather than having to treat archives specially. stat() must have
// been called.
Data_Reader& reader() { assert( stat_called ); return *this; }
// See fex.h
blargg_err_t data( const void** data_out );
BOOST::uint64_t tell() const { return size_ - remain(); }
// Derived interface
protected:
// Sets type of object
fex_t( fex_type_t );
// Path to archive file, or "" if none supplied
const char* arc_path() const { return path_.begin(); }
// Opens archive file if it's not already. If unbuffered is true, opens file
// without any buffering.
blargg_err_t open_arc_file( bool unbuffered = false );
// Archive file
File_Reader& arc() const { return *reader_; }
// Sets current file name
void set_name( const char name [], const blargg_wchar_t* wname = NULL );
// Sets current file information
void set_info( BOOST::uint64_t size, unsigned date = 0, unsigned crc = 0 );
// User overrides
// Overrides must do indicated task. Non-pure functions have reasonable default
// implementation. Overrides should avoid calling public functions like
// next() and rewind().
// Open archive using file_path(). OK to delay actual file opening until later.
// Default just calls open_arc_file(), then open_v().
virtual blargg_err_t open_path_v();
// Open archive using file() for source data. If unsupported, return error.
virtual blargg_err_t open_v() BLARGG_PURE( ; )
// Go to next file in archive and call set_name() and optionally set_info()
virtual blargg_err_t next_v() BLARGG_PURE( ; )
// Go back to first file in archive
virtual blargg_err_t rewind_v() BLARGG_PURE( ; )
// Close archive. Called even if open_path_v() or open_v() return unsuccessfully.
virtual void close_v() BLARGG_PURE( ; )
// Clear any fields related to current file
virtual void clear_file_v() { }
// Call set_info() if not already called by next_v()
virtual blargg_err_t stat_v() { return blargg_ok; }
// Return value that allows later return to this file. Result must be >= 0.
virtual fex_pos_t tell_arc_v() const;
// Return to previously saved position
virtual blargg_err_t seek_arc_v( fex_pos_t );
// One or both of the following must be overridden
// Provide pointer to data for current file in archive
virtual blargg_err_t data_v( const void** out );
// Extract next n bytes
virtual blargg_err_t extract_v( void* out, int n );
// Implementation
public:
BLARGG_DISABLE_NOTHROW
private:
fex_type_t const type_;
// Archive file
blargg_vector<char> path_;
File_Reader* reader_;
File_Reader* own_file_;
bool opened_;
// Position in archive
fex_pos_t tell_; // only used by default implementation of tell/seek
bool done_;
// Info for current file in archive
const char* name_;
const blargg_wchar_t* wname_;
unsigned date_;
unsigned crc32_;
BOOST::uint64_t size_;
bool stat_called;
// Current file contents
void const* data_ptr_; // NULL if not read into memory
blargg_vector<char> own_data_;
bool opened() const { return opened_; }
void clear_file();
void close_();
blargg_err_t set_path( const char* path );
blargg_err_t rewind_file();
blargg_err_t next_();
// Data_Reader overrides
// TODO: override skip_v?
virtual blargg_err_t read_v( void* out, int n );
};
struct fex_type_t_
{
const char* extension;
File_Extractor* (*new_fex)();
const char* name;
blargg_err_t (*init)(); // Called by fex_init(). Can be NULL.
};
extern const fex_type_t_
fex_7z_type [1],
fex_gz_type [1],
fex_rar_type [1],
fex_zip_type [1],
fex_bin_type [1];
inline blargg_err_t File_Extractor::open_v() { return blargg_ok; }
inline blargg_err_t File_Extractor::next_v() { return blargg_ok; }
inline blargg_err_t File_Extractor::rewind_v() { return blargg_ok; }
inline void File_Extractor::close_v() { }
// Default to Std_File_Reader for archive access
#ifndef FEX_FILE_READER
#define FEX_FILE_READER Std_File_Reader
#elif defined (FEX_FILE_READER_INCLUDE)
#include FEX_FILE_READER_INCLUDE
#endif
#endif

View File

@ -1,98 +0,0 @@
// File_Extractor 1.0.0. http://www.slack.net/~ant/
#include "Gzip_Extractor.h"
/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
// TODO: could close file once data has been read into memory
static blargg_err_t init_gzip_file()
{
get_crc_table(); // initialize zlib's CRC-32 tables
return blargg_ok;
}
static File_Extractor* new_gzip()
{
return BLARGG_NEW Gzip_Extractor;
}
fex_type_t_ const fex_gz_type [1] = {{
".gz",
&new_gzip,
"gzipped file",
&init_gzip_file
}};
Gzip_Extractor::Gzip_Extractor() :
File_Extractor( fex_gz_type )
{ }
Gzip_Extractor::~Gzip_Extractor()
{
close();
}
blargg_err_t Gzip_Extractor::open_path_v()
{
// skip opening file
return open_v();
}
blargg_err_t Gzip_Extractor::stat_v()
{
RETURN_ERR( open_arc_file( true ) );
if ( !gr.opened() || gr.tell() != 0 )
RETURN_ERR( gr.open( &arc() ) );
set_info( gr.remain(), 0, gr.crc32() );
return blargg_ok;
}
blargg_err_t Gzip_Extractor::open_v()
{
// Remove .gz suffix
size_t len = strlen( arc_path() );
if ( fex_has_extension( arc_path(), ".gz" ) )
len -= 3;
RETURN_ERR( name.resize( len + 1 ) );
memcpy( name.begin(), arc_path(), name.size() );
name [name.size() - 1] = '\0';
set_name( name.begin() );
return blargg_ok;
}
void Gzip_Extractor::close_v()
{
name.clear();
gr.close();
}
blargg_err_t Gzip_Extractor::next_v()
{
return blargg_ok;
}
blargg_err_t Gzip_Extractor::rewind_v()
{
set_name( name.begin() );
return blargg_ok;
}
blargg_err_t Gzip_Extractor::extract_v( void* p, int n )
{
return gr.read( p, n );
}

View File

@ -1,34 +0,0 @@
// Presents a gzipped file as an "archive" of just that file.
// Also handles non-gzipped files.
// File_Extractor 1.0.0
#ifndef GZIP_EXTRACTOR_H
#define GZIP_EXTRACTOR_H
#include "File_Extractor.h"
#include "Gzip_Reader.h"
class Gzip_Extractor : public File_Extractor {
public:
Gzip_Extractor();
virtual ~Gzip_Extractor();
protected:
virtual blargg_err_t open_path_v();
virtual blargg_err_t open_v();
virtual void close_v();
virtual blargg_err_t next_v();
virtual blargg_err_t rewind_v();
virtual blargg_err_t stat_v();
virtual blargg_err_t extract_v( void*, int );
private:
Gzip_Reader gr;
blargg_vector<char> name;
void set_info_();
};
#endif

View File

@ -1,85 +0,0 @@
// File_Extractor 1.0.0. http://www.slack.net/~ant/
#include "Gzip_Reader.h"
#include "blargg_endian.h"
/* Copyright (C) 2006-2009 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
Gzip_Reader::Gzip_Reader()
{
close();
}
Gzip_Reader::~Gzip_Reader()
{ }
static blargg_err_t gzip_reader_read( void* file, void* out, int* count )
{
return STATIC_CAST(File_Reader*,file)->read_avail( out, count );
}
blargg_err_t Gzip_Reader::calc_size()
{
size_ = in->size();
crc32_ = 0;
if ( inflater.deflated() )
{
byte trailer [8];
int old_pos = in->tell();
RETURN_ERR( in->seek( size_ - sizeof trailer ) );
RETURN_ERR( in->read( trailer, sizeof trailer ) );
RETURN_ERR( in->seek( old_pos ) );
crc32_ = get_le32( trailer + 0 );
unsigned n = get_le32( trailer + 4 );
if ( n > INT_MAX )
return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "gzip larger than 2GB" );
size_ = n;
}
return blargg_ok;
}
blargg_err_t Gzip_Reader::open( File_Reader* new_in )
{
close();
in = new_in;
RETURN_ERR( in->seek( 0 ) );
RETURN_ERR( inflater.begin( gzip_reader_read, new_in ) );
RETURN_ERR( inflater.set_mode( inflater.mode_auto ) );
RETURN_ERR( calc_size() );
set_remain( size_ );
return blargg_ok;
}
void Gzip_Reader::close()
{
in = NULL;
inflater.end();
}
blargg_err_t Gzip_Reader::read_v( void* out, int count )
{
assert( in );
int actual = count;
RETURN_ERR( inflater.read( out, &actual ) );
if ( actual != count )
return blargg_err_file_corrupt;
return blargg_ok;
}

View File

@ -1,46 +0,0 @@
// Transparently decompresses gzip files, as well as uncompressed
// File_Extractor 1.0.0
#ifndef GZIP_READER_H
#define GZIP_READER_H
#include "Data_Reader.h"
#include "Zlib_Inflater.h"
class Gzip_Reader : public Data_Reader {
public:
// Keeps pointer to reader until close(). If
blargg_err_t open( File_Reader* );
// True if file is open
bool opened() const { return in != NULL; }
// Frees memory
void close();
// True if file is compressed
bool deflated() const { return inflater.deflated(); }
// CRC-32 of data, of 0 if unavailable
unsigned int crc32() const { return crc32_; }
// Number of bytes read since opening
BOOST::uint64_t tell() const { return size_ - remain(); }
public:
Gzip_Reader();
virtual ~Gzip_Reader();
protected:
virtual blargg_err_t read_v( void*, int );
private:
File_Reader* in;
unsigned crc32_;
int size_;
Zlib_Inflater inflater;
blargg_err_t calc_size();
};
#endif

View File

@ -1,197 +0,0 @@
// File_Extractor 1.0.0. http://www.slack.net/~ant/
#include "blargg_common.h"
#if FEX_ENABLE_RAR
#include "Rar_Extractor.h"
/* Copyright (C) 2009 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
static blargg_err_t init_rar()
{
unrar_init();
return blargg_ok;
}
static File_Extractor* new_rar()
{
return BLARGG_NEW Rar_Extractor;
}
fex_type_t_ const fex_rar_type [1] = {{
".rar",
&new_rar,
"RAR archive",
&init_rar
}};
blargg_err_t Rar_Extractor::convert_err( unrar_err_t err )
{
blargg_err_t reader_err = reader.err;
reader.err = blargg_ok;
if ( reader_err )
check( err == unrar_next_err );
switch ( err )
{
case unrar_ok: return blargg_ok;
case unrar_err_memory: return blargg_err_memory;
case unrar_err_open: return blargg_err_file_read;
case unrar_err_not_arc: return blargg_err_file_type;
case unrar_err_corrupt: return blargg_err_file_corrupt;
case unrar_err_io: return blargg_err_file_io;
case unrar_err_arc_eof: return blargg_err_internal;
case unrar_err_encrypted: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR encryption not supported" );
case unrar_err_segmented: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR segmentation not supported" );
case unrar_err_huge: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "Huge RAR files not supported" );
case unrar_err_old_algo: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "Old RAR compression not supported" );
case unrar_err_new_algo: return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "RAR uses unknown newer compression" );
case unrar_next_err: break;
default:
check( false ); // unhandled RAR error
}
if ( reader_err )
return reader_err;
check( false );
return BLARGG_ERR( BLARGG_ERR_INTERNAL, "RAR archive" );
}
static inline unrar_err_t handle_err( Rar_Extractor::read_callback_t* h, blargg_err_t err )
{
if ( !err )
return unrar_ok;
h->err = err;
return unrar_next_err;
}
extern "C"
{
static unrar_err_t my_unrar_read( void* data, void* out, int* count, unrar_pos_t pos )
{
// TODO: 64-bit file support
Rar_Extractor::read_callback_t* h = STATIC_CAST(Rar_Extractor::read_callback_t*,data);
if ( h->pos != pos )
{
blargg_err_t err = h->in->seek( pos );
if ( err )
return handle_err( h, err );
h->pos = pos;
}
blargg_err_t err = h->in->read_avail( out, count );
if ( err )
return handle_err( h, err );
h->pos += *count;
return unrar_ok;
}
}
Rar_Extractor::Rar_Extractor() :
File_Extractor( fex_rar_type )
{
unrar = NULL;
}
Rar_Extractor::~Rar_Extractor()
{
close();
}
blargg_err_t Rar_Extractor::open_v()
{
reader.pos = 0;
reader.in = &arc();
reader.err = blargg_ok;
RETURN_ERR( arc().seek( 0 ) );
RETURN_ERR( convert_err( unrar_open_custom( &unrar, &my_unrar_read, &reader ) ) );
return skip_unextractables();
}
void Rar_Extractor::close_v()
{
unrar_close( unrar );
unrar = NULL;
reader.in = NULL;
}
blargg_err_t Rar_Extractor::skip_unextractables()
{
while ( !unrar_done( unrar ) && unrar_try_extract( unrar ) )
RETURN_ERR( next_raw() );
if ( !unrar_done( unrar ) )
{
unrar_info_t const* info = unrar_info( unrar );
set_name( info->name, (info->name_w && *info->name_w) ? info->name_w : NULL );
set_info( info->size, info->dos_date, (info->is_crc32 ? info->crc : 0) );
}
return blargg_ok;
}
blargg_err_t Rar_Extractor::next_raw()
{
return convert_err( unrar_next( unrar ) );
}
blargg_err_t Rar_Extractor::next_v()
{
RETURN_ERR( next_raw() );
return skip_unextractables();
}
blargg_err_t Rar_Extractor::rewind_v()
{
RETURN_ERR( convert_err( unrar_rewind( unrar ) ) );
return skip_unextractables();
}
fex_pos_t Rar_Extractor::tell_arc_v() const
{
return unrar_tell( unrar );
}
blargg_err_t Rar_Extractor::seek_arc_v( fex_pos_t pos )
{
RETURN_ERR( convert_err( unrar_seek( unrar, pos ) ) );
return skip_unextractables();
}
blargg_err_t Rar_Extractor::data_v( void const** out )
{
return convert_err( unrar_extract_mem( unrar, out ) );
}
blargg_err_t Rar_Extractor::extract_v( void* out, int count )
{
// We can read entire file directly into user buffer
if ( count == size() )
return convert_err( unrar_extract( unrar, out, count ) );
// This will call data_v() and copy from that buffer for us
return File_Extractor::extract_v( out, count );
}
#endif

View File

@ -1,43 +0,0 @@
// RAR archive extractor
// File_Extractor 1.0.0
#ifndef RAR_EXTRACTOR_H
#define RAR_EXTRACTOR_H
#include "File_Extractor.h"
#include "unrar/unrar.h"
class Rar_Extractor : public File_Extractor {
public:
Rar_Extractor();
virtual ~Rar_Extractor();
struct read_callback_t
{
const char* err;
BOOST::uint64_t pos;
File_Reader* in;
};
protected:
virtual blargg_err_t open_v();
virtual void close_v();
virtual blargg_err_t next_v();
virtual blargg_err_t rewind_v();
virtual fex_pos_t tell_arc_v() const;
virtual blargg_err_t seek_arc_v( fex_pos_t );
virtual blargg_err_t data_v( void const** );
virtual blargg_err_t extract_v( void*, int );
private:
unrar_t* unrar;
read_callback_t reader;
blargg_err_t convert_err( unrar_err_t );
blargg_err_t skip_unextractables();
blargg_err_t next_raw();
};
#endif

View File

@ -1,290 +0,0 @@
// File_Extractor 1.0.0. http://www.slack.net/~ant/
#include "Zip7_Extractor.h"
extern "C" {
#include "7z_C/7z.h"
#include "7z_C/7zAlloc.h"
#include "7z_C/7zCrc.h"
}
#include <time.h>
/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
static ISzAlloc zip7_alloc = { SzAlloc, SzFree };
static ISzAlloc zip7_alloc_temp = { SzAllocTemp, SzFreeTemp };
struct Zip7_Extractor_Impl :
ISeekInStream
{
CLookToRead look;
CSzArEx db;
// SzExtract state
UInt32 block_index;
Byte* buf;
size_t buf_size;
File_Reader* in;
const char* in_err;
};
extern "C"
{
// 7-zip callbacks pass an ISeekInStream* for data, so we must cast it
// back to ISeekInStream* FIRST, then cast to our Impl structure
static SRes zip7_read_( void* vstream, void* out, size_t* size )
{
assert( out && size );
ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream);
Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream);
long lsize = *size;
blargg_err_t err = impl->in->read_avail( out, &lsize );
if ( err )
{
*size = 0;
impl->in_err = err;
return SZ_ERROR_READ;
}
*size = lsize;
return SZ_OK;
}
static SRes zip7_seek_( void* vstream, Int64* pos, ESzSeek mode )
{
ISeekInStream* stream = STATIC_CAST(ISeekInStream*,vstream);
Zip7_Extractor_Impl* impl = STATIC_CAST(Zip7_Extractor_Impl*,stream);
if ( mode == SZ_SEEK_CUR )
{
assert( *pos == 0 ); // only used to find the archive start position
*pos = impl->in->tell();
return SZ_OK;
}
if ( mode == SZ_SEEK_END )
{
assert( *pos == 0 ); // only used to find file length
*pos = impl->in->size();
return SZ_OK;
}
assert( mode == SZ_SEEK_SET );
blargg_err_t err = impl->in->seek( *pos );
if ( err )
{
// don't set in_err in this case, since it might be benign
if ( err == blargg_err_file_eof )
return SZ_ERROR_INPUT_EOF;
impl->in_err = err;
return SZ_ERROR_READ;
}
return SZ_OK;
}
}
blargg_err_t Zip7_Extractor::zip7_err( int err )
{
// TODO: ignore in_err in some cases? unsure about which error to use
blargg_err_t in_err = impl->in_err;
impl->in_err = NULL;
if ( in_err )
{
check( err != SZ_OK );
return in_err;
}
switch ( err )
{
case SZ_OK: return blargg_ok;
case SZ_ERROR_MEM: return blargg_err_memory;
case SZ_ERROR_READ: return blargg_err_file_io;
case SZ_ERROR_CRC:
case SZ_ERROR_DATA:
case SZ_ERROR_INPUT_EOF:
case SZ_ERROR_ARCHIVE: return blargg_err_file_corrupt;
case SZ_ERROR_UNSUPPORTED: return blargg_err_file_feature;
case SZ_ERROR_NO_ARCHIVE: return blargg_err_file_type;
}
return blargg_err_generic;
}
static blargg_err_t init_7z()
{
static bool inited;
if ( !inited )
{
inited = true;
CrcGenerateTable();
}
return blargg_ok;
}
static File_Extractor* new_7z()
{
return BLARGG_NEW Zip7_Extractor;
}
fex_type_t_ const fex_7z_type [1] = {{
".7z",
&new_7z,
"7-zip archive",
&init_7z
}};
Zip7_Extractor::Zip7_Extractor() :
File_Extractor( fex_7z_type )
{
impl = NULL;
}
Zip7_Extractor::~Zip7_Extractor()
{
close();
}
blargg_err_t Zip7_Extractor::open_v()
{
RETURN_ERR( init_7z() );
if ( !impl )
{
impl = (Zip7_Extractor_Impl*) malloc( sizeof *impl );
CHECK_ALLOC( impl );
}
impl->in = &arc();
impl->block_index = (UInt32) -1;
impl->buf = NULL;
impl->buf_size = 0;
LookToRead_CreateVTable( &impl->look, false );
impl->ISeekInStream::Read = zip7_read_;
impl->ISeekInStream::Seek = zip7_seek_;
impl->look.realStream = impl;
LookToRead_Init( &impl->look );
SzArEx_Init( &impl->db );
impl->in_err = NULL;
RETURN_ERR( zip7_err( SzArEx_Open( &impl->db, &impl->look.s,
&zip7_alloc, &zip7_alloc_temp ) ) );
return seek_arc_v( 0 );
}
void Zip7_Extractor::close_v()
{
if ( impl )
{
if ( impl->in )
{
impl->in = NULL;
SzArEx_Free( &impl->db, &zip7_alloc );
}
IAlloc_Free( &zip7_alloc, impl->buf );
free( impl );
impl = NULL;
}
}
blargg_err_t Zip7_Extractor::next_v()
{
while ( ++index < (int) impl->db.db.NumFiles )
{
CSzFileItem const& item = impl->db.db.Files [index];
if ( !item.IsDir )
{
unsigned long date = 0;
if ( item.MTimeDefined )
{
const UInt64 epoch = ((UInt64)0x019db1de << 32) + 0xd53e8000;
/* 0x019db1ded53e8000ULL: 1970-01-01 00:00:00 (UTC) */
struct tm tm;
UInt64 time = ((UInt64)item.MTime.High << 32) + item.MTime.Low - epoch;
time /= 1000000;
time_t _time = time;
#ifdef _MSC_VER
localtime_s( &tm, &_time );
#else
localtime_r( &_time, &tm );
#endif
date = ( tm.tm_sec >> 1 ) & 0x1F |
(( tm.tm_min & 0x3F ) << 5 ) |
(( tm.tm_hour & 0x1F ) << 11 ) |
(( tm.tm_mday & 0x1F ) << 16 ) |
(( ( tm.tm_mon + 1 ) & 0x0F ) << 21 ) |
(( ( tm.tm_year - 80 ) & 0x7F ) << 25 );
}
size_t name_length = SzArEx_GetFileNameUtf16( &impl->db, index, 0 );
name16.resize( name_length );
SzArEx_GetFileNameUtf16( &impl->db, index, ( UInt16 * ) name16.begin() );
char * temp = blargg_to_utf8( name16.begin() );
if ( !temp ) temp = "";
size_t utf8_length = strlen( temp );
name8.resize( utf8_length + 1 );
memcpy( name8.begin(), temp, utf8_length + 1 );
free( temp );
set_name( name8.begin(), name16.begin() );
set_info( item.Size, 0, (item.CrcDefined ? item.Crc : 0) );
break;
}
}
return blargg_ok;
}
blargg_err_t Zip7_Extractor::rewind_v()
{
return seek_arc_v( 0 );
}
fex_pos_t Zip7_Extractor::tell_arc_v() const
{
return index;
}
blargg_err_t Zip7_Extractor::seek_arc_v( fex_pos_t pos )
{
assert( 0 <= pos && pos <= (int) impl->db.db.NumFiles );
index = pos - 1;
return next_v();
}
blargg_err_t Zip7_Extractor::data_v( void const** out )
{
impl->in_err = NULL;
size_t offset = 0;
size_t count = 0;
RETURN_ERR( zip7_err( SzArEx_Extract( &impl->db, &impl->look.s, index,
&impl->block_index, &impl->buf, &impl->buf_size,
&offset, &count, &zip7_alloc, &zip7_alloc_temp ) ) );
assert( count == (size_t) size() );
*out = impl->buf + offset;
return blargg_ok;
}

View File

@ -1,36 +0,0 @@
// 7-zip archive extractor
// File_Extractor 1.0.0
#ifndef ZIP7_EXTRACTOR_H
#define ZIP7_EXTRACTOR_H
#include "File_Extractor.h"
struct Zip7_Extractor_Impl;
class Zip7_Extractor : public File_Extractor {
public:
Zip7_Extractor();
virtual ~Zip7_Extractor();
protected:
virtual blargg_err_t open_v();
virtual void close_v();
virtual blargg_err_t next_v();
virtual blargg_err_t rewind_v();
virtual fex_pos_t tell_arc_v() const;
virtual blargg_err_t seek_arc_v( fex_pos_t );
virtual blargg_err_t data_v( void const** out );
private:
Zip7_Extractor_Impl* impl;
int index;
blargg_vector<char> name8;
blargg_vector<blargg_wchar_t> name16;
blargg_err_t zip7_err( int err );
};
#endif

View File

@ -1,390 +0,0 @@
// File_Extractor 1.0.0. http://www.slack.net/~ant/
#include "Zip_Extractor.h"
#include "blargg_endian.h"
/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
/* To avoid copying filename string from catalog, I terminate it by modifying
catalog data. This potentially requires moving the first byte of the type
of the next entry elsewhere; I move it to the first byte of made_by. Kind
of hacky, but I'd rather not have to allocate memory for a copy of it. */
#include "blargg_source.h"
/* Reads this much from end of file when first opening. Only this much is
searched for the end catalog entry. If whole catalog is within this data,
nothing more needs to be read on open. */
int const end_read_size = 8 * 1024;
/* Reads are are made using file offset that's a multiple of this,
increasing performance. */
int const disk_block_size = 4 * 1024;
// Read buffer used for extracting file data
int const read_buf_size = 16 * 1024;
struct header_t
{
char type [4];
byte vers [2];
byte flags [2];
byte method [2];
byte date [4];
byte crc [4];
byte raw_size [4];
byte size [4];
byte filename_len [2];
byte extra_len [2];
char filename [2]; // [filename_len]
//char extra [extra_len];
};
int const header_size = 30;
struct entry_t
{
char type [4];
byte made_by [2];
byte vers [2];
byte flags [2];
byte method [2];
byte date [4];
byte crc [4];
byte raw_size [4];
byte size [4];
byte filename_len [2];
byte extra_len [2];
byte comment_len [2];
byte disk [2];
byte int_attrib [2];
byte ext_attrib [4];
byte file_offset [4];
char filename [2]; // [filename_len]
//char extra [extra_len];
//char comment [comment_len];
};
int const entry_size = 46;
struct end_entry_t
{
char type [4];
byte disk [2];
byte first_disk [2];
byte disk_entry_count [2];
byte entry_count [2];
byte dir_size [4];
byte dir_offset [4];
byte comment_len [2];
char comment [2]; // [comment_len]
};
int const end_entry_size = 22;
static blargg_err_t init_zip()
{
get_crc_table(); // initialize zlib's CRC-32 tables
return blargg_ok;
}
static File_Extractor* new_zip()
{
return BLARGG_NEW Zip_Extractor;
}
fex_type_t_ const fex_zip_type [1] = {{
".zip",
&new_zip,
"ZIP archive",
&init_zip
}};
Zip_Extractor::Zip_Extractor() :
File_Extractor( fex_zip_type )
{
Zip_Extractor::clear_file_v();
// If these fail, structures had extra padding inserted by compiler
assert( offsetof (header_t,filename) == header_size );
assert( offsetof (entry_t,filename) == entry_size );
assert( offsetof (end_entry_t,comment) == end_entry_size );
}
Zip_Extractor::~Zip_Extractor()
{
close();
}
blargg_err_t Zip_Extractor::open_path_v()
{
RETURN_ERR( open_arc_file( true ) );
return File_Extractor::open_path_v();
}
inline
void Zip_Extractor::reorder_entry_header( int offset )
{
catalog [offset + 0] = 0;
catalog [offset + 4] = 'P';
}
blargg_err_t Zip_Extractor::open_v()
{
if ( arc().size() < end_entry_size )
return blargg_err_file_type;
// Read final end_read_size bytes of file
BOOST::uint64_t file_pos = max( (BOOST::uint64_t) 0, arc().size() - end_read_size );
file_pos -= file_pos % disk_block_size;
RETURN_ERR( catalog.resize( arc().size() - file_pos ) );
RETURN_ERR( arc().seek( file_pos ) );
RETURN_ERR( arc().read( catalog.begin(), catalog.size() ) );
// Find end-of-catalog entry
BOOST::uint64_t end_pos = catalog.size() - end_entry_size;
while ( end_pos >= 0 && memcmp( &catalog [end_pos], "PK\5\6", 4 ) )
end_pos--;
if ( end_pos < 0 )
return blargg_err_file_type;
end_entry_t const& end_entry = (end_entry_t&) catalog [end_pos];
end_pos += file_pos;
// some idiotic zip compressors add data to end of zip without setting comment len
// check( arc().size() == end_pos + end_entry_size + get_le16( end_entry.comment_len ) );
// Find file offset of beginning of catalog
catalog_begin = get_le32( end_entry.dir_offset );
int catalog_size = end_pos - catalog_begin;
if ( catalog_size < 0 )
return blargg_err_file_corrupt;
catalog_size += end_entry_size;
// See if catalog is entirely contained in bytes already read
BOOST::uint64_t begin_offset = catalog_begin - file_pos;
if ( begin_offset >= 0 )
memmove( catalog.begin(), &catalog [begin_offset], catalog_size );
RETURN_ERR( catalog.resize( catalog_size ) );
if ( begin_offset < 0 )
{
// Catalog begins before bytes read, so it needs to be read
RETURN_ERR( arc().seek( catalog_begin ) );
RETURN_ERR( arc().read( catalog.begin(), catalog.size() ) );
}
// First entry in catalog should be a file or end of archive
if ( memcmp( catalog.begin(), "PK\1\2", 4 ) && memcmp( catalog.begin(), "PK\5\6", 4 ) )
return blargg_err_file_type;
reorder_entry_header( 0 );
return rewind_v();
}
void Zip_Extractor::close_v()
{
catalog.clear();
}
// Scanning
inline
static bool is_normal_file( entry_t const& e, unsigned len )
{
int last_char = (len ? e.filename [len - 1] : '/');
bool is_dir = (last_char == '/' || last_char == '\\');
if ( is_dir && get_le32( e.size ) == 0 )
return false;
check( !is_dir );
// Mac OS X puts meta-information in separate files with normal extensions,
// so they must be filtered out or caller will mistake them for normal files.
if ( e.made_by[1] == 3 )
{
const char* dir = strrchr( e.filename, '/' );
if ( dir )
dir++;
else
dir = e.filename;
if ( *dir == '.' )
return false;
if ( !strcmp( dir, "Icon\x0D" ) )
return false;
}
return true;
}
blargg_err_t Zip_Extractor::update_info( bool advance_first )
{
while ( 1 )
{
entry_t& e = (entry_t&) catalog [catalog_pos];
if ( memcmp( e.type, "\0K\1\2P", 5 ) && memcmp( e.type, "PK\1\2", 4 ) )
{
check( !memcmp( e.type, "\0K\5\6P", 5 ) );
break;
}
unsigned len = get_le16( e.filename_len );
int next_offset = catalog_pos + entry_size + len + get_le16( e.extra_len ) +
get_le16( e.comment_len );
if ( (unsigned) next_offset > catalog.size() - end_entry_size )
return blargg_err_file_corrupt;
if ( catalog [next_offset] == 'P' )
reorder_entry_header( next_offset );
if ( !advance_first )
{
e.filename [len] = 0; // terminate name
if ( is_normal_file( e, len ) )
{
set_name( e.filename );
set_info( get_le32( e.size ), get_le32( e.date ), get_le32( e.crc ) );
break;
}
}
catalog_pos = next_offset;
advance_first = false;
}
return blargg_ok;
}
blargg_err_t Zip_Extractor::next_v()
{
return update_info( true );
}
blargg_err_t Zip_Extractor::rewind_v()
{
return seek_arc_v( 0 );
}
fex_pos_t Zip_Extractor::tell_arc_v() const
{
return catalog_pos;
}
blargg_err_t Zip_Extractor::seek_arc_v( fex_pos_t pos )
{
assert( 0 <= pos && (size_t) pos <= catalog.size() - end_entry_size );
catalog_pos = pos;
return update_info( false );
}
// Reading
void Zip_Extractor::clear_file_v()
{
buf.end();
}
blargg_err_t Zip_Extractor::inflater_read( void* data, void* out, int* count )
{
Zip_Extractor& self = *STATIC_CAST(Zip_Extractor*,data);
if ( *count > self.raw_remain )
*count = self.raw_remain;
self.raw_remain -= *count;
return self.arc().read( out, *count );
}
blargg_err_t Zip_Extractor::fill_buf( int offset, int buf_size, int initial_read )
{
raw_remain = arc().size() - offset;
RETURN_ERR( arc().seek( offset ) );
return buf.begin( inflater_read, this, buf_size, initial_read );
}
blargg_err_t Zip_Extractor::first_read( int count )
{
entry_t const& e = (entry_t&) catalog [catalog_pos];
// Determine compression
{
int method = get_le16( e.method );
if ( (method && method != Z_DEFLATED) || get_le16( e.vers ) > 20 )
return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "compression method" );
file_deflated = (method != 0);
}
int raw_size = get_le32( e.raw_size );
int file_offset = get_le32( e.file_offset );
int align = file_offset % disk_block_size;
{
// read header
int buf_size = 3 * disk_block_size - 1 + raw_size; // space for all raw data
buf_size -= buf_size % disk_block_size;
int initial_read = buf_size;
if ( !file_deflated || count < size() )
{
buf_size = read_buf_size;
initial_read = disk_block_size * 2;
}
// TODO: avoid re-reading if buffer already has data we want?
RETURN_ERR( fill_buf( file_offset - align, buf_size, initial_read ) );
}
header_t const& h = (header_t&) buf.data() [align];
if ( buf.filled() < align + header_size || memcmp( h.type, "PK\3\4", 4 ) )
return blargg_err_file_corrupt;
// CRCs of header and file data
correct_crc = get_le32( h.crc );
if ( !correct_crc )
correct_crc = get_le32( e.crc );
check( correct_crc == get_le32( e.crc ) ); // catalog CRC should match
crc = ::crc32( 0, NULL, 0 );
// Data offset
int data_offset = file_offset + header_size +
get_le16( h.filename_len ) + get_le16( h.extra_len );
if ( data_offset + raw_size > catalog_begin )
return blargg_err_file_corrupt;
// Refill buffer if there's lots of extra data after header
int buf_offset = data_offset - file_offset + align;
if ( buf_offset > buf.filled() )
{
// TODO: this will almost never occur, making it a good place for bugs
buf_offset = data_offset % disk_block_size;
RETURN_ERR( fill_buf( data_offset - buf_offset, read_buf_size, disk_block_size ) );
}
raw_remain = raw_size - (buf.filled() - buf_offset);
return buf.set_mode( (file_deflated ? buf.mode_raw_deflate : buf.mode_copy), buf_offset );
}
blargg_err_t Zip_Extractor::extract_v( void* out, int count )
{
if ( tell() == 0 )
RETURN_ERR( first_read( count ) );
int actual = count;
RETURN_ERR( buf.read( out, &actual ) );
if ( actual < count )
return blargg_err_file_corrupt;
crc = ::crc32( crc, (byte const*) out, count );
if ( count == reader().remain() && crc != correct_crc )
return blargg_err_file_corrupt;
return blargg_ok;
}

View File

@ -1,45 +0,0 @@
// ZIP archive extractor. Only supports deflation and store (no compression).
// File_Extractor 1.0.0
#ifndef ZIP_EXTRACTOR_H
#define ZIP_EXTRACTOR_H
#include "File_Extractor.h"
#include "Zlib_Inflater.h"
class Zip_Extractor : public File_Extractor {
public:
Zip_Extractor();
virtual ~Zip_Extractor();
protected:
virtual blargg_err_t open_path_v();
virtual blargg_err_t open_v();
virtual void close_v();
virtual void clear_file_v();
virtual blargg_err_t next_v();
virtual blargg_err_t rewind_v();
virtual fex_pos_t tell_arc_v() const;
virtual blargg_err_t seek_arc_v( fex_pos_t );
virtual blargg_err_t extract_v( void*, int );
private:
blargg_vector<char> catalog;
int catalog_begin; // offset of first catalog entry in file (to detect corruption)
int catalog_pos; // position of current entry in catalog
BOOST::uint64_t raw_remain; // bytes remaining to be read from zip file for current file
unsigned crc; // ongoing CRC of extracted bytes
unsigned correct_crc;
bool file_deflated;
Zlib_Inflater buf;
blargg_err_t fill_buf( int offset, int buf_size, int initial_read );
blargg_err_t update_info( bool advance_first );
blargg_err_t first_read( int count );
void reorder_entry_header( int offset );
static blargg_err_t inflater_read( void* data, void* out, int* count );
};
#endif

View File

@ -1,257 +0,0 @@
// File_Extractor 1.0.0. http://www.slack.net/~ant/
#include "Zlib_Inflater.h"
/* Copyright (C) 2006-2009 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
int const block_size = 4096;
static const char* get_zlib_err( int code )
{
assert( code != Z_OK );
switch ( code )
{
case Z_MEM_ERROR: return blargg_err_memory;
case Z_DATA_ERROR: return blargg_err_file_corrupt;
// TODO: handle more error codes
}
const char* str = zError( code );
if ( !str )
str = BLARGG_ERR( BLARGG_ERR_GENERIC, "problem unzipping data" );
return str;
}
void Zlib_Inflater::end()
{
if ( deflated_ )
{
deflated_ = false;
if ( inflateEnd( &zbuf ) )
check( false );
}
buf.clear();
static z_stream const empty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
memcpy( &zbuf, &empty, sizeof zbuf );
}
Zlib_Inflater::Zlib_Inflater()
{
deflated_ = false;
end(); // initialize things
}
Zlib_Inflater::~Zlib_Inflater()
{
end();
}
blargg_err_t Zlib_Inflater::fill_buf( int count )
{
byte* out = buf.end() - count;
RETURN_ERR( callback( user_data, out, &count ) );
zbuf.avail_in = count;
zbuf.next_in = out;
return blargg_ok;
}
blargg_err_t Zlib_Inflater::begin( callback_t new_callback, void* new_user_data,
int new_buf_size, int initial_read )
{
callback = new_callback;
user_data = new_user_data;
end();
// TODO: decide whether using different size on alloc failure is a good idea
//RETURN_ERR( buf.resize( new_buf_size ? new_buf_size : 4 * block_size ) );
if ( new_buf_size && buf.resize( new_buf_size ) )
{
ACK_FAILURE();
new_buf_size = 0;
}
if ( !new_buf_size )
{
RETURN_ERR( buf.resize( 4 * block_size ) );
initial_read = 0;
}
// Fill buffer with some data, less than normal buffer size since caller might
// just be examining beginning of file.
return fill_buf( initial_read ? initial_read : block_size );
}
blargg_err_t Zlib_Inflater::set_mode( mode_t mode, int data_offset )
{
zbuf.next_in += data_offset;
zbuf.avail_in -= data_offset;
if ( mode == mode_auto )
{
// examine buffer for gzip header
mode = mode_copy;
unsigned const min_gzip_size = 2 + 8 + 8;
if ( zbuf.avail_in >= min_gzip_size &&
zbuf.next_in [0] == 0x1F && zbuf.next_in [1] == 0x8B )
mode = mode_ungz;
}
if ( mode != mode_copy )
{
int wb = MAX_WBITS + 16; // have zlib handle gzip header
if ( mode == mode_raw_deflate )
wb = -MAX_WBITS;
int zerr = inflateInit2( &zbuf, wb );
if ( zerr )
{
zbuf.next_in = NULL;
return get_zlib_err( zerr );
}
deflated_ = true;
}
return blargg_ok;
}
/*
// Reads/inflates entire stream. All input must be in buffer, and count must be total
// of all output.
blargg_err_t read_all( void* out, int count );
// zlib automatically applies this optimization (uses inflateFast)
// TODO: remove
blargg_err_t Zlib_Inflater::read_all( void* out, int count )
{
if ( deflated_ )
{
zbuf.next_out = (Bytef*) out;
zbuf.avail_out = count;
int err = inflate( &zbuf, Z_FINISH );
if ( zbuf.avail_out || err != Z_STREAM_END )
return blargg_err_file_corrupt;
}
else
{
if ( zbuf.avail_in < count )
return blargg_err_file_corrupt;
memcpy( out, zbuf.next_in, count );
zbuf.next_in += count;
zbuf.avail_in -= count;
}
return blargg_ok;
}
*/
blargg_err_t Zlib_Inflater::read( void* out, int* count_io )
{
int remain = *count_io;
if ( remain && zbuf.next_in )
{
if ( deflated_ )
{
zbuf.next_out = (Bytef*) out;
zbuf.avail_out = remain;
while ( 1 )
{
uInt old_avail_in = zbuf.avail_in;
int err = inflate( &zbuf, Z_NO_FLUSH );
if ( err == Z_STREAM_END )
{
remain = zbuf.avail_out;
end();
break; // no more data to inflate
}
if ( err && (err != Z_BUF_ERROR || old_avail_in) )
return get_zlib_err( err );
if ( !zbuf.avail_out )
{
remain = 0;
break; // requested number of bytes inflated
}
if ( zbuf.avail_in )
{
// inflate() should never leave input if there's still space for output
check( false );
return blargg_err_file_corrupt;
}
RETURN_ERR( fill_buf( buf.size() ) );
if ( !zbuf.avail_in )
return blargg_err_file_corrupt; // stream didn't end but there's no more data
}
}
else
{
while ( 1 )
{
// copy buffered data
if ( zbuf.avail_in )
{
long count = zbuf.avail_in;
if ( count > remain )
count = remain;
memcpy( out, zbuf.next_in, count );
zbuf.total_out += count;
out = (char*) out + count;
remain -= count;
zbuf.next_in += count;
zbuf.avail_in -= count;
}
if ( !zbuf.avail_in && zbuf.next_in < buf.end() )
{
end();
break;
}
// read large request directly
if ( remain + zbuf.total_out % block_size >= buf.size() )
{
int count = remain;
RETURN_ERR( callback( user_data, out, &count ) );
zbuf.total_out += count;
out = (char*) out + count;
remain -= count;
if ( remain )
{
end();
break;
}
}
if ( !remain )
break;
RETURN_ERR( fill_buf( buf.size() - zbuf.total_out % block_size ) );
}
}
}
*count_io -= remain;
return blargg_ok;
}

View File

@ -1,70 +0,0 @@
// Simplifies use of zlib for inflating data
// File_Extractor 1.0.0
#ifndef ZLIB_INFLATER_H
#define ZLIB_INFLATER_H
#include "blargg_common.h"
#include "Data_Reader.h"
#include <zlib.h>
class Zlib_Inflater {
public:
// Reads at most min(*count,bytes_until_eof()) bytes into *out and set *count
// to that number, or returns error if that many can't be read.
typedef blargg_err_t (*callback_t)( void* user_data, void* out, int* count );
// Begins by setting callback and filling buffer. Default buffer is 16K and
// filled to 4K, or specify buf_size and initial_read for custom buffer size
// and how much to read initially.
blargg_err_t begin( callback_t, void* user_data,
int buf_size = 0, int initial_read = 0 );
// Data read into buffer by begin()
const unsigned char* data() const { return zbuf.next_in; }
int filled() const { return zbuf.avail_in; }
// Begins inflation using specified mode. Using mode_auto selects between
// mode_copy and mode_ungz by examining first two bytes of buffer. Use
// buf_offset to specify where data begins in buffer, in case there is
// header data that should be skipped.
enum mode_t { mode_copy, mode_ungz, mode_raw_deflate, mode_auto };
blargg_err_t set_mode( mode_t, int buf_offset = 0 );
// True if set_mode() has been called with mode_ungz or mode_raw_deflate
bool deflated() const { return deflated_; }
// Reads/inflates at most *count_io bytes into *out and sets *count_io to actual
// number of bytes read (less than requested if end of data was reached).
// Buffers source data internally, even in copy mode, so input file can be
// unbuffered without sacrificing performance.
blargg_err_t read( void* out, int* count_io );
// Total number of bytes read since begin()
int tell() const { return zbuf.total_out; }
// Ends inflation and frees memory
void end();
private:
// noncopyable
Zlib_Inflater( const Zlib_Inflater& );
Zlib_Inflater& operator = ( const Zlib_Inflater& );
// Implementation
public:
Zlib_Inflater();
~Zlib_Inflater();
private:
z_stream_s zbuf;
blargg_vector<unsigned char> buf;
bool deflated_;
callback_t callback;
void* user_data;
blargg_err_t fill_buf( int count );
};
#endif

View File

@ -1,321 +0,0 @@
// File_Extractor 1.0.0. http://www.slack.net/~ant/
#include "fex.h"
#include "File_Extractor.h"
#include "blargg_endian.h"
#include <string.h>
#include <ctype.h>
/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
//// Types
BLARGG_EXPORT const fex_type_t* fex_type_list( void )
{
static fex_type_t const fex_type_list_ [] =
{
#ifdef FEX_TYPE_LIST
FEX_TYPE_LIST
#else
// Modify blargg_config.h to change type list, NOT this file
fex_7z_type,
fex_gz_type,
#if FEX_ENABLE_RAR
fex_rar_type,
#endif
fex_zip_type,
#endif
fex_bin_type,
NULL
};
return fex_type_list_;
}
BLARGG_EXPORT fex_err_t fex_init( void )
{
static bool inited;
if ( !inited )
{
for ( fex_type_t const* t = fex_type_list(); *t != NULL; ++t )
{
if ( (*t)->init )
RETURN_ERR( (*t)->init() );
}
inited = true;
}
return blargg_ok;
}
BLARGG_EXPORT const char* fex_identify_header( void const* header )
{
unsigned four = get_be32( header );
switch ( four )
{
case 0x52457E5E:
case 0x52617221: return ".rar";
case 0x377ABCAF: return ".7z";
case 0x504B0304:
case 0x504B0506: return ".zip";
case 0x53495421: return ".sit";
case 0x41724301: return ".arc";
case 0x4D534346: return ".cab";
case 0x5A4F4F20: return ".zoo";
}
unsigned three = four >> 8;
switch ( three )
{
case 0x425A68: return ".bz2";
}
unsigned two = four >> 16;
switch ( two )
{
case 0x1F8B: return ".gz";
case 0x60EA: return ".arj";
}
unsigned skip_first_two = four & 0xFFFF;
if ( skip_first_two == 0x2D6C )
return ".lha";
return "";
}
static int fex_has_extension_( const char str [], const char suffix [], size_t str_len )
{
size_t suffix_len = strlen( suffix );
if ( str_len >= suffix_len )
{
str += str_len - suffix_len;
while ( *str && tolower( (unsigned char) *str ) == *suffix )
{
str++;
suffix++;
}
}
return *suffix == 0;
}
BLARGG_EXPORT int fex_has_extension( const char str [], const char suffix [] )
{
return fex_has_extension_( str, suffix, strlen( str ) );
}
static int is_archive_extension( const char str [] )
{
static const char exts [] [6] = {
".7z",
".arc",
".arj",
".bz2",
".cab",
".dmg",
".gz",
".lha",
".lz",
".lzh",
".lzma",
".lzo",
".lzx",
".pea",
".rar",
".sit",
".sitx",
".tgz",
".tlz",
".z",
".zip",
".zoo",
""
};
size_t str_len = strlen( str );
const char (*ext) [6] = exts;
for ( ; **ext; ext++ )
{
if ( fex_has_extension_( str, *ext, str_len ) )
return 1;
}
return 0;
}
BLARGG_EXPORT fex_type_t fex_identify_extension( const char str [] )
{
size_t str_len = strlen( str );
for ( fex_type_t const* types = fex_type_list(); *types; types++ )
{
if ( fex_has_extension_( str, (*types)->extension, str_len ) )
{
// Avoid treating known archive type as binary
if ( *(*types)->extension || !is_archive_extension( str ) )
return *types;
}
}
return NULL;
}
BLARGG_EXPORT fex_err_t fex_identify_file( fex_type_t* type_out, const char path [] )
{
*type_out = NULL;
fex_type_t type = fex_identify_extension( path );
// Unsupported extension?
if ( !type )
return blargg_ok; // reject
// Unknown/no extension?
if ( !*(type->extension) )
{
// Examine header
FEX_FILE_READER in;
RETURN_ERR( in.open( path ) );
if ( in.remain() >= fex_identify_header_size )
{
char h [fex_identify_header_size];
RETURN_ERR( in.read( h, sizeof h ) );
type = fex_identify_extension( fex_identify_header( h ) );
}
}
*type_out = type;
return blargg_ok;
}
BLARGG_EXPORT fex_err_t fex_open_type( fex_t** fe_out, const char path [], fex_type_t type )
{
*fe_out = NULL;
if ( !type )
return blargg_err_file_type;
fex_t* fe = type->new_fex();
CHECK_ALLOC( fe );
fex_err_t err = fe->open( path );
if ( err )
{
delete fe;
return err;
}
*fe_out = fe;
return blargg_ok;
}
BLARGG_EXPORT fex_err_t fex_open( fex_t** fe_out, const char path [] )
{
*fe_out = NULL;
fex_type_t type;
RETURN_ERR( fex_identify_file( &type, path ) );
return fex_open_type( fe_out, path, type );
}
//// Wide paths
char* fex_wide_to_path( const blargg_wchar_t* wide )
{
return blargg_to_utf8( wide );
}
void fex_free_path( char* path )
{
free( path );
}
//// Errors
#define ENTRY( name ) { blargg_err_##name, fex_err_##name }
static blargg_err_to_code_t const fex_codes [] =
{
ENTRY( generic ),
ENTRY( memory ),
ENTRY( caller ),
ENTRY( internal ),
ENTRY( limitation ),
ENTRY( file_missing ),
ENTRY( file_read ),
ENTRY( file_io ),
ENTRY( file_eof ),
ENTRY( file_type ),
ENTRY( file_feature ),
ENTRY( file_corrupt ),
{ 0, -1 }
};
#undef ENTRY
static int err_code( fex_err_t err )
{
return blargg_err_to_code( err, fex_codes );
}
BLARGG_EXPORT int fex_err_code( fex_err_t err )
{
int code = err_code( err );
return (code >= 0 ? code : fex_err_generic);
}
BLARGG_EXPORT fex_err_t fex_code_to_err( int code )
{
return blargg_code_to_err( code, fex_codes );
}
BLARGG_EXPORT const char* fex_err_details( fex_err_t err )
{
// If we don't have error code assigned, return entire string
return (err_code( err ) >= 0 ? blargg_err_details( err ) : blargg_err_str( err ));
}
//// Wrappers
BLARGG_EXPORT fex_err_t fex_read( fex_t* fe, void* out, int count )
{
RETURN_ERR( fe->stat() );
return fe->reader().read( out, count );
}
BLARGG_EXPORT void fex_close ( fex_t* fe ) { delete fe; }
BLARGG_EXPORT fex_type_t fex_type ( const fex_t* fe ) { return fe->type(); }
BLARGG_EXPORT int fex_done ( const fex_t* fe ) { return fe->done(); }
BLARGG_EXPORT const char* fex_name ( const fex_t* fe ) { return fe->name(); }
BLARGG_EXPORT const blargg_wchar_t* fex_wname ( const fex_t* fe ) { return fe->wname(); }
BLARGG_EXPORT uint64_t fex_size ( const fex_t* fe ) { return fe->size(); }
BLARGG_EXPORT unsigned fex_dos_date ( const fex_t* fe ) { return fe->dos_date(); }
BLARGG_EXPORT unsigned fex_crc32 ( const fex_t* fe ) { return fe->crc32(); }
BLARGG_EXPORT fex_err_t fex_stat ( fex_t* fe ) { return fe->stat(); }
BLARGG_EXPORT fex_err_t fex_next ( fex_t* fe ) { return fe->next(); }
BLARGG_EXPORT fex_err_t fex_rewind ( fex_t* fe ) { return fe->rewind(); }
BLARGG_EXPORT uint64_t fex_tell ( const fex_t* fe ) { return fe->tell(); }
BLARGG_EXPORT fex_pos_t fex_tell_arc ( const fex_t* fe ) { return fe->tell_arc(); }
BLARGG_EXPORT fex_err_t fex_seek_arc ( fex_t* fe, fex_pos_t pos ) { return fe->seek_arc( pos ); }
BLARGG_EXPORT const char* fex_type_extension ( fex_type_t t ) { return t->extension; }
BLARGG_EXPORT const char* fex_type_name ( fex_type_t t ) { return t->name; }
BLARGG_EXPORT fex_err_t fex_data ( fex_t* fe, const void** data_out ) { return fe->data( data_out ); }
BLARGG_EXPORT const char* fex_err_str ( fex_err_t err ) { return blargg_err_str( err ); }

View File

@ -1,206 +0,0 @@
/** Uniform access to zip, gzip, 7-zip, and RAR compressed archives \file */
/* File_Extractor 1.0.0 */
#ifndef FEX_H
#define FEX_H
#include <stddef.h>
#include <stdint.h>
#include "blargg_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/** First parameter of most functions is fex_t*, or const fex_t* if nothing is
changed. Once one of these functions returns an error, the archive should not
be used any further, other than to close it. One exception is
fex_error_file_eof; the archive may still be used after this. */
typedef struct fex_t fex_t;
/** Pointer to error, or NULL if function was successful. See error functions
below. */
#ifndef fex_err_t /* (#ifndef allows better testing of library) */
typedef const char* fex_err_t;
#endif
/**** File types ****/
/** Archive file type identifier. Can also hold NULL. */
typedef const struct fex_type_t_* fex_type_t;
/** Array of supported types, with NULL at end */
const fex_type_t* fex_type_list( void );
/** Name of this archive type, e.g. "ZIP archive", "file" */
const char* fex_type_name( fex_type_t );
/** Usual file extension for type, e.g. ".zip", ".7z". For binary file type,
returns "", since it can open any file. */
const char* fex_type_extension( fex_type_t );
/**** Wide-character file paths ****/
/** Converts wide-character path to form suitable for use with fex functions. */
char* fex_wide_to_path( const blargg_wchar_t* wide );
/** Frees converted path. OK to pass NULL. */
void fex_free_path( char* );
/**** Identification ****/
/** True if str ends in extension. If extension is "", always returns true.
Converts str to lowercase before comparison, so extension should ALREADY be
lowercase (i.e. pass ".zip", NOT ".ZIP"). */
int fex_has_extension( const char str [], const char extension [] );
/** Determines type based on first fex_identify_header_size bytes of file.
Returns usual file extension this should have (e.g. ".zip", ".gz", etc.).
Returns "" if file header is not recognized. */
const char* fex_identify_header( const void* header );
enum { fex_identify_header_size = 16 };
/** Determines type based on extension of a file path, or just a lone extension
(must include '.', e.g. ".zip", NOT just "zip"). Returns NULL if extension is
for an unsupported type (e.g. ".lzh"). */
fex_type_t fex_identify_extension( const char path_or_extension [] );
/** Determines type based on filename extension and/or file header. Sets *out
to determined type, or NULL if type is not supported. */
fex_err_t fex_identify_file( fex_type_t* out, const char path [] );
/** Type of an already-opened archive */
fex_type_t fex_type( const fex_t* );
/**** Open/close ****/
/** Initializes static tables used by library. Automatically called by
fex_open(). OK to call more than once. */
fex_err_t fex_init( void );
/** Opens archive and points *out at it. If error, sets *out to NULL. */
fex_err_t fex_open( fex_t** out, const char path [] );
/** Opens archive of specified type and sets *out. Returns error if file is not
of that archive type. If error, sets *out to NULL. */
fex_err_t fex_open_type( fex_t** out, const char path [], fex_type_t );
/** Closes archive and frees memory. OK to pass NULL. */
void fex_close( fex_t* );
/**** Scanning ****/
/** True if at end of archive. Must be called after fex_open() or fex_rewind(),
as an archive might contain no files. */
int fex_done( const fex_t* );
/** Goes to next file in archive. If there are no more files, fex_done() will
now return true. */
fex_err_t fex_next( fex_t* );
/** Goes back to first file in archive, as if it were just opened with
fex_open() */
fex_err_t fex_rewind( fex_t* );
/** Saved position in archive. Can also store zero. */
typedef uint64_t fex_pos_t;
/** Position of current file in archive. Never returns zero. */
fex_pos_t fex_tell_arc( const fex_t* );
/** Returns to file at previously-saved position */
fex_err_t fex_seek_arc( fex_t*, fex_pos_t );
/**** Info ****/
/** Name of current file */
const char* fex_name( const fex_t* );
/** Wide-character name of current file, or NULL if unavailable */
const blargg_wchar_t* fex_wname( const fex_t* );
/** Makes further information available for file */
fex_err_t fex_stat( fex_t* );
/** Size of current file. fex_stat() or fex_data() must have been called. */
uint64_t fex_size( const fex_t* );
/** Modification date of current file (MS-DOS format), or 0 if unavailable.
fex_stat() must have been called. */
unsigned int fex_dos_date( const fex_t* );
/** CRC-32 checksum of current file's contents, or 0 if unavailable. Doesn't
require calculation; simply gets it from file's header. fex_stat() must have
been called. */
unsigned int fex_crc32( const fex_t* );
/**** Extraction ****/
/** Reads n bytes from current file. Reading past end of file results in
fex_err_file_eof. */
fex_err_t fex_read( fex_t*, void* out, int n );
/** Number of bytes read from current file */
uint64_t fex_tell( const fex_t* );
/** Points *out at current file's data in memory. Pointer is valid until
fex_next(), fex_rewind(), fex_seek_arc(), or fex_close() is called. Pointer
must NOT be freed(); library frees it automatically. If error, sets *out to
NULL. */
fex_err_t fex_data( fex_t*, const void** out );
/**** Errors ****/
/** Error string associated with err. Returns "" if err is NULL. Returns err
unchanged if it isn't a fex_err_t returned by library. */
const char* fex_err_str( fex_err_t err );
/** Details of error beyond main cause, or "" if none or err is NULL. Returns
err unchanged if it isn't a fex_err_t returned by library. */
const char* fex_err_details( fex_err_t err );
/** Numeric code corresponding to err. Returns fex_ok if err is NULL. Returns
fex_err_generic if err isn't a fex_err_t returned by library. */
int fex_err_code( fex_err_t err );
enum {
fex_ok = 0,/**< Successful call. Guaranteed to be zero. */
fex_err_generic = 0x01,/**< Error of unspecified type */
fex_err_memory = 0x02,/**< Out of memory */
fex_err_caller = 0x03,/**< Caller called function with bad args */
fex_err_internal = 0x04,/**< Internal problem, bug, etc. */
fex_err_limitation = 0x05,/**< Exceeded program limit */
fex_err_file_missing = 0x20,/**< File not found at specified path */
fex_err_file_read = 0x21,/**< Couldn't open file for reading */
fex_err_file_io = 0x23,/**< Read/write error */
fex_err_file_eof = 0x25,/**< Tried to read past end of file */
fex_err_file_type = 0x30,/**< File is of wrong type */
fex_err_file_feature = 0x32,/**< File requires unsupported feature */
fex_err_file_corrupt = 0x33 /**< File is corrupt */
};
/** fex_err_t corresponding to numeric code. Note that this might not recover
the original fex_err_t before it was converted to a numeric code; in
particular, fex_err_details(fex_code_to_err(code)) will be "" in most cases. */
fex_err_t fex_code_to_err( int code );
/* Deprecated */
typedef fex_t File_Extractor;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -42,10 +42,6 @@ SRCS = \
../fex/Data_Reader.cpp \
../fex/blargg_errors.cpp \
../fex/blargg_common.cpp
# ../nes_emu/Nes_Recorder.cpp
# ../nes_emu/Nes_Film.cpp
# ../nes_emu/Nes_Film_Data.cpp
# ../nes_emu/Nes_Film_Packer.cpp
OBJS = $(SRCS:.cpp=.o)

View File

@ -1,215 +0,0 @@
// Sunsoft FME-07 mapper
// Nes_Emu 0.5.6. http://www.slack.net/~ant/libs/
#include "Nes_Mapper.h"
#include "blargg_endian.h"
#include "Nes_Fme07_Apu.h"
/* Copyright (C) 2005 Chris Moeller */
/* Copyright (C) 2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
struct fme07_state_t
{
// first 16 bytes in register order
BOOST::uint8_t regs [13];
BOOST::uint8_t irq_mode;
BOOST::uint16_t irq_count;
BOOST::uint8_t command;
BOOST::uint8_t irq_pending;
fme07_snapshot_t sound_state; // only used when saving/restoring state
void swap();
};
BOOST_STATIC_ASSERT( sizeof (fme07_state_t) == 18 + sizeof (fme07_snapshot_t) );
void fme07_state_t::swap()
{
set_le16( &irq_count, irq_count );
for ( int i = 0; i < sizeof sound_state.delays / sizeof sound_state.delays [0]; i++ )
set_le16( &sound_state.delays [i], sound_state.delays [i] );
}
class Mapper_Fme07 : public Nes_Mapper, fme07_state_t {
nes_time_t last_time;
Nes_Fme07_Apu sound;
public:
Mapper_Fme07()
{
fme07_state_t* state = this;
register_state( state, sizeof *state );
}
virtual int channel_count() const { return sound.osc_count; }
virtual void set_channel_buf( int i, Blip_Buffer* b ) { sound.osc_output( i, b ); }
virtual void set_treble( blip_eq_t const& eq ) { sound.treble_eq( eq ); }
virtual void reset_state()
{
regs [8] = 0x40; // wram disabled
irq_count = 0xFFFF;
sound.reset();
}
virtual void save_state( mapper_state_t& out )
{
sound.save_snapshot( &sound_state );
fme07_state_t::swap();
Nes_Mapper::save_state( out );
fme07_state_t::swap(); // to do: kind of hacky to swap in place
}
virtual void read_state( mapper_state_t const& in )
{
Nes_Mapper::read_state( in );
fme07_state_t::swap();
sound.load_snapshot( sound_state );
}
void write_register( int index, int data );
virtual void apply_mapping()
{
last_time = 0;
for ( int i = 0; i < sizeof regs; i++ )
write_register( i, regs [i] );
}
virtual void run_until( nes_time_t end_time )
{
int new_count = irq_count - (end_time - last_time);
last_time = end_time;
if ( new_count <= 0 && (irq_mode & 0x81) == 0x81 )
irq_pending = true;
if ( irq_mode & 0x01 )
irq_count = new_count & 0xFFFF;
}
virtual nes_time_t next_irq( nes_time_t present )
{
if ( irq_pending )
return 0;
if ( (irq_mode & 0x81) == 0x81 )
return last_time + irq_count + 1;
return no_irq;
}
virtual void end_frame( nes_time_t end_time )
{
if ( end_time > last_time )
run_until( end_time );
last_time -= end_time;
assert( last_time >= 0 );
sound.end_frame( end_time );
}
void write_irq( nes_time_t, int index, int data );
virtual void write( nes_time_t time, nes_addr_t addr, int data )
{
switch ( addr & 0xE000 )
{
case 0x8000:
command = data & 0x0F;
break;
case 0xA000:
if ( command < 0x0D )
write_register( command, data );
else
write_irq( time, command, data );
break;
case 0xC000:
sound.write_latch( data );
break;
case 0xE000:
sound.write_data( time, data );
break;
}
}
};
void Mapper_Fme07::write_irq( nes_time_t time, int index, int data )
{
run_until( time );
switch ( index )
{
case 0x0D:
irq_mode = data;
if ( (irq_mode & 0x81) != 0x81 )
irq_pending = false;
break;
case 0x0E:
irq_count = (irq_count & 0xFF00) | data;
break;
case 0x0F:
irq_count = data << 8 | (irq_count & 0xFF);
break;
}
if ( (irq_mode & 0x81) == 0x81 )
irq_changed();
}
void Mapper_Fme07::write_register( int index, int data )
{
regs [index] = data;
int prg_bank = index - 0x09;
if ( (unsigned) prg_bank < 3 ) // most common
{
set_prg_bank( 0x8000 | (prg_bank << bank_8k), bank_8k, data );
}
else if ( index == 0x08 )
{
enable_sram( (data & 0xC0) == 0xC0 );
if ( !(data & 0xC0) )
set_prg_bank( 0x6000, bank_8k, data & 0x3F );
}
else if ( index < 0x08 )
{
set_chr_bank( index * 0x400, bank_1k, data );
}
else
{
assert( index == 0x0C );
if ( data & 2 )
mirror_single( data & 1 );
else if ( data & 1 )
mirror_horiz();
else
mirror_vert();
}
}
void register_fme07_mapper();
void register_fme07_mapper()
{
register_mapper<Mapper_Fme07>( 69 );
}

View File

@ -1,125 +0,0 @@
// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/
#include "Nes_Apu.h"
#include "Nes_State.h"
/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
template<int mode>
struct apu_reflection
{
#define REFLECT( apu, state ) (mode ? void (apu = state) : void (state = apu))
static void reflect_env( apu_state_t::env_t& state, Nes_Envelope& osc )
{
REFLECT( state [0], osc.env_delay );
REFLECT( state [1], osc.envelope );
REFLECT( state [2], osc.reg_written [3] );
}
static void reflect_square( apu_state_t::square_t& state, Nes_Square& osc )
{
reflect_env( state.env, osc );
REFLECT( state.delay, osc.delay );
REFLECT( state.length, osc.length_counter );
REFLECT( state.phase, osc.phase );
REFLECT( state.swp_delay, osc.sweep_delay );
REFLECT( state.swp_reset, osc.reg_written [1] );
}
static void reflect_triangle( apu_state_t::triangle_t& state, Nes_Triangle& osc )
{
REFLECT( state.delay, osc.delay );
REFLECT( state.length, osc.length_counter );
REFLECT( state.linear_counter, osc.linear_counter );
REFLECT( state.linear_mode, osc.reg_written [3] );
}
static void reflect_noise( apu_state_t::noise_t& state, Nes_Noise& osc )
{
reflect_env( state.env, osc );
REFLECT( state.delay, osc.delay );
REFLECT( state.length, osc.length_counter );
REFLECT( state.shift_reg, osc.noise );
}
static void reflect_dmc( apu_state_t::dmc_t& state, Nes_Dmc& osc )
{
REFLECT( state.delay, osc.delay );
REFLECT( state.remain, osc.length_counter );
REFLECT( state.buf, osc.buf );
REFLECT( state.bits_remain, osc.bits_remain );
REFLECT( state.bits, osc.bits );
REFLECT( state.buf_empty, osc.buf_empty );
REFLECT( state.silence, osc.silence );
REFLECT( state.irq_flag, osc.irq_flag );
if ( mode )
state.addr = osc.address | 0x8000;
else
osc.address = state.addr & 0x7fff;
}
};
void Nes_Apu::get_state( apu_state_t* state ) const
{
for ( int i = 0; i < osc_count * 4; i++ )
state->w40xx [i] = oscs [i >> 2]->regs [i & 3];
state->w40xx [0x11] = dmc.dac;
state->w4015 = osc_enables;
state->w4017 = frame_mode;
state->delay = frame_delay;
state->step = frame;
state->irq_flag = irq_flag;
typedef apu_reflection<1> refl;
Nes_Apu& apu = *(Nes_Apu*) this; // const_cast
refl::reflect_square ( state->square1, apu.square1 );
refl::reflect_square ( state->square2, apu.square2 );
refl::reflect_triangle( state->triangle, apu.triangle );
refl::reflect_noise ( state->noise, apu.noise );
refl::reflect_dmc ( state->dmc, apu.dmc );
}
void Nes_Apu::set_state( apu_state_t const& state )
{
reset();
write_register( 0, 0x4017, state.w4017 );
write_register( 0, 0x4015, state.w4015 );
for ( int i = 0; i < osc_count * 4; i++ )
{
int n = state.w40xx [i];
oscs [i >> 2]->regs [i & 3] = n;
write_register( 0, 0x4000 + i, n );
}
frame_delay = state.delay;
frame = state.step;
irq_flag = state.irq_flag;
typedef apu_reflection<0> refl;
apu_state_t& st = (apu_state_t&) state; // const_cast
refl::reflect_square ( st.square1, square1 );
refl::reflect_square ( st.square2, square2 );
refl::reflect_triangle( st.triangle, triangle );
refl::reflect_noise ( st.noise, noise );
refl::reflect_dmc ( st.dmc, dmc );
dmc.recalc_irq();
dmc.last_amp = dmc.dac;
}

View File

@ -1,113 +0,0 @@
#include "Nes_Blitter.h"
#include "blargg_endian.h"
#include "blargg_source.h"
#ifndef NES_BLITTER_OUT_DEPTH
#define NES_BLITTER_OUT_DEPTH 16
#endif
Nes_Blitter::Nes_Blitter() { ntsc = 0; }
Nes_Blitter::~Nes_Blitter() { free( ntsc ); }
blargg_err_t Nes_Blitter::init()
{
assert( !ntsc );
CHECK_ALLOC( ntsc = (nes_ntsc_emph_t*) malloc( sizeof *ntsc ) );
static setup_t const s = { };
setup_ = s;
setup_.ntsc = nes_ntsc_composite;
return setup( setup_ );
}
blargg_err_t Nes_Blitter::setup( setup_t const& s )
{
setup_ = s;
chunk_count = ((Nes_Emu::image_width - setup_.crop.left - setup_.crop.right) + 5) / 6;
height = Nes_Emu::image_height - setup_.crop.top - setup_.crop.bottom;
nes_ntsc_init_emph( ntsc, &setup_.ntsc );
return 0;
}
void Nes_Blitter::blit( Nes_Emu& emu, void* out, long out_pitch )
{
short const* palette = emu.frame().palette;
int burst_phase = (setup_.ntsc.merge_fields ? 0 : emu.frame().burst_phase);
long in_pitch = emu.frame().pitch;
unsigned char* in = emu.frame().pixels + setup_.crop.top * in_pitch + setup_.crop.left;
for ( int n = height; n; --n )
{
unsigned char* line_in = in;
in += in_pitch;
BOOST::uint32_t* line_out = (BOOST::uint32_t*) out;
out = (char*) out + out_pitch;
NES_NTSC_BEGIN_ROW( ntsc, burst_phase,
nes_ntsc_black, nes_ntsc_black, palette [*line_in] );
line_in [256] = 252; // loop reads 3 extra pixels, so set them to black
line_in [257] = 252;
line_in [258] = 252;
line_in++;
burst_phase = (burst_phase + 1) % nes_ntsc_burst_count;
// assemble two 16-bit pixels into a 32-bit int for better performance
#if BLARGG_BIG_ENDIAN
#define COMBINE_PIXELS right |= left << 16;
#else
#define COMBINE_PIXELS right <<= 16; right |= left;
#endif
#define OUT_PIXEL( i ) \
if ( !(i & 1) ) {\
NES_NTSC_RGB_OUT( (i % 7), left, NES_BLITTER_OUT_DEPTH );\
if ( i > 1 ) line_out [(i/2)-1] = right;\
}\
else {\
NES_NTSC_RGB_OUT( (i % 7), right, NES_BLITTER_OUT_DEPTH );\
COMBINE_PIXELS;\
}
for ( int n = chunk_count; n; --n )
{
unsigned long left, right;
NES_NTSC_COLOR_IN( 0, palette [line_in [0]] );
OUT_PIXEL( 0 );
OUT_PIXEL( 1 );
NES_NTSC_COLOR_IN( 1, palette [line_in [1]] );
OUT_PIXEL( 2 );
OUT_PIXEL( 3 );
NES_NTSC_COLOR_IN( 2, palette [line_in [2]] );
OUT_PIXEL( 4 );
OUT_PIXEL( 5 );
OUT_PIXEL( 6 );
NES_NTSC_COLOR_IN( 0, palette [line_in [3]] );
OUT_PIXEL( 7 );
OUT_PIXEL( 8 );
NES_NTSC_COLOR_IN( 1, palette [line_in [4]] );
OUT_PIXEL( 9 );
OUT_PIXEL( 10);
NES_NTSC_COLOR_IN( 2, palette [line_in [5]] );
line_in += 6;
OUT_PIXEL( 11 );
OUT_PIXEL( 12 );
OUT_PIXEL( 13 );
line_out [6] = right;
line_out += 7;
}
}
}

View File

@ -1,45 +0,0 @@
// NTSC filter for use with Nes_Emu
#ifndef NES_BLITTER_H
#define NES_BLITTER_H
#include "nes_emu/Nes_Emu.h"
#include "nes_emu/nes_ntsc.h"
class Nes_Blitter {
public:
Nes_Blitter();
~Nes_Blitter();
blargg_err_t init();
struct setup_t
{
nes_ntsc_setup_t ntsc;
struct {
// Number of NES source pixels to remove from each border.
// Resulting width will be rounded to multiple of 6 pixels due
// to internal limitations.
int left, top, right, bottom;
} crop;
};
setup_t const& setup() const { return setup_; }
blargg_err_t setup( setup_t const& );
// size of output image generated by blit()
int out_width() const { return chunk_count * 14; }
int out_height() const { return height; }
// Generate NTSC filtered image to 16-bit 565 RGB output pixels
void blit( Nes_Emu&, void* out, long pitch );
private:
setup_t setup_;
nes_ntsc_emph_t* ntsc;
int chunk_count;
int height;
};
#endif

View File

@ -1,531 +0,0 @@
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
#include "Nes_Film.h"
#include <string.h>
#include <stdlib.h>
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "blargg_source.h"
nes_tag_t const joypad_data_tag = FOUR_CHAR('JOYP');
Nes_Film::Nes_Film() { clear( 60 * 60 ); }
Nes_Film::~Nes_Film() { }
void Nes_Film::clear( frame_count_t new_period )
{
period_ = new_period;
end_ = begin_ = -invalid_frame_count;
time_offset = 0;
has_joypad_sync_ = true;
has_second_joypad = false;
data.clear( new_period );
}
inline int Nes_Film::calc_block_count( frame_count_t new_end ) const
{
// usually one block extra than absolutely needed
return (new_end - time_offset) / data.period() + 2;
}
blargg_err_t Nes_Film::resize( frame_count_t new_end )
{
blargg_err_t err = data.resize( calc_block_count( new_end ) );
if ( !err )
end_ = new_end;
return err;
}
inline int Nes_Film::calc_block( frame_count_t time, int* index_out ) const
{
assert( time_offset <= time && time <= end() );
frame_count_t rel = time - time_offset;
int block = rel / data.period();
*index_out = rel - block * data.period();
return block;
}
Nes_Film::joypad_t Nes_Film::get_joypad( frame_count_t time ) const
{
int index;
block_t const& b = data.read( calc_block( time, &index ) );
joypad_t result = b.joypad0 [index];
if ( b.joypads [1] )
result |= b.joypads [1] [index] << 8;
return result;
}
blargg_err_t Nes_Film::set_joypad( frame_count_t time, joypad_t joypad )
{
int index;
int block = calc_block( time, &index );
block_t* b = data.write( block );
CHECK_ALLOC( b );
b->joypad0 [index] = joypad & 0xFF;
int joypad2 = joypad >> 8 & 0xFF;
if ( joypad2 && !b->joypads [1] )
CHECK_ALLOC( b = data.alloc_joypad2( block ) );
if ( b->joypads [1] )
{
b->joypads [1] [index] = joypad2;
has_second_joypad = true;
}
return 0;
}
blargg_err_t Nes_Film::record_frame( frame_count_t time, joypad_t joypad, Nes_State_** out )
{
if ( out )
*out = 0;
if ( !contains( time ) )
{
require( blank() );
clear();
begin_ = end_ = time;
time_offset = time - time % period_;
}
RETURN_ERR( resize( time + 1 ) );
RETURN_ERR( set_joypad( time, joypad ) );
// first check detects stale snapshot left after trimming film
if ( read_snapshot( time ).timestamp() > time || time == begin_ || time % period_ == 0 )
{
Nes_State_* ss = modify_snapshot( time );
CHECK_ALLOC( ss );
if ( out )
*out = ss;
if ( time != begin_ )
ss->set_timestamp( invalid_frame_count ); // caller might not take snapshot
}
return 0;
}
Nes_State_ const* Nes_Film::nearest_snapshot( frame_count_t time ) const
{
require( contains( time ) );
if ( time > end() )
time = end();
for ( int i = snapshot_index( time ); i >= 0; i-- )
{
Nes_State_ const& ss = snapshots( i );
if ( ss.timestamp() <= time ) // invalid timestamp will always be greater
return &ss;
}
return 0;
}
frame_count_t Nes_Film::constrain( frame_count_t t ) const
{
if ( t != invalid_frame_count && !blank() )
{
if ( t < begin_ ) t = begin_;
if ( t > end_ ) t = end_;
}
return t;
}
inline bool Nes_Film::contains_range( frame_count_t first, frame_count_t last ) const
{
return begin_ <= first && first <= last && last <= end_;
}
void Nes_Film::trim( frame_count_t first, frame_count_t last )
{
check( begin() <= first && first <= last && last <= end() );
// TODO: this routine was broken; check thoroughly
if ( first > begin_ )
begin_ = first;
// preserve first snapshot, which might be before beginning
int first_block = (begin_ - time_offset) / data.period();
if ( first_block > 0 )
{
// TODO: pathological thrashing still possible
Nes_State_ const* ss = nearest_snapshot( begin_ );
if ( ss )
first_block = (ss->timestamp() - time_offset) / data.period();
time_offset += first_block * data.period();
}
if ( begin_ <= last && last < end_ )
end_ = last;
data.trim( first_block, calc_block_count( end_ ) );
// be sure snapshot for beginning was preserved
assert( nearest_snapshot( begin_ ) );
}
// Nes_Film_Joypad_Scanner
// Simplifies scanning joypad data
class Nes_Film_Joypad_Scanner {
public:
// Begin scanning range and set public members for first block
Nes_Film_Joypad_Scanner( frame_count_t first, frame_count_t last, Nes_Film const& );
int block; // block index
int offset; // offset in data
int count; // number of bytes
frame_count_t remain; // number of bytes remaining to scan
// Pointer to temporary buffer of 'block_period' bytes. Cleared
// to zero before first use.
unsigned char* buf();
// Go to next block. False if no more blocks.
bool next();
~Nes_Film_Joypad_Scanner();
private:
Nes_Film& film;
unsigned char* buf_;
void recalc_count();
};
inline unsigned char* Nes_Film_Joypad_Scanner::buf()
{
if ( !buf_ )
buf_ = (unsigned char*) calloc( 1, film.data.period() );
return buf_;
}
inline void Nes_Film_Joypad_Scanner::recalc_count()
{
count = film.data.period() - offset;
if ( count > remain )
count = remain;
}
Nes_Film_Joypad_Scanner::Nes_Film_Joypad_Scanner( frame_count_t first, frame_count_t last,
Nes_Film const& f ) : film( *(Nes_Film*) &f )
{
buf_ = 0;
remain = last - first;
block = film.calc_block( first, &offset );
recalc_count();
film.data.joypad_only( true );
}
Nes_Film_Joypad_Scanner::~Nes_Film_Joypad_Scanner()
{
film.data.joypad_only( false );
free( buf_ );
}
bool Nes_Film_Joypad_Scanner::next()
{
block++;
offset = 0;
remain -= count;
if ( remain <= 0 )
return false;
recalc_count();
return true;
}
// Nes_Film_Writer
blargg_err_t Nes_Film_Writer::end( Nes_Film const& film, frame_count_t first,
frame_count_t last, frame_count_t period )
{
RETURN_ERR( film.write_blocks( *this, first, last, period ) );
return Nes_File_Writer::end();
}
blargg_err_t Nes_Film::write( Auto_File_Writer out, frame_count_t first,
frame_count_t last, frame_count_t period ) const
{
Nes_Film_Writer writer;
RETURN_ERR( writer.begin( out ) );
return writer.end( *this, first, last, (period ? period : this->period()) );
}
static blargg_err_t write_state( Nes_State_ const& ss, Nes_File_Writer& out )
{
RETURN_ERR( out.begin_group( state_file_tag ) );
RETURN_ERR( ss.write_blocks( out ) );
return out.end_group();
}
blargg_err_t Nes_Film::write_blocks( Nes_File_Writer& out, frame_count_t first,
frame_count_t last, frame_count_t period ) const
{
require( contains_range( first, last ) );
require( nearest_snapshot( first ) );
frame_count_t first_snapshot = nearest_snapshot( first )->timestamp();
assert( first_snapshot <= first );
// write info block
movie_info_t info;
memset( &info, 0, sizeof info );
info.begin = first;
info.length = last - first;
info.extra = first - first_snapshot;
info.period = period;
info.has_joypad_sync = has_joypad_sync_;
info.joypad_count = 1;
if ( has_second_joypad )
{
// Scan second joypad data for any blocks containing non-zero data
Nes_Film_Joypad_Scanner joypad( first, last, *this );
do
{
block_t const& b = data.read( joypad.block );
if ( b.joypads [1] &&
mem_differs( &b.joypads [1] [joypad.offset], 0, joypad.count ) )
{
info.joypad_count = 2;
break;
}
}
while ( joypad.next() );
}
RETURN_ERR( write_nes_state( out, info ) );
// write joypad data
for ( int i = 0; i < info.joypad_count; i++ )
{
Nes_Film_Joypad_Scanner joypad( first_snapshot, last, *this );
RETURN_ERR( out.write_block_header( joypad_data_tag, joypad.remain ) );
do
{
block_t const& b = data.read( joypad.block );
byte const* data = b.joypads [i];
if ( !data )
CHECK_ALLOC( data = joypad.buf() );
RETURN_ERR( out.write( &data [joypad.offset], joypad.count ) );
}
while ( joypad.next() );
}
// write first state
int index = snapshot_index( first_snapshot );
assert( snapshots( index ).timestamp() == first_snapshot );
RETURN_ERR( write_state( snapshots( index ), out ) );
// write snapshots that fall within output periods
// TODO: thorougly verify this tricky algorithm
//dprintf( "last: %6d\n", last );
int last_index = snapshot_index( last );
frame_count_t time = first_snapshot + period;
for ( ; ++index <= last_index; )
{
Nes_State_ const& ss = snapshots( index );
frame_count_t t = ss.timestamp();
if ( t != invalid_frame_count )
{
while ( time + period <= t )
time += period;
if ( t >= time - period )
{
time += period;
//dprintf( "time: %6d\n", t );
RETURN_ERR( write_state( ss, out ) );
}
}
}
return 0;
}
// Nes_Film_Reader
blargg_err_t Nes_Film::read( Auto_File_Reader in )
{
Nes_Film_Reader reader;
RETURN_ERR( reader.begin( in, this ) );
while ( !reader.done() )
RETURN_ERR( reader.next_block() );
return 0;
}
Nes_Film_Reader::Nes_Film_Reader()
{
film = 0;
info_ptr = 0;
joypad_count = 0;
film_initialized = false;
memset( &info_, 0, sizeof info_ );
}
Nes_Film_Reader::~Nes_Film_Reader() { }
blargg_err_t Nes_Film_Reader::begin( Auto_File_Reader dr, Nes_Film* nf )
{
film = nf;
RETURN_ERR( Nes_File_Reader::begin( dr ) );
if ( block_tag() != movie_file_tag )
return "Not a movie file";
return 0;
}
blargg_err_t Nes_Film::begin_read( movie_info_t const& info )
{
begin_ = info.begin - info.extra;
end_ = info.begin + info.length;
time_offset = begin_ - begin_ % period_;
has_joypad_sync_ = info.has_joypad_sync;
assert( begin_ <= end_ );
return resize( end_ );
}
blargg_err_t Nes_Film_Reader::customize()
{
require( info_ptr );
if ( film_initialized )
return 0;
film_initialized = true;
film->clear();
return film->begin_read( info_ );
}
blargg_err_t Nes_Film_Reader::next_block()
{
blargg_err_t err = next_block_();
if ( err )
film->clear(); // don't leave film in inconsistent state when reading fails
return err;
}
blargg_err_t Nes_Film_Reader::next_block_()
{
for ( ; ; )
{
RETURN_ERR( Nes_File_Reader::next_block() );
switch ( depth() == 0 ? block_tag() : 0 )
{
case movie_info_t::tag:
check( !info_ptr );
RETURN_ERR( read_nes_state( *this, &info_ ) );
info_ptr = &info_;
return 0;
case joypad_data_tag:
RETURN_ERR( customize() );
RETURN_ERR( film->read_joypad( *this, joypad_count++ ) );
break;
case state_file_tag:
RETURN_ERR( customize() );
RETURN_ERR( film->read_snapshot( *this ) );
break;
default:
if ( done() )
{
// at least first snapshot must have been read
check( film->read_snapshot( film->begin_ ).timestamp() != invalid_frame_count );
film->begin_ += info_.extra; // bump back to claimed beginning
// todo: remove
#if !defined (NDEBUG) && 0
FILE* out = fopen( "raw_block", "wb" );
int block_count = (film->end() - film->time_offset) / film->data.period() + 1;
//for ( int i = 0; i < block_count; i++ )
int i = (block_count > 1);
fwrite( &film->data.read( i ), offsetof (Nes_Film_Data::block_t,joypad0 [film->data.period()]), 1, out );
fclose( out );
#endif
}
return 0;
}
}
}
blargg_err_t Nes_Film::read_joypad( Nes_File_Reader& in, int index )
{
check( index <= 1 );
if ( index <= 1 )
{
Nes_Film_Joypad_Scanner joypad( begin_, end_, *this );
do
{
block_t* b = data.write( joypad.block );
CHECK_ALLOC( b );
byte* p = b->joypads [index];
if ( !p )
CHECK_ALLOC( p = joypad.buf() );
p += joypad.offset;
RETURN_ERR( in.read( p, joypad.count ) );
if ( !b->joypads [index] && mem_differs( p, 0, joypad.count ) )
{
// non-zero joypad2 data
CHECK_ALLOC( b = data.alloc_joypad2( joypad.block ) );
memcpy( &b->joypads [index] [joypad.offset], p, joypad.count );
has_second_joypad = true;
}
}
while ( joypad.next() );
}
return 0;
}
blargg_err_t Nes_Film::read_snapshot( Nes_File_Reader& in )
{
RETURN_ERR( in.enter_group() );
// read snapshot's timestamp
nes_state_t info;
memset( &info, 0, sizeof info );
for ( ; ; )
{
RETURN_ERR( in.next_block() );
if ( in.block_tag() == info.tag )
{
RETURN_ERR( read_nes_state( in, &info ) );
break;
}
check( false ); // shouldn't encounter any unknown blocks
}
frame_count_t time = info.frame_count;
if ( !contains( time ) )
{
check( false );
}
else
{
// read snapshot only if it's earlier than any existing snapshot in same segment
Nes_State_* ss = modify_snapshot( time );
CHECK_ALLOC( ss );
// uninitialized snapshot's time is large positive value so always compares greater
if ( time < ss->timestamp() )
{
// read new snapshot
ss->clear();
ss->set_nes_state( info );
do
{
RETURN_ERR( ss->read_blocks( in ) );
}
while ( in.block_type() != in.group_end );
}
}
return in.exit_group();
}

View File

@ -1,214 +0,0 @@
// Film to record NES movies on using Nes_Recorder
// Nes_Emu 0.7.0
#ifndef NES_FILM_H
#define NES_FILM_H
#include "blargg_common.h"
#include "Nes_Film_Data.h"
// See below for custom reader and writer classes that allow user data in movie files
class Nes_Film_Writer;
class Nes_Film_Reader;
class Nes_Film {
public:
Nes_Film();
~Nes_Film();
// Clear film to blankness
void clear() { clear( period() ); }
// Time of first recorded snapshot
frame_count_t begin() const { return begin_; }
// Time of *end* of last recorded frame
frame_count_t end() const { return end_; }
// Number of frames in recording
frame_count_t length() const { return end() - begin(); }
// Trim to subset of recording. OK if new_begin == new_end, which does
// not make film blank, merely of zero length.
void trim( frame_count_t new_begin, frame_count_t new_end );
// Write entire recording to file
blargg_err_t write( Auto_File_Writer ) const;
// Read entire recording from file
blargg_err_t read( Auto_File_Reader );
// Additional features
// Write trimmed recording to file, with snapshots approximately every 'period' frames
blargg_err_t write( Auto_File_Writer, frame_count_t begin, frame_count_t end,
frame_count_t period = 0 ) const;
// Clear film and set how often snapshots are taken. One snapshot is kept for
// every 'period' frames of recording. A lower period makes seeking faster but
// uses more memory.
void clear( frame_count_t new_period );
// Average number of frames between snapshots
frame_count_t period() const { return period_; }
// True if film has just been cleared
bool blank() const { return end_ < 0; }
// True if timestamp is within recording. Always false if film is blank.
bool contains( frame_count_t t ) const { return begin() <= t && t <= end(); }
// True if recording contains frame beginning at timestamp
bool contains_frame( frame_count_t t ) const { return begin() <= t && t < end(); }
// Constrain timestamp to recorded range, or return unchanged if film is blank
frame_count_t constrain( frame_count_t ) const;
// Raw access for use by Nes_Recorder
// True if joypad entries are 0xFF on frames that the joypad isn't read
bool has_joypad_sync() const { return has_joypad_sync_; }
// Snapshot that might have current timestamp
Nes_State_ const& read_snapshot( frame_count_t ) const;
// Snapshot that current timestamp maps to. NULL if out of memory.
Nes_State_* modify_snapshot( frame_count_t );
// Pointer to nearest snapshot at or before timestamp, or NULL if none
Nes_State_ const* nearest_snapshot( frame_count_t ) const;
typedef unsigned long joypad_t;
// Get joypad data for frame beginning at timestamp
joypad_t get_joypad( frame_count_t ) const;
// Change joypad data for frame beginning at timestamp. Frame must already have
// been recorded normally.
blargg_err_t set_joypad( frame_count_t, joypad_t );
// Record new frame beginning at timestamp using joypad data. Returns
// pointer where snapshot should be saved to, or NULL if a snapshot isn't
// needed for this timestamp. Removes anything recorded after frame.
blargg_err_t record_frame( frame_count_t, joypad_t joypad, Nes_State_** out = 0 );
private:
// noncopyable
Nes_Film( Nes_Film const& );
Nes_Film& operator = ( Nes_Film const& );
typedef Nes_Film_Data::block_t block_t;
Nes_Film_Data data;
frame_count_t begin_;
frame_count_t end_;
frame_count_t period_;
frame_count_t time_offset;
bool has_joypad_sync_;
bool has_second_joypad;
int calc_block( frame_count_t time, int* index_out ) const;
int snapshot_index( frame_count_t ) const;
Nes_State_ const& snapshots( int ) const;
int calc_block_count( frame_count_t new_end ) const;
blargg_err_t resize( frame_count_t new_end );
bool contains_range( frame_count_t first, frame_count_t last ) const;
blargg_err_t write_blocks( Nes_File_Writer&, frame_count_t first,
frame_count_t last, frame_count_t period ) const;
blargg_err_t begin_read( movie_info_t const& );
blargg_err_t read_joypad( Nes_File_Reader&, int index );
blargg_err_t read_snapshot( Nes_File_Reader& );
friend class Nes_Film_Reader;
friend class Nes_Film_Writer;
friend class Nes_Film_Joypad_Scanner;
};
// Allows user data blocks to be written with film
class Nes_Film_Writer : public Nes_File_Writer {
public:
// Begin writing movie file
blargg_err_t begin( Auto_File_Writer );
// End writing movie file. Optionally specify custom period and subset of
// recording to write.
blargg_err_t end( Nes_Film const& );
blargg_err_t end( Nes_Film const&, frame_count_t first, frame_count_t last,
frame_count_t period );
};
// Allows film information to be checked before loading film, and for handling
// of user data blocks.
class Nes_Film_Reader : public Nes_File_Reader {
public:
Nes_Film_Reader();
~Nes_Film_Reader();
// Begin reading from movie file. Does not modify film until later (see below).
blargg_err_t begin( Auto_File_Reader, Nes_Film* out );
// Go to next custom block in file
blargg_err_t next_block();
// Information about film (see nes_state.h for fields). Returns zero
// until information is encountered in file. Once return value becomes
// non-zero, next call to next_block() will read movie into film.
// Until that time, film is not modified or examined at all.
movie_info_t const* info() const { return info_ptr; }
// to do: allow reading subset of recording from file (for example, last 5 minutes)
private:
Nes_Film* film;
bool film_initialized;
movie_info_t info_;
movie_info_t* info_ptr;
int joypad_count;
blargg_err_t customize();
blargg_err_t next_block_();
};
inline blargg_err_t Nes_Film_Writer::begin( Auto_File_Writer dw )
{
return Nes_File_Writer::begin( dw, movie_file_tag );
}
inline blargg_err_t Nes_Film::write( Auto_File_Writer out ) const
{
return write( out, begin(), end(), period() );
}
inline blargg_err_t Nes_Film_Writer::end( Nes_Film const& film )
{
return end( film, film.begin(), film.end(), film.period() );
}
inline int Nes_Film::snapshot_index( frame_count_t time ) const
{
return (time - time_offset) / period_;
}
inline Nes_State_ const& Nes_Film::snapshots( int i ) const
{
return data.read( (unsigned) i / data.block_size ).states [(unsigned) i % data.block_size];
}
inline Nes_State_ const& Nes_Film::read_snapshot( frame_count_t time ) const
{
return snapshots( snapshot_index( time ) );
}
inline Nes_State_* Nes_Film::modify_snapshot( frame_count_t time )
{
int i = snapshot_index( time );
block_t* b = data.write( (unsigned) i / data.block_size );
if ( b )
return &b->states [(unsigned) i % data.block_size];
return 0;
}
#endif

View File

@ -1,273 +0,0 @@
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
#include "Nes_Film_Data.h"
#include <stdlib.h>
#include <string.h>
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "blargg_source.h"
Nes_Film_Data::Nes_Film_Data()
{
blocks = 0;
active = 0;
block_count = 0;
period_ = 0;
packer = 0;
BOOST_STATIC_ASSERT( sizeof active->cpu [0] % 4 == 0 );
BOOST_STATIC_ASSERT( sizeof active->joypad [0] % 4 == 0 );
BOOST_STATIC_ASSERT( sizeof active->apu [0] % 4 == 0 );
BOOST_STATIC_ASSERT( sizeof active->ppu [0] % 4 == 0 );
BOOST_STATIC_ASSERT( sizeof active->mapper [0] % 4 == 0 );
BOOST_STATIC_ASSERT( sizeof active->states [0] % 4 == 0 );
//BOOST_STATIC_ASSERT( offsetof (block_t,joypad0) % 4 == 0 ); // XXX
}
#ifndef NDEBUG
static void write_file( void const* in, long size, const char* path )
{
FILE* out = fopen( path, "wb" );
if ( out )
{
fwrite( in, size, 1, out );
fclose( out );
}
}
void Nes_Film_Data::debug_packer() const
{
comp_block_t* b = blocks [active_index];
static byte* temp = new byte [active_size() * 2];
for ( int i = 0; i < active_size() * 2; i++ )
temp [i] = i;
long vs = packer->unpack( b->data, b->size, temp );
if ( vs != active_size() - b->offset )
{
dprintf( "Unpacked size differs\n" );
write_file( (byte*) active + b->offset, vs, "original" );
write_file( temp, vs, "error" );
assert( false );
}
if ( memcmp( (byte*) active + b->offset, temp, vs ) )
{
dprintf( "Unpacked content differs\n" );
write_file( (byte*) active + b->offset, vs, "original" );
write_file( temp, vs, "error" );
assert( false );
}
if ( 0 )
{
long total = 0;
for ( int i = 0; i < block_count; i++ )
if ( blocks [i] )
total += blocks [i]->size;
//dprintf( "Compression: %ld%%\n", total * 100 / (block_count * (active_size() - period_)) );
dprintf( "Memory: %ld+%ldK\n", total / 1024, active_size() * 2 / 1024 + 16 );
//dprintf( "Memory: %ldK\n", (total + active_size() * 2) / 1024 + 16 );
}
}
#endif
void Nes_Film_Data::flush_active() const
{
if ( active_dirty )
{
assert( (unsigned) active_index < (unsigned) block_count );
active_dirty = false;
comp_block_t* b = blocks [active_index];
assert( b && !b->size ); // should have been reallocated in write()
check( b->offset == joypad_only_ );
b->size = packer->pack( (byte*) active + b->offset, active_size() - b->offset, b->data );
assert( b->size <= packer->worst_case( active_size() - b->offset ) );
// shrink allocation
void* mem = realloc( b, offsetof (comp_block_t, data) + b->size * sizeof(b->data[0]) );
if ( mem )
blocks [active_index] = (comp_block_t*) mem;
else
check( false ); // shrink shouldn't fail, but fine if it does
#ifndef NDEBUG
debug_packer();
#endif
}
active_index = -1;
}
void Nes_Film_Data::init_states() const
{
memset( active->states, 0, sizeof active->states );
block_t* b = active;
b->garbage0 = 1;
b->garbage1 = 2;
b->garbage2 = 3;
for ( int j = 0; j < block_size; j++ )
{
Nes_State_& s = b->states [j];
s.cpu = &b->cpu [j];
s.joypad = &b->joypad [j];
s.apu = &b->apu [j];
s.ppu = &b->ppu [j];
s.mapper = &b->mapper [j];
s.ram = b->ram [j];
s.sram = b->sram [j];
s.spr_ram = b->spr_ram [j];
s.nametable = b->nametable [j];
s.chr = b->chr [j];
s.set_timestamp( invalid_frame_count );
}
}
void Nes_Film_Data::access( index_t i ) const
{
assert( (unsigned) i < (unsigned) block_count );
if ( active_dirty )
flush_active();
active_index = i;
comp_block_t* b = blocks [i];
if ( b )
{
assert( b->size );
long size = packer->unpack( b->data, b->size, (byte*) active + b->offset );
assert( b->offset + size == active_size() );
if ( b->offset )
init_states();
}
else
{
active->joypads [0] = &active->joypad0 [0];
active->joypads [1] = &active->joypad0 [period_];
init_states();
memset( active->joypad0, 0, period_ * 2 );
}
}
Nes_Film_Data::block_t* Nes_Film_Data::write( int i )
{
require( (unsigned) i < (unsigned) block_count );
if ( i != active_index )
access( i );
if ( !active_dirty )
{
// preallocate now to avoid losing write when flushed later
long size = packer->worst_case( active_size() - joypad_only_ );
comp_block_t* new_mem = (comp_block_t*) realloc( blocks [i], size );
if ( !new_mem )
return 0;
new_mem->size = 0;
new_mem->offset = joypad_only_;
blocks [i] = new_mem;
active_dirty = true;
}
return active;
}
void Nes_Film_Data::joypad_only( bool b )
{
flush_active();
joypad_only_ = b * offsetof (block_t,joypad0);
}
blargg_err_t Nes_Film_Data::resize( int new_count )
{
if ( new_count < block_count )
{
assert( active );
if ( active_index >= new_count )
flush_active();
for ( int i = new_count; i < block_count; i++ )
free( blocks [i] );
block_count = new_count;
void* new_blocks = realloc( blocks, new_count * sizeof *blocks );
if ( new_blocks || !new_count )
blocks = (comp_block_t**) new_blocks;
else
check( false ); // shrink shouldn't fail, but fine if it does
}
else if ( new_count > block_count )
{
if ( !packer )
CHECK_ALLOC( packer = BLARGG_NEW Nes_Film_Packer );
if ( !active )
{
assert( period_ );
active = (block_t*) calloc( active_size(), 1 );
CHECK_ALLOC( active );
//init_active(); // TODO: unnecessary since it's called on first access anyway?
packer->prepare( active, active_size() );
}
void* new_blocks = realloc( blocks, new_count * sizeof *blocks );
CHECK_ALLOC( new_blocks );
blocks = (comp_block_t**) new_blocks;
for ( int i = block_count; i < new_count; i++ )
blocks [i] = 0;
block_count = new_count;
}
return 0;
}
void Nes_Film_Data::clear( frame_count_t period )
{
active_index = -1;
active_dirty = false;
if ( resize( 0 ) )
check( false ); // shrink should never fail
joypad_only_ = false;
period_ = period * block_size;
free( active );
active = 0;
}
void Nes_Film_Data::trim( int begin, int new_count )
{
require( 0 <= begin && begin + new_count <= block_count );
require( (unsigned) new_count <= (unsigned) block_count );
if ( (unsigned) new_count < (unsigned) block_count )
{
if ( begin || active_index >= new_count )
flush_active();
if ( begin )
{
for ( int i = 0; i < begin; i++ )
free( blocks [i] );
memmove( &blocks [0], &blocks [begin], (block_count - begin) * sizeof *blocks );
block_count -= begin;
}
if ( resize( new_count ) )
check( false ); // shrink should never fail
}
}
Nes_Film_Data::~Nes_Film_Data()
{
if ( resize( 0 ) )
check( false ); // shrink should never fail
free( active );
delete packer;
}

View File

@ -1,86 +0,0 @@
// Film data manager that keeps data compressed in memory
// Nes_Emu 0.7.0
#ifndef NES_FILM_DATA_H
#define NES_FILM_DATA_H
#include "Nes_State.h"
#include "Nes_Film_Packer.h"
class Nes_Film_Data {
public:
Nes_Film_Data();
~Nes_Film_Data();
void clear( frame_count_t period );
frame_count_t period() const { return period_; }
blargg_err_t resize( int new_count );
void trim( int begin, int end );
enum { block_size = 8 }; // 16 helps compression but doubles temp buffer size
typedef int index_t;
struct block_t
{
BOOST::uint8_t* joypads [2];
Nes_State_ states [block_size];
Nes_Cpu::registers_t cpu [block_size];
joypad_state_t joypad [block_size];
apu_state_t apu [block_size];
ppu_state_t ppu [block_size];
mapper_state_t mapper [block_size];
BOOST::uint8_t spr_ram [block_size] [Nes_State_::spr_ram_size];
BOOST::uint8_t ram [block_size] [Nes_State_::ram_size];
BOOST::uint8_t nametable [block_size] [Nes_State_::nametable_max];
BOOST::uint32_t garbage0;
BOOST::uint8_t chr [block_size] [Nes_State_::chr_max];
BOOST::uint32_t garbage1;
BOOST::uint8_t sram [block_size] [Nes_State_::sram_max];
BOOST::uint32_t garbage2;
// garbage values prevent matches in compressor from being longer than 256K
BOOST::uint8_t joypad0 [60 * 60L * 60L];
};
block_t const& read( index_t ) const;
block_t* write( index_t ); // NULL if out of memory
block_t* alloc_joypad2( index_t i ) { return write( i ); }
void joypad_only( bool );
private:
struct comp_block_t
{
long size;
long offset;
BOOST::uint8_t data [1024 * 1024L];
};
comp_block_t** blocks;
int block_count;
frame_count_t period_;
block_t* active;
mutable int active_index;
mutable bool active_dirty;
long joypad_only_;
Nes_Film_Packer* packer;
void debug_packer() const;
void flush_active() const;
void init_active() const;
void init_states() const;
void invalidate_active();
void access( index_t ) const;
// must be multiple of 4 for packer
long active_size() const { return (offsetof (block_t,joypad0) + 3) & ~3; }
};
inline Nes_Film_Data::block_t const& Nes_Film_Data::read( int i ) const
{
//assert( blocks [i] ); // catch reads of uninitialized blocks
if ( i != active_index )
access( i );
return *active;
}
#endif

View File

@ -1,220 +0,0 @@
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
#include "Nes_Film_Packer.h"
#include <string.h>
/* Copyright (C) 2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "blargg_source.h"
#ifdef BLARGG_ENABLE_OPTIMIZER
#include BLARGG_ENABLE_OPTIMIZER
#endif
// - On my 400 MHz PowerPC G3, pack() = 230MB/sec, unpack() = 320MB/sec.
// - All 32-bit accessess are on 4-byte boundaries of the input/output buffers.
// - This would not make a good general-purpose compressor because the match
// offset is limited to a multiple of 4.
#ifdef __MWERKS__
static unsigned zero = 0; // helps CodeWarrior optimizer when added to constants
#else
const unsigned zero = 0; // compile-time constant on other compilers
#endif
void Nes_Film_Packer::prepare( void const* begin, long size )
{
uint32_t const* end = (uint32_t*) ((byte*) begin + size - 4);
uint32_t const** d = dict;
for ( int n = dict_size; n--; )
*d++ = end;
uint32_t temp = 0x80000000;
assert( (BOOST::int32_t) temp < 0 ); // be sure high bit is sign
}
long Nes_Film_Packer::pack( byte const* in_begin, long in_size, byte* out_begin )
{
//memcpy( out_begin, in_begin, in_size ); return in_size;
assert( (in_size & 3) == 0 );
uint32_t const* const in_end = (uint32_t*) (in_begin + in_size);
uint32_t const* const end = in_end - 2;
uint32_t const* in = (uint32_t*) in_begin;
uint32_t* out = (uint32_t*) out_begin;
unsigned long first = *in++;
unsigned long offset;
uint32_t const* match;
unsigned long const factor = 0x100801 + zero;
// spaghetti program flow gives better efficiency
goto begin;
// match loop
do
{
if ( match [-1] != first ) break;
offset <<= 14;
if ( *match != *in ) break;
match:
// count matching words beyond the first two
unsigned long n = (byte*) end - (byte*) in;
first = *++in;
unsigned long m = *++match;
uint32_t const* start = in;
for ( n >>= 2; n; --n )
{
if ( m != first ) break;
m = *++match;
first = *++in;
}
// encode match offset and length
unsigned long length = (byte*) in - (byte*) start;
assert( 0 <= length && length <= 0xFFFF << 2 );
assert( offset >> 16 <= 0x7FFF );
offset |= length >> 2;
// check for next match
unsigned long index = (first * factor) >> dict_shift & (dict_size - 1);
match = dict [index];
*out++ = offset; // interleved write of previous match
offset = (byte*) in - (byte*) match; assert( !(offset & 3) );
if ( in >= end ) goto match_end;
++in;
dict [index] = in;
}
while ( offset < 0x20000 );
begin:
// start writing next literal
out [1] = first;
uint32_t* literal;
literal = out;
out++;
// literal loop
literal:
first = *in;
do
{
// check for match
unsigned long index = (first * factor) >> dict_shift & (dict_size - 1);
*++out = first; // interleved write of current literal
match = dict [index];
dict [index] = in + 1;
if ( in >= end ) goto literal_end;
offset = (byte*) in - (byte*) match; assert( !(offset & 3) );
++in;
if ( match [-1] != first ) goto literal;
first = *in;
}
while ( offset >= 0x20000 || *match != first );
// set length of completed literal
offset <<= 14;
*literal = (((byte*) out - (byte*) literal) >> 2) | 0x80000000;
goto match;
match_end:
// start new literal for remaining data after final match
literal = out++;
literal_end:
--out;
// write remaining data to literal
assert( in < in_end );
do
{
*++out = *in++;
}
while ( in < in_end );
*literal = (((byte*) out - (byte*) literal) >> 2) + 0x80000001;
// mark end with zero word
*++out = 0x80000000;
++out;
long out_size = (byte*) out - out_begin;
assert( (out_size & 3) == 0 );
return out_size;
}
long Nes_Film_Packer::unpack( byte const* in_begin, long in_size, byte* out_begin )
{
//memcpy( out_begin, in_begin, in_size ); return in_size;
assert( (in_size & 3) == 0 );
uint32_t const* in = (uint32_t*) in_begin;
uint32_t* out = (uint32_t*) out_begin;
long const literal_offset = 0x7FFFFFFE + zero;
long count = (BOOST::int32_t) *in++;
uint32_t const* m;
assert( count < 0 ); // first item should be literal
goto literal;
do
{
// match
do
{
assert( m - 1 >= (void*) out_begin );
assert( m - 1 < out );
unsigned long data = m [-1];
*out++ = data;
data = *m;
if ( (count &= 0xFFFF) != 0 )
{
do
{
*out++ = data;
data = *++m;
}
while ( --count );
}
count = (BOOST::int32_t) *in++;
*out++ = data;
m = out - (count >> 16);
}
while ( count >= 0 );
literal:
unsigned long data = *in++;
*out++ = data;
data = *in++;
if ( (count += literal_offset) != 0 )
{
do
{
*out++ = data;
data = *in++;
}
while ( --count );
}
count = (BOOST::int32_t) data;
m = out - (data >> 16);
}
while ( count >= 0 );
assert( count == (BOOST::int32_t) 0x80000000 );
assert( (byte*) in == in_begin + in_size );
long out_size = (byte*) out - out_begin;
assert( (out_size & 3) == 0 );
return out_size;
}

View File

@ -1,32 +0,0 @@
// Fast save state compressor/decompressor for reducing Nes_Film memory usage
// Nes_Emu 0.7.0
#ifndef NES_FILM_PACKER_H
#define NES_FILM_PACKER_H
#include "blargg_common.h"
class Nes_Film_Packer {
public:
void prepare( void const* begin, long size );
// Worst-case output size for given input size
long worst_case( long in_size ) const { return in_size + 8; }
typedef unsigned char byte;
long pack( byte const* in, long size, byte* packed_out );
long unpack( byte const* packed_in, long packed_size, byte* out );
private:
enum { dict_bits = 12 };
enum { dict_size = 1 << dict_bits };
enum { dict_shift = 32 - dict_bits };
typedef BOOST::uint32_t uint32_t;
uint32_t const* dict [dict_size];
};
#endif

View File

@ -1,122 +0,0 @@
// Nes_Emu 0.5.6. http://www.slack.net/~ant/libs/
#include "Nes_Fme07_Apu.h"
#include <string.h>
/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
void Nes_Fme07_Apu::reset()
{
last_time = 0;
for ( int i = 0; i < osc_count; i++ )
oscs [i].last_amp = 0;
fme07_snapshot_t* state = this;
memset( state, 0, sizeof *state );
}
#include BLARGG_ENABLE_OPTIMIZER
unsigned char Nes_Fme07_Apu::amp_table [16] =
{
#define ENTRY( n ) (n * amp_range) + 0.5
ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156),
ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624),
ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498),
ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000)
#undef ENTRY
};
void Nes_Fme07_Apu::run_until( blip_time_t end_time )
{
require( end_time >= last_time );
for ( int index = 0; index < osc_count; index++ )
{
int mode = regs [7] >> index;
int vol_mode = regs [010 + index];
int volume = amp_table [vol_mode & 0x0f];
if ( !oscs [index].output )
continue;
// check for unsupported mode
#ifndef NDEBUG
if ( (mode & 011) <= 001 && vol_mode & 0x1f )
dprintf( "FME07 used unimplemented sound mode: %02X, vol_mode: %02X\n",
mode, vol_mode & 0x1f );
#endif
if ( (mode & 001) | (vol_mode & 0x10) )
volume = 0; // noise and envelope aren't supported
// period
int const period_factor = 16;
unsigned period = (regs [index * 2 + 1] & 0x0f) * 0x100 * period_factor +
regs [index * 2] * period_factor;
if ( period < 50 ) // around 22 kHz
{
volume = 0;
if ( !period ) // on my AY-3-8910A, period doesn't have extra one added
period = period_factor;
}
// current amplitude
int amp = volume;
if ( !phases [index] )
amp = 0;
int delta = amp - oscs [index].last_amp;
if ( delta )
{
oscs [index].last_amp = amp;
synth.offset( last_time, delta, oscs [index].output );
}
blip_time_t time = last_time + delays [index];
if ( time < end_time )
{
Blip_Buffer* const osc_output = oscs [index].output;
int delta = amp * 2 - volume;
if ( volume )
{
do
{
delta = -delta;
synth.offset_inline( time, delta, osc_output );
time += period;
}
while ( time < end_time );
oscs [index].last_amp = (delta + volume) >> 1;
phases [index] = (delta > 0);
}
else
{
// maintain phase when silent
int count = (end_time - time + period - 1) / period;
phases [index] ^= count & 1;
time += (long) count * period;
}
}
delays [index] = time - end_time;
}
last_time = end_time;
}

View File

@ -1,133 +0,0 @@
// Sunsoft FME-07 sound emulator
// Nes_Emu 0.5.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
#ifndef NES_FME07_APU_H
#define NES_FME07_APU_H
#include "Blip_Buffer.h"
struct fme07_snapshot_t
{
enum { reg_count = 14 };
BOOST::uint8_t regs [reg_count];
BOOST::uint8_t phases [3]; // 0 or 1
BOOST::uint8_t latch;
BOOST::uint16_t delays [3]; // a, b, c
};
BOOST_STATIC_ASSERT( sizeof (fme07_snapshot_t) == 24 );
class Nes_Fme07_Apu : private fme07_snapshot_t {
public:
Nes_Fme07_Apu();
// See Nes_Apu.h for reference
void reset();
void volume( double );
void treble_eq( blip_eq_t const& );
void output( Blip_Buffer* );
enum { osc_count = 3 };
void osc_output( int index, Blip_Buffer* );
void end_frame( blip_time_t );
void save_snapshot( fme07_snapshot_t* ) const;
void load_snapshot( fme07_snapshot_t const& );
// Mask and addresses of registers
enum { addr_mask = 0xe000 };
enum { data_addr = 0xe000 };
enum { latch_addr = 0xc000 };
// (addr & addr_mask) == latch_addr
void write_latch( int );
// (addr & addr_mask) == data_addr
void write_data( blip_time_t, int data );
// End of public interface
private:
// noncopyable
Nes_Fme07_Apu( const Nes_Fme07_Apu& );
Nes_Fme07_Apu& operator = ( const Nes_Fme07_Apu& );
static unsigned char amp_table [16];
struct {
Blip_Buffer* output;
int last_amp;
} oscs [osc_count];
blip_time_t last_time;
enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff
Blip_Synth<blip_good_quality,1> synth;
void run_until( blip_time_t );
};
inline void Nes_Fme07_Apu::volume( double v )
{
synth.volume_unit( 0.38 / amp_range * v ); // to do: fine-tune
}
inline void Nes_Fme07_Apu::treble_eq( blip_eq_t const& eq )
{
synth.treble_eq( eq );
}
inline void Nes_Fme07_Apu::output( Blip_Buffer* buf )
{
for ( int i = 0; i < osc_count; i++ )
osc_output( i, buf );
}
inline void Nes_Fme07_Apu::osc_output( int i, Blip_Buffer* buf )
{
assert( (unsigned) i < osc_count );
oscs [i].output = buf;
}
inline Nes_Fme07_Apu::Nes_Fme07_Apu()
{
output( NULL );
volume( 1.0 );
reset();
}
inline void Nes_Fme07_Apu::write_latch( int data ) { latch = data; }
inline void Nes_Fme07_Apu::write_data( blip_time_t time, int data )
{
if ( (unsigned) latch >= reg_count )
{
#ifdef dprintf
dprintf( "FME07 write to %02X (past end of sound registers)\n", (int) latch );
#endif
return;
}
run_until( time );
regs [latch] = data;
}
inline void Nes_Fme07_Apu::end_frame( blip_time_t time )
{
if ( time > last_time )
run_until( time );
last_time -= time;
assert( last_time >= 0 );
}
inline void Nes_Fme07_Apu::save_snapshot( fme07_snapshot_t* out ) const
{
*out = *this;
}
inline void Nes_Fme07_Apu::load_snapshot( fme07_snapshot_t const& in )
{
reset();
fme07_snapshot_t* state = this;
*state = in;
}
#endif

View File

@ -1,157 +0,0 @@
// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/
#include "Nes_Nonlinearizer.h"
/* Library Copyright (C) 2003-2005 Shay Green. This library is free software;
you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
Nes_Nonlinearizer::Nes_Nonlinearizer() : Multi_Buffer( 1 )
{
enable_nonlinearity( true );
}
Nes_Nonlinearizer::~Nes_Nonlinearizer()
{
}
blargg_err_t Nes_Nonlinearizer::sample_rate( long rate, int msec )
{
BLARGG_RETURN_ERR( buf.sample_rate( rate, msec ) );
BLARGG_RETURN_ERR( tnd.sample_rate( rate, msec ) );
return Multi_Buffer::sample_rate( buf.sample_rate(), buf.length() );
}
void Nes_Nonlinearizer::clock_rate( long rate )
{
buf.clock_rate( rate );
tnd.clock_rate( rate );
}
void Nes_Nonlinearizer::bass_freq( int freq )
{
buf.bass_freq( freq );
tnd.bass_freq( freq );
}
void Nes_Nonlinearizer::clear()
{
accum = 0x8000;
buf.clear();
tnd.clear();
}
Nes_Nonlinearizer::channel_t Nes_Nonlinearizer::channel( int i )
{
channel_t c;
c.center = (2 <= i && i <= 4) ? &tnd : &buf;
c.left = c.center;
c.right = c.center;
return c;
}
void Nes_Nonlinearizer::end_frame( blip_time_t length, bool )
{
buf.end_frame( length );
tnd.end_frame( length );
}
long Nes_Nonlinearizer::samples_avail() const
{
return buf.samples_avail();
}
#include BLARGG_ENABLE_OPTIMIZER
void Nes_Nonlinearizer::enable_nonlinearity( bool b )
{
require( b ); // to do: implement non-linear output
double gain = 0x7fff * 0.742467605 * 1.2;
for ( int i = 0; i < half * 2; i++ )
{
int out = i << shift;
if ( i > half )
{
double n = 202.0 / (half - 1) * (i - half);
double d = 163.67 / (24329.0 / n + 100);
out = int (d * gain) + 0x8000;
}
table [i] = out;
}
}
void Nes_Nonlinearizer::make_nonlinear( long count )
{
const int zero_offset = 0x7f7f; // to do: use private constant from Blip_Buffer.h
#define ENTRY( s ) (table [((s) >> shift) & entry_mask])
BOOST::uint16_t* p = tnd.buffer_;
unsigned prev = ENTRY( accum );
long accum = this->accum;
for ( unsigned n = count; n--; )
{
accum += (long) *p - zero_offset;
if ( (accum >> shift) >= half * 2 )
{
// to do: extend table to handle overflow better
check( false ); // overflowed
accum = (half * 2 - 1) << shift;
}
unsigned entry = ENTRY( accum );
*p++ = entry - prev + zero_offset;
prev = entry;
}
this->accum = accum;
}
long Nes_Nonlinearizer::read_samples( blip_sample_t* out, long count )
{
long avail = buf.samples_avail();
assert( tnd.samples_avail() == avail );
if ( count > avail )
count = avail;
if ( count )
{
make_nonlinear( count );
Blip_Reader lin;
Blip_Reader nonlin;
int lin_bass = lin.begin( buf );
int nonlin_bass = nonlin.begin( tnd );
for ( int n = count; n--; )
{
int s = lin.read() + nonlin.read();
lin.next( lin_bass );
nonlin.next( nonlin_bass );
*out++ = s;
if ( (BOOST::int16_t) s != s )
out [-1] = 0x7FFF - (s >> 24);
}
lin.end( buf );
nonlin.end( tnd );
buf.remove_samples( count );
tnd.remove_samples( count );
}
return count;
}

View File

@ -1,44 +0,0 @@
// NES non-linear audio output
// Nes_Emu 0.5.0. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
#ifndef NES_NONLINEARIZER_H
#define NES_NONLINEARIZER_H
#include "Multi_Buffer.h"
class Nes_Nonlinearizer : public Multi_Buffer {
public:
Nes_Nonlinearizer();
~Nes_Nonlinearizer();
// Enable non-linear output
void enable_nonlinearity( bool = true );
// See Multi_Buffer.h
blargg_err_t sample_rate( long rate, int msec = blip_default_length );
Multi_Buffer::sample_rate;
void clock_rate( long );
void bass_freq( int );
void clear();
channel_t channel( int );
void end_frame( blip_time_t, bool unused = true );
long samples_avail() const;
long read_samples( blip_sample_t*, long );
// End of public interface
private:
enum { shift = 5 };
enum { half = 0x8000 >> shift };
enum { entry_mask = half * 2 - 1 };
Blip_Buffer buf;
Blip_Buffer tnd;
long accum;
BOOST::uint16_t table [half * 2];
void make_nonlinear( long );
};
#endif

View File

@ -1,419 +0,0 @@
// NES PPU register read/write and frame timing
// Nes_Emu 0.5.0. http://www.slack.net/~ant/
#include "Nes_Ppu.h"
#include <string.h>
/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
// to do: implement junk in unused bits when reading registers
// to do: put in common or something
template<class T>
inline const T& min( const T& x, const T& y )
{
if ( x < y )
return x;
return y;
}
typedef BOOST::uint8_t byte;
ppu_time_t const ppu_overclock = 3; // PPU clocks for each CPU clock
ppu_time_t const scanline_duration = 341;
ppu_time_t const t_to_v_time = 20 * scanline_duration + 293;
//ppu_time_t const t_to_v_time = 19 * scanline_duration + 330; // 322 - 339 passes test
ppu_time_t const first_scanline_time = 21 * scanline_duration + 128;
ppu_time_t const first_hblank_time = 21 * scanline_duration + 256;
void Nes_Ppu::start_frame()
{
scanline_time = first_scanline_time;
hblank_time = first_hblank_time;
next_time = t_to_v_time / ppu_overclock;
next_scanline = 0;
frame_phase = 0;
query_phase = 0;
w2003 = 0;
memset( sprite_scanlines, 64 - max_sprites, sizeof sprite_scanlines );
}
void Nes_Ppu::query_until( nes_time_t time )
{
// nothing happens until scanline 20
if ( time > 2271 )
{
// clear VBL flag and sprite hit after 20 scanlines
if ( query_phase < 1 )
{
query_phase = 1;
r2002 &= ~0xc0;
}
// update sprite hit
if ( query_phase < 2 && update_sprite_hit( time ) )
query_phase = 2;
// set VBL flag a few clocks before the end of the frame (cheap hack)
if ( query_phase < 3 && time > 29777 )
{
query_phase = 3;
r2002 |= 0x80;
}
}
}
void Nes_Ppu::end_frame( nes_time_t end_time )
{
render_until( end_time );
query_until( end_time );
// to do: remove (shows number of sprites per line graphically)
if ( false )
if ( base_pixels )
{
for ( int i = 0; i < image_height; i++ )
{
int n = sprite_scanlines [i] - (64 - max_sprites);
memset( base_pixels + 16 + i * row_bytes, (n <= 8 ? 3 : 6), n );
}
}
// dprintf( "End of frame\n" );
start_frame();
}
inline byte const* Nes_Ppu::map_chr( int addr ) const
{
return &chr_rom [map_chr_addr( addr )];
}
// Read/write
inline byte* Nes_Ppu::map_palette( int addr )
{
if ( (addr & 3) == 0 )
addr &= 0x0f; // 0x10, 0x14, 0x18, 0x1c map to 0x00, 0x04, 0x08, 0x0c
return &palette [addr & 0x1f];
}
int Nes_Ppu::read( nes_time_t time, unsigned addr )
{
// Don't catch rendering up to present since status reads don't affect
// rendering and status is often polled in a tight loop.
switch ( addr & 7 )
{
// status
case 2: {
second_write = false;
query_until( time );
int result = r2002;
r2002 &= ~0x80;
return result;
}
// sprite ram
case 4: {
int result = spr_ram [w2003];
if ( (w2003 & 3) == 2 )
result &= 0xe3;
return result;
}
// video ram
case 7: {
render_until( time ); // changes to vram_addr affect rendering
int result = r2007;
int a = vram_addr & 0x3fff;
vram_addr = a + ((w2000 & 4) ? 32 : 1);
if ( a < 0x2000 )
{
r2007 = *map_chr( a );
}
else
{
r2007 = get_nametable( a ) [a & 0x3ff];
// palette doesn't use read buffer, but it's still filled with nametable contents
if ( a >= 0x3f00 )
result = *map_palette( a );
}
return result;
}
}
return 0;
}
void Nes_Ppu::write( nes_time_t time, unsigned addr, int data )
{
if ( addr > 0x2007 )
printf( "Write to mirrored $200x\n" );
int reg = (addr & 7);
if ( reg == 0 )
{
// render only if changes to register could affect it
int new_temp = (vram_temp & ~0x0c00) | ((data & 3) * 0x400);
if ( (new_temp - vram_temp) | ((w2000 ^ data) & 0x38) )
render_until( time );
vram_temp = new_temp;
w2000 = data;
return;
}
render_until( time );
switch ( reg )
{
//case 0: // control (handled above)
case 1: // sprites, bg enable
w2001 = data;
break;
case 3: // spr addr
w2003 = data;
break;
case 4:
spr_ram [w2003] = data;
w2003 = (w2003 + 1) & 0xff;
break;
case 5:
if ( second_write ) {
vram_temp = (vram_temp & ~0x73e0) |
((data & 0xf8) << 2) | ((data & 7) << 12);
}
else {
pixel_x = data & 7;
vram_temp = (vram_temp & ~0x001f) | (data >> 3);
}
second_write ^= 1;
break;
case 6: {
unsigned old_addr = vram_addr;
if ( second_write )
{
vram_addr = vram_temp = (vram_temp & ~0x00ff) | data;
// if ( time >= 2271 )
// dprintf( "%d VRAM fine: %d, tile: %d\n",
// (int) time, int (vram_addr >> 12), int ((vram_addr >> 5) & 31) );
}
else
{
vram_temp = (vram_temp & ~0xff00) | ((data << 8) & 0x3f00);
}
second_write ^= 1;
// if ( (vram_addr & old_addr) & 0x2000 )
// dprintf( "%d Toggled A13\n", time );
break;
}
case 7:
{
int a = vram_addr & 0x3fff;
vram_addr = a + ((w2000 & 4) ? 32 : 1);
if ( a < 0x2000 )
{
a = map_chr_addr( a );
BOOST::uint8_t& b = impl->chr_ram [a];
if ( (b ^ data) & chr_write_mask )
{
b = data;
assert( a < sizeof impl->chr_ram );
tiles_modified [(unsigned) a / bytes_per_tile] = true;
any_tiles_modified = true;
}
}
else if ( a < 0x3f00 )
{
get_nametable( a ) [a & 0x3ff] = data;
}
else
{
*map_palette( a ) = data & 0x3f;
}
break;
}
}
}
// Frame rendering
// returns true when sprite hit checking is done for the frame (hit flag won't change any more)
bool Nes_Ppu::update_sprite_hit( nes_time_t cpu_time )
{
// earliest time of hit empirically determined by testing on NES
ppu_time_t const delay = 21 * scanline_duration + 333;
if ( cpu_time < delay / ppu_overclock )
return false;
long time = cpu_time * ppu_overclock - delay;
long low_bound = spr_ram [0] * scanline_duration + spr_ram [3];
if ( time < low_bound )
return false;
int tile = spr_ram [1] + ((w2000 << 5) & 0x100);
int height = 1;
if ( w2000 & 0x20 )
{
height = 2;
tile = (tile & 1) * 0x100 + (tile & 0xfe);
}
byte const* data = map_chr( tile * bytes_per_tile );
for ( int n = height; n--; )
{
for ( int n = 8; n--; )
{
if ( time < low_bound )
return false;
if ( data [0] | data [8] )
{
r2002 |= 0x40;
return true;
}
data++;
low_bound += scanline_duration;
}
data += 8;
}
return true;
}
void Nes_Ppu::run_hblank( int n )
{
hblank_time += scanline_duration * n;
if ( w2001 & 0x08 )
{
// vram_addr = (vram_addr & ~0x41f) | (vram_temp & 0x41f);
long addr = vram_addr + n * 0x1000;
if ( addr >= 0x8000 )
{
addr &= 0x7fff;
int const mask = 0x3e0;
int a = (addr + 0x20) & mask;
if ( a == 30 * 0x20 )
{
a &= 0x1f;
addr ^= 0x800;
}
addr = (addr & ~mask) | (a & mask);
}
assert( addr < 0x8000 );
vram_addr = addr;
}
}
void Nes_Ppu::render_until_( nes_time_t cpu_time )
{
ppu_time_t time = cpu_time * ppu_overclock;
ppu_time_t const frame_duration = scanline_duration * 261;
if ( time > frame_duration )
time = frame_duration;
if ( frame_phase == 0 )
{
frame_phase = 1;
if ( w2001 & 0x08 )
vram_addr = vram_temp;
// else
// dprintf( "PPU off\n" );
}
if ( hblank_time < scanline_time && hblank_time < time )
run_hblank( 1 );
int count = 0;
while ( scanline_time < time )
{
scanline_time += scanline_duration;
count++;
}
if ( count )
{
int start = next_scanline;
int end = start + count;
assert( end <= image_height );
next_scanline = end;
if ( base_pixels )
{
if ( start == 0 )
{
memcpy( host_palette, palette, 32 );
int bg = palette [0];
for ( int i = 0; i < 32; i += 4 )
host_palette [i] = bg;
memcpy( host_palette + 32, host_palette, 32 );
}
if ( w2001 & 0x18 && any_tiles_modified )
{
any_tiles_modified = false;
update_tiles( 0 );
}
if ( w2001 & 0x08 )
{
draw_background( start, end );
}
else
{
run_hblank( end - start - 1 );
black_background( start, end );
}
// when clipping just sprites, save left strip then restore after drawing sprites
int const obj_mask = 0x04;
int const bg_mask = 0x02;
int clip_mode = ~w2001 & (obj_mask | bg_mask);
if ( clip_mode == obj_mask )
save_left( start, end );
else if ( clip_mode == bg_mask )
clip_left( start, end );
if ( w2001 & 0x10 )
draw_sprites( start, end );
if ( clip_mode == obj_mask )
restore_left( start, end );
else if ( clip_mode == (obj_mask | bg_mask) )
clip_left( start, end );
}
else
{
run_hblank( end - start - 1 );
}
}
if ( hblank_time < time )
run_hblank( 1 );
assert( time <= hblank_time );
next_time = min( scanline_time, hblank_time ) / ppu_overclock;
}

View File

@ -1,472 +0,0 @@
// Nes_Emu 0.7.0. http://www.slack.net/~ant/
#include "Nes_Recorder.h"
#include <string.h>
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "blargg_source.h"
int const joypad_sync_value = 0xFF; // joypad data on frames it's never read
Nes_Recorder::Nes_Recorder()
{
cache = 0;
film_ = 0;
frames = 0;
resync_enabled = false;
disable_reverse( 1 ); // sets cache_period_ and cache_size
reverse_allowed = true;
buffer_height_ = Nes_Ppu::buffer_height * frames_size + 2;
}
Nes_Recorder::~Nes_Recorder()
{
free( frames );
delete [] cache;
}
blargg_err_t Nes_Recorder::init_()
{
RETURN_ERR( base::init_() );
cache = new Nes_State [cache_size];
int frame_count = reverse_allowed ? frames_size : 1;
CHECK_ALLOC( frames = (saved_frame_t*) calloc( sizeof *frames, frame_count ) );
for ( int i = 0; i < frame_count; i++ )
frames [i].top = (long) i * Nes_Ppu::buffer_height + 1;
return 0;
}
void Nes_Recorder::clear_cache()
{
ready_to_resync = false;
reverse_enabled = false;
for ( int i = 0; i < cache_size; i++ )
cache [i].set_timestamp( invalid_frame_count );
}
void Nes_Recorder::set_film( Nes_Film* new_film, frame_count_t time )
{
require( new_film );
film_ = new_film;
clear_cache();
if ( !film_->blank() )
{
tell_ = film_->constrain( time );
check( tell_ == time ); // catch seeks outside film
seek_( tell_ );
}
}
void Nes_Recorder::reset( bool full_reset, bool erase_battery_ram )
{
base::reset( full_reset, erase_battery_ram );
tell_ = 0;
film_->clear();
clear_cache();
}
void Nes_Recorder::loading_state( Nes_State const& in )
{
reset();
tell_ = in.timestamp();
}
// Frame emulation
inline int Nes_Recorder::cache_index( frame_count_t t ) const
{
return (t / cache_period_) % cache_size;
}
void Nes_Recorder::emulate_frame_( Nes_Film::joypad_t joypad )
{
if ( base::timestamp() % cache_period_ == 0 )
save_state( &cache [cache_index( base::timestamp() )] );
if ( base::emulate_frame( joypad & 0xFF, (joypad >> 8) & 0xFF ) ) { }
}
void Nes_Recorder::replay_frame_( Nes_Film::joypad_t joypad )
{
if ( base::timestamp() % film_->period() == 0 )
{
if ( film_->read_snapshot( base::timestamp() ).timestamp() == invalid_frame_count )
{
Nes_State_* ss = film_->modify_snapshot( base::timestamp() );
if ( ss )
save_state( ss );
else
check( false ); // out of memory simply causes lack of caching
}
}
emulate_frame_( joypad );
}
int Nes_Recorder::replay_frame()
{
frame_count_t start_time = base::timestamp();
int joypad = film_->get_joypad( start_time );
if ( !film_->has_joypad_sync() )
{
replay_frame_( joypad );
}
else if ( (joypad & 0xFF) != joypad_sync_value )
{
// joypad should be read
replay_frame_( joypad );
if ( !joypad_read_count() )
{
// emulator has fallen behind
dprintf( "Fell behind joypad data \n" );
base::set_timestamp( start_time );
}
}
else
{
// get joypad for next frame in case emulator gets ahead
if ( film_->contains_frame( start_time + 1 ) )
{
joypad = film_->get_joypad( start_time + 1 );
if ( (joypad & 0xFF) == joypad_sync_value )
joypad = 0; // next frame shouldn't read joypad either, so just give it nothing
}
// joypad should not be read
replay_frame_( joypad );
if ( joypad_read_count() )
{
// emulator is ahead
dprintf( "Ahead of joypad data \n" );
base::set_timestamp( film_->constrain( base::timestamp() + 1 ) );
}
}
return base::timestamp() - start_time;
}
// Film handling
Nes_State_ const* Nes_Recorder::nearest_snapshot( frame_count_t time ) const
{
Nes_State_ const* ss = film_->nearest_snapshot( time );
if ( ss )
{
// check cache for any snapshots more recent than film_'s
for ( frame_count_t t = time - time % cache_period_;
ss->timestamp() < t; t -= cache_period_ )
{
Nes_State_ const& cache_ss = cache [cache_index( t )];
if ( cache_ss.timestamp() == t )
return &cache_ss;
}
}
return ss;
}
void Nes_Recorder::seek_( frame_count_t time )
{
Nes_State_ const* ss = nearest_snapshot( time );
if ( !film_->contains( time ) || !ss )
{
require( false ); // tried to seek outside recording
return;
}
base::load_state( *ss );
frame_ = 0; // don't render graphics
frame_count_t max_iter = (time - base::timestamp()) * 2; // don't seek forever
while ( base::timestamp() < time && max_iter-- )
replay_frame();
}
void Nes_Recorder::seek( frame_count_t time )
{
check( film_->contains( time ) );
time = film_->constrain( time );
if ( time != tell() )
{
reverse_enabled = false;
seek_( time );
tell_ = base::timestamp();
}
}
inline frame_count_t Nes_Recorder::advancing_frame()
{
if ( reverse_enabled )
{
reverse_enabled = false;
if ( !film_->blank() )
seek_( tell_ );
}
frame_ = &frames [0];
tell_ = base::timestamp();
return tell_++;
}
blargg_err_t Nes_Recorder::emulate_frame( int joypad, int joypad2 )
{
frame_count_t time = advancing_frame();
require( film_->blank() || film_->contains( time ) );
Nes_Film::joypad_t joypads = joypad2 * 0x100 + joypad;
Nes_State_* ss = 0;
RETURN_ERR( film_->record_frame( time, joypads, &ss ) );
if ( ss )
save_state( ss );
emulate_frame_( joypads );
if ( film_->has_joypad_sync() && !joypad_read_count() )
RETURN_ERR( film_->set_joypad( time, joypad_sync_value ) );
// avoid stale cache snapshot after trimming film
if ( base::timestamp() % cache_period_ == 0 )
cache [cache_index( base::timestamp() )].set_timestamp( invalid_frame_count );
return 0;
}
void Nes_Recorder::next_frame()
{
if ( tell() >= film_->end() )
{
check( false ); // tried to go past end
return;
}
frame_count_t time = advancing_frame();
assert( base::timestamp() == time || base::timestamp() == time + 1 );
// ready_to_resync avoids endless resyncing if joypad isn't getting read when
// it should, thus the timestamp never incrementing.
if ( ready_to_resync )
{
Nes_State_ const& ss = film_->read_snapshot( time );
if ( ss.timestamp() == time )
{
// todo: remove
#if !defined (NDEBUG) && 1
dprintf( "Resynced \n" );
static Nes_State* temp = BLARGG_NEW Nes_State [2];
static char* temp2 = new char [sizeof *temp];
if ( temp && temp2 )
{
save_state( temp );
memcpy( temp2, temp, sizeof *temp );
long a = temp->apu.noise.shift_reg;
long b = temp->apu.apu.w40xx [0x11];
long c = temp->apu.dmc.bits;
base::load_state( ss );
save_state( temp );
save_state( &temp [1] );
// shift register and dac are not maintained
temp->apu.noise.shift_reg = a;
temp->apu.apu.w40xx [0x11] = b;
temp->apu.dmc.bits = c;
if ( memcmp( temp2, temp, sizeof *temp ) )
{
check( !"Film sync corrected error" );
Std_File_Writer out;
(void) !out.open( "state0" );
(void) !temp [0].write( out );
(void) !out.open( "state1" );
(void) !temp [1].write( out );
//(void) !out.open( "state2" );
//(void) !ss.write( out );
}
}
if ( 0 )
#endif
base::load_state( ss );
}
}
int count = replay_frame();
tell_ = base::timestamp();
// examination of count prevents endless resync if frame is getting doubled
ready_to_resync = false;
if ( count && resync_enabled && film_->read_snapshot( tell_ ).timestamp() == tell_ )
{
fade_sound_out = true;
ready_to_resync = true;
}
}
// Extra features
void Nes_Recorder::record_keyframe()
{
if ( !film_->blank() )
{
Nes_State_* ss = film_->modify_snapshot( base::timestamp() );
if ( !ss )
{
check( false ); // out of memory simply causes lack of key frame adjustment
}
// first snapshot can only be replaced if key frame is at beginning of film
else if ( ss->timestamp() > film_->begin() || base::timestamp() == film_->begin() )
{
if ( ss->timestamp() != base::timestamp() )
save_state( ss );
}
}
}
frame_count_t Nes_Recorder::nearby_keyframe( frame_count_t time ) const
{
// TODO: reimplement using direct snapshot and cache access
check( film_->contains( time ) );
// don't adjust time if seeking to beginning or end
if ( film_->begin() < time && time < film_->end() )
{
// rounded time must be within about a minute of requested time
int const half_threshold = 45 * frame_rate;
// find nearest snapshots before and after requested time
frame_count_t after = invalid_frame_count;
frame_count_t before = time + half_threshold;
do
{
after = before;
Nes_State_ const* ss = nearest_snapshot( film_->constrain( before - 1 ) );
if ( !ss )
{
require( false ); // tried to seek outside recording
return time;
}
before = ss->timestamp();
}
while ( time < before );
// determine closest and use if within threshold
frame_count_t closest = after;
if ( time - before < after - time )
closest = before;
int delta = time - closest;
if ( max( delta, -delta ) < half_threshold )
time = closest;
if ( time < film_->begin() )
time = film_->begin();
}
return time;
}
void Nes_Recorder::skip( int delta )
{
if ( delta ) // rounding code can't handle zero
{
// round to nearest cache timestamp (even if not in cache)
frame_count_t current = tell();
frame_count_t time = current + delta + cache_period_ / 2;
time -= time % cache_period_;
if ( delta < 0 )
{
if ( time >= current )
time -= cache_period_;
}
else if ( time <= current )
{
time += cache_period_;
}
seek( film_->constrain( time ) );
}
}
// Reverse handling
// Get index of frame at given timestamp in reverse frames
inline int Nes_Recorder::reverse_index( frame_count_t time ) const
{
int index = time % (frames_size * 2);
if ( index >= frames_size ) // opposite direction on odd runs
index = frames_size * 2 - 1 - index;
return index;
}
// Generate frame at given timestamp into proper position in reverse frames
void Nes_Recorder::reverse_fill( frame_count_t time )
{
if ( time >= film_->begin() )
{
// todo: can cause excessive seeking when joypad data loses sync
if ( base::timestamp() != time )
seek_( time );
saved_frame_t* frame = &frames [reverse_index( time )];
frame_ = frame;
if ( time % frames_size == frames_size - 1 )
fade_sound_out = true;
replay_frame();
frame->sample_count = base::read_samples( frame->samples, frame->max_samples );
}
}
void Nes_Recorder::prev_frame()
{
if ( tell() <= film_->begin() || !reverse_allowed )
{
check( false ); // tried to go before beginning
return;
}
int offset = tell_ % frames_size;
frame_count_t aligned = tell_ - offset;
if ( reverse_enabled )
{
reverse_fill( aligned - 1 - offset );
}
else
{
reverse_enabled = true;
for ( int i = 0; i < frames_size; i++ )
{
frame_count_t time = aligned + i;
if ( i >= offset )
time -= offset + frames_size; // restore some of previous second
reverse_fill( time );
}
}
tell_--;
frame_ = &frames [reverse_index( tell_ )];
}
long Nes_Recorder::read_samples( short* out, long count )
{
require( count >= frame().sample_count );
if ( !reverse_enabled )
return base::read_samples( out, count );
// copy samples in reverse without reversing left and right channels
// to do: optimize?
count = frame().sample_count;
blip_sample_t const* in = STATIC_CAST(saved_frame_t const&,frame()).samples;
int step = frame().chan_count - 1;
for ( int i = 0; i < count; i++ )
out [count - 1 - (i ^ step)] = in [i];
return count;
}

View File

@ -1,142 +0,0 @@
// NES emulator with movie recording and playback in both directions
// Nes_Emu 0.7.0
#ifndef NES_RECORDER_H
#define NES_RECORDER_H
#include "Nes_Emu.h"
#include "Nes_Film.h"
class Nes_Recorder : public Nes_Emu {
public:
// Set new film to use for recording and playback. If film isn't empty, seeks to
// its beginning (or to optional specified fime). Film *must* be loaded before
// using the recorder for emulation.
void set_film( Nes_Film*, frame_count_t );
void set_film( Nes_Film* f ) { set_film( f, f->begin() ); }
// Currently loaded film
Nes_Film& film() const { return *film_; }
// Time references are in terms of timestamps at a single moment, rather than
// the frames that occur between two timestamps. All timestamps must be within
// the current recording. The emulator is always at a particular timestamp in the
// recording; a frame exists only in the graphics buffer and sample buffer. Shown
// below are timestamps and the frame that occurs between 1 and 2.
// |---|///|---|---
// 0 1 2 3 timestamps of snapshots between frames
// Current timestamp
frame_count_t tell() const;
// Seek to new timestamp. Time is constrained to recording if it falls outside.
void seek( frame_count_t );
// Record new frame at current timestamp and remove anything previously
// recorded after it.
blargg_err_t emulate_frame( int joypad, int joypad2 = 0 );
// Increment timestamp and generate frame for that duration. Does nothing
// if already at end of recording.
void next_frame();
// Decrement timestamp and generate frame for that duration. Does nothing
// if already at beginning of recording. Performance is close to that of
// next_frame().
void prev_frame();
// Additional features
// Disable reverse support and optionally use less-frequent cache snapshots, in order
// to drastically reduce the required height of the graphics buffer and memory usage.
// If used, must be called one time *before* any use of emulator.
void disable_reverse( int cache_period_secs = 5 );
// Call when current film has been significantly changed (loaded from file).
// Doesn't need to be called if film was merely trimmed.
void film_changed();
// Attempt to add keyframe to film at current timestamp, which will make future
// seeking to this point faster after film is later loaded.
void record_keyframe();
// Get time of nearby key frame within +/- 45 seconds, otherwise return time unchanged.
// Seeking to times of key frames is much faster than an arbitrary time. Time
// is constrained to film if it falls outside.
frame_count_t nearby_keyframe( frame_count_t ) const;
// Quickly skip forwards or backwards, staying within recording. May skip slightly
// more or less than delta, but will always skip at least some if delta is non-zero
// (i.e. repeated skip( 1 ) calls will eventually reach the end of the recording).
void skip( int delta );
// When enabled, emulator is synchronized at each snapshot in film. This
// corrects any emulation differences compared to when the film was made. Might
// be necessary when playing movies made in an older version of this emulator
// or from other emulators.
void enable_resync( bool b = true ) { resync_enabled = b; }
// Height of graphics buffer needed when running forward (next_frame() or
// emulate_frame(), but not prev_frame())
enum { forward_buffer_height = Nes_Ppu::buffer_height };
public:
Nes_Recorder();
virtual ~Nes_Recorder();
virtual blargg_err_t init_();
virtual void reset( bool = true, bool = false );
void load_state( Nes_State const& s ) { Nes_Emu::load_state( s ); }
blargg_err_t load_state( Auto_File_Reader r ) { return Nes_Emu::load_state( r ); }
virtual long read_samples( short* out, long count );
private:
typedef Nes_Emu base;
// snapshots
Nes_State* cache;
int cache_size;
int cache_period_;
Nes_Film* film_;
void clear_cache();
int cache_index( frame_count_t ) const;
Nes_State_ const* nearest_snapshot( frame_count_t ) const;
// film
frame_count_t tell_;
bool resync_enabled;
bool ready_to_resync;
void emulate_frame_( Nes_Film::joypad_t );
void replay_frame_( Nes_Film::joypad_t );
int replay_frame();
void seek_( frame_count_t );
frame_count_t advancing_frame();
void loading_state( Nes_State const& );
// reverse handling
enum { frames_size = frame_rate };
struct saved_frame_t : Nes_Emu::frame_t {
enum { max_samples = 2048 };
blip_sample_t samples [max_samples];
};
saved_frame_t* frames;
bool reverse_enabled;
bool reverse_allowed;
void reverse_fill( frame_count_t );
int reverse_index( frame_count_t ) const; // index for given frame
};
inline frame_count_t Nes_Recorder::tell() const { return film_->constrain( tell_ ); }
inline void Nes_Recorder::film_changed() { set_film( &film(), film().constrain( tell() ) ); }
inline void Nes_Recorder::disable_reverse( int new_cache_period )
{
reverse_allowed = false;
buffer_height_ = Nes_Ppu::buffer_height;
cache_period_ = new_cache_period * frame_rate;
cache_size = 2 * 60 * frame_rate / cache_period_ + 7; // +7 reduces contention
}
#endif

View File

@ -1,324 +0,0 @@
// Nes_Emu 0.5.6. http://www.slack.net/~ant/
#include "Nes_Rewinder.h"
#include <string.h>
/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
// to do: fade out at transitions between forward and reverse
#include BLARGG_SOURCE_BEGIN
// If true, always keep recent frame images in graphics buffer. Reduces overall
// performance by about 33% on my machine, due to the frame buffer not staying in the cache.
bool const quick_reverse = false;
Nes_Rewinder::Nes_Rewinder( frame_count_t snapshot_period ) : recorder( snapshot_period )
{
pixels = NULL;
frames = NULL;
reverse_enabled = false;
}
Nes_Rewinder::~Nes_Rewinder()
{
delete [] frames;
}
blargg_err_t Nes_Rewinder::init()
{
if ( !frames )
{
BLARGG_RETURN_ERR( recorder::init() );
frames = BLARGG_NEW frame_t [frames_size];
BLARGG_CHECK_ALLOC( frames );
}
return blargg_success;
}
blargg_err_t Nes_Rewinder::load_ines_rom( Data_Reader& in, Data_Reader* ips )
{
if ( !frames )
BLARGG_RETURN_ERR( init() );
return recorder::load_ines_rom( in, ips );
}
long Nes_Rewinder::samples_avail() const
{
return frames [current_frame].sample_count;
}
inline void copy_reverse( const blip_sample_t* in, int count, blip_sample_t* out, int step )
{
in += count;
while ( count > 0 )
{
count -= step;
in -= step;
*out = *in;
out += step;
}
}
long Nes_Rewinder::read_samples( short* out, long out_size )
{
int count = samples_avail();
if ( count )
{
if ( count > out_size )
{
count = out_size;
assert( false ); // has no provision for reading partial buffer
}
if ( !reverse_enabled )
{
memcpy( out, frames [current_frame].samples, count * sizeof *out );
}
else
{
int step = samples_per_frame();
for ( int i = step; i-- > 0; )
copy_reverse( frames [current_frame].samples + i, count, out + i, step );
}
if ( fade_sound_in )
{
fade_sound_in = false;
fade_samples_( out, count, 1 );
}
if ( frames [current_frame].fade_out )
{
fade_sound_in = true;
fade_samples_( out, count, -1 );
}
}
return count;
}
void Nes_Rewinder::seek( frame_count_t time )
{
if ( time != tell() )
{
clear_reverse();
recorder::seek_( time );
}
}
inline void Nes_Rewinder::set_output( int index )
{
recorder::set_pixels( pixels + index * frame_height * row_bytes, row_bytes );
}
void Nes_Rewinder::frame_rendered( int index, bool using_buffer )
{
frame_t& frame = frames [index];
if ( recorder::frames_emulated() > 0 )
{
frame.palette_size = recorder::palette_size();
for ( int i = frame.palette_size; i--; )
frame.palette [i] = recorder::palette_entry( i );
frame.sample_count = recorder::read_samples( frame.samples, frame.max_samples );
}
else if ( pixels && using_buffer )
{
int old_index = (index + frames_size - 1) % frames_size;
memcpy( &frame, &frames [old_index], sizeof frame );
// to do: handle case where row_bytes is a lot greater than buffer_width
memcpy( pixels + index * frame_height * row_bytes,
pixels + old_index * frame_height * row_bytes,
row_bytes * frame_height );
}
frame.fade_out = false;
}
blargg_err_t Nes_Rewinder::next_frame( int joypad, int joypad2 )
{
if ( reverse_enabled )
{
if ( !get_film().empty() ) // if empty then we can't seek
recorder::seek_( reversed_time );
clear_reverse();
}
current_frame = 0;
if ( quick_reverse )
{
current_frame = recorder::tell() % frames_size;
if ( buffer_scrambled )
buffer_scrambled--;
}
set_output( current_frame );
BLARGG_RETURN_ERR( recorder::next_frame( joypad, joypad2 ) );
frame_rendered( current_frame, quick_reverse );
return blargg_success;
}
frame_count_t Nes_Rewinder::tell() const
{
return reverse_enabled ? reversed_time : recorder::tell();
}
void Nes_Rewinder::clear_cache()
{
recorder::clear_cache();
clear_reverse();
}
void Nes_Rewinder::clear_reverse()
{
buffer_scrambled = frames_size;
reverse_enabled = false;
reverse_unmirrored = 0;
reverse_pivot = 0;
reversed_time = 0;
}
void Nes_Rewinder::play_frame_( int index )
{
if ( negative_seek > 0 )
{
negative_seek--;
}
else
{
set_output( index );
recorder::play_frame_();
frame_rendered( index, true );
}
}
void Nes_Rewinder::seek_clamped( frame_count_t time )
{
negative_seek = movie_begin() - time;
if ( negative_seek > 0 )
time = movie_begin();
recorder::seek_( time );
}
void Nes_Rewinder::enter_reverse()
{
reversed_time = recorder::tell() - 1;
reverse_pivot = reversed_time % frames_size;
frame_count_t first_frame = reversed_time - reverse_pivot;
if ( buffer_scrambled )
{
// buffer hasn't been filled with a clean second of frames since last seek
dprintf( "Refilling reverse buffer, pivot: %d\n", (int) reverse_pivot );
// fill beginning
seek_clamped( first_frame );
for ( int i = 0; i <= reverse_pivot; i++ )
play_frame_( i );
frames [0].fade_out = true;
// fill end
seek_clamped( first_frame - frames_size + reverse_pivot + 1 );
for ( int i = reverse_pivot + 1; i < frames_size; i++ )
play_frame_( i );
}
if ( reverse_pivot + 1 < frames_size )
frames [reverse_pivot + 1].fade_out = true;
reverse_unmirrored = 2; // unmirrored for first two passes, then alternating
reverse_pivot = -reverse_pivot; // don't pivot yet
seek_clamped( first_frame - frames_size );
// Buffer is now filled. Current second is at beginning and previous at end,
// and in this example reversed_time is 24 and reverse_pivot is 4:
// 20 21 22 23 24 25 16 17 18 19
// As fragment of current second is played backwards, it will be replaced with
// beginning of previous second:
// <---------------
// 20 21 22 23 24 25 16 17 18 19 frame 25
// 20 21 22 23 24 10 16 17 18 19 frame 24
// 20 21 22 23 11 10 16 17 18 19 frame 23
// 20 21 22 12 11 10 16 17 18 19 frame 22
// 20 21 13 12 11 10 16 17 18 19 frame 21
// 20 14 13 12 11 10 16 17 18 19 frame 20
// 15 14 13 12 11 10 16 17 18 19 frame 19
// Then filling will keep replacing buffer contents in a converging fashion:
// <---------
// 15 14 13 12 11 10 16 17 18 0 frame 19
// 15 14 13 12 11 10 16 17 1 0 frame 18
// 15 14 13 12 11 10 16 2 1 0 frame 17
// 15 14 13 12 11 10 3 2 1 0 frame 16
// -------------->
// 4 14 13 12 11 10 3 2 1 0 frame 15
// 4 5 13 12 11 10 3 2 1 0 frame 14
// 4 5 6 12 11 10 3 2 1 0 frame 13
// 4 5 6 7 11 10 3 2 1 0 frame 12
// 4 5 6 7 8 10 3 2 1 0 frame 11
// 4 5 6 7 8 9 3 2 1 0 frame 10
// <---------------
// etc.
}
void Nes_Rewinder::prev_frame()
{
if ( tell() <= movie_begin() )
{
require( false ); // tried to go before beginning of movie
return;
}
if ( !reverse_enabled )
{
reverse_enabled = true;
enter_reverse();
}
else
{
play_frame_( current_frame );
reversed_time--;
if ( reversed_time % frames_size == frames_size - 1 )
{
if ( reverse_pivot < 0 )
reverse_pivot = -reverse_pivot;
if ( --reverse_unmirrored < 0 )
reverse_unmirrored = 1;
seek_clamped( reversed_time - frames_size * 2 + 1 );
}
}
// determine index of frame in buffer
int raw_index = reversed_time % frames_size;
int index = raw_index;
if ( !reverse_unmirrored )
index = frames_size - 1 - index;
if ( index <= reverse_pivot )
index = reverse_pivot - index;
current_frame = index;
if ( raw_index == 0 ) // previous frame will be from previous restoration
frames [index].fade_out = true;
}

View File

@ -1,95 +0,0 @@
// NES recorder with smooth rewind (backwards playback)
// Nes_Emu 0.5.6. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
#ifndef NES_REWINDER_H
#define NES_REWINDER_H
#include "Nes_Recorder.h"
class Nes_Rewinder : public Nes_Recorder {
typedef Nes_Recorder recorder;
enum { frames_size = frames_per_second };
public:
explicit Nes_Rewinder( frame_count_t snapshot_period = 0 );
~Nes_Rewinder();
// Nes_Rewinder adds a single feature to Nes_Recorder: the ability to generate
// consecutive previous frames with similar performance to normal forward
// generation.
// Emulate frame *ending* at current timestamp. On exit, timestamp = timestamp - 1.
void prev_frame();
// Recently-generated frames are stored in a caller-supplied graphics buffer,
// which is many times taller than a single frame image.
enum { frame_height = Nes_Recorder::buffer_height };
enum { buffer_height = frame_height * frames_size };
// Y coordinate of image for current frame in graphics buffer
int get_buffer_y();
// Documented in Nes_Emu.h and Nes_Recorder.h
blargg_err_t load_ines_rom( Data_Reader&, Data_Reader* = NULL );
void set_pixels( void*, long bytes_per_row );
blargg_err_t next_frame( int joypad, int joypad2 = 0 );
frame_count_t tell() const;
void seek( frame_count_t );
long samples_avail() const;
long read_samples( short* out, long max_samples );
int palette_size() const { return frames [current_frame].palette_size; }
int palette_entry( int i ) const { return frames [current_frame].palette [i]; }
blargg_err_t init();
// End of public interface
private:
BOOST::uint8_t* pixels;
long row_bytes;
// frame cache
struct frame_t
{
int sample_count;
bool fade_out;
int palette_size;
byte palette [max_palette_size];
enum { max_samples = 2048 };
blip_sample_t samples [max_samples];
};
frame_t* frames;
int current_frame;
bool fade_sound_in;
void set_output( int index );
void frame_rendered( int index, bool using_buffer );
void clear_cache(); // Nes_Recorder override
// clamped seek
int negative_seek; // if positive, number of frames to ignore before playing
void play_frame_( int index );
void seek_clamped( frame_count_t ); // allows timestamp to be before beginning
// reversed frame mapping
frame_count_t reversed_time;// current time is different than emulator time in reverse
int buffer_scrambled; // number of frames remaining before buffer isn't scrambled
bool reverse_enabled;
int reverse_unmirrored; // number of buffers until buffer order is mirrored
int reverse_pivot; // pivot point in buffer
void clear_reverse();
void enter_reverse();
};
inline int Nes_Rewinder::get_buffer_y()
{
return current_frame * frame_height + image_top;
}
inline void Nes_Rewinder::set_pixels( void* p, long rb )
{
pixels = (BOOST::uint8_t*) p;
row_bytes = rb;
}
#endif

View File

@ -1,216 +0,0 @@
// Nes_Emu 0.5.6. http://www.slack.net/~ant/
#include "Nes_Rom.h"
#include <stdlib.h>
#include <string.h>
/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
Nes_Rom::Nes_Rom()
{
prg_ = NULL;
chr_ = NULL;
reset();
}
Nes_Rom::~Nes_Rom()
{
reset();
}
void Nes_Rom::reset()
{
free( prg_ );
prg_ = NULL;
free( chr_ );
chr_ = NULL;
prg_size_ = 0;
chr_size_ = 0;
mapper = 0;
}
long Nes_Rom::round_to_bank_size( long n )
{
n += bank_size - 1;
return n - n % bank_size;
}
blargg_err_t Nes_Rom::resize_prg( long size )
{
if ( size != prg_size_ )
{
// extra byte allows CPU to always read operand of instruction, which
// might go past end of ROM
void* p = realloc( prg_, round_to_bank_size( size ) + 1 );
BLARGG_CHECK_ALLOC( p || !size );
prg_ = (byte*) p;
prg_size_ = size;
}
return blargg_success;
}
blargg_err_t Nes_Rom::resize_chr( long size )
{
if ( size != chr_size_ )
{
void* p = realloc( chr_, round_to_bank_size( size ) );
BLARGG_CHECK_ALLOC( p || !size );
chr_ = (byte*) p;
chr_size_ = size;
}
return blargg_success;
}
// iNES reading
struct ines_header_t {
BOOST::uint8_t signature [4];
BOOST::uint8_t prg_count; // number of 16K PRG banks
BOOST::uint8_t chr_count; // number of 8K CHR banks
BOOST::uint8_t flags; // MMMM FTBV Mapper low, Four-screen, Trainer, Battery, V mirror
BOOST::uint8_t flags2; // MMMM --XX Mapper high 4 bits
BOOST::uint8_t zero [8]; // if zero [7] is non-zero, treat flags2 as zero
};
BOOST_STATIC_ASSERT( sizeof (ines_header_t) == 16 );
blargg_err_t Nes_Rom::load_ines_rom( Data_Reader& in )
{
ines_header_t h;
BLARGG_RETURN_ERR( in.read( &h, sizeof h ) );
if ( 0 != memcmp( h.signature, "NES\x1A", 4 ) )
return "Not a iNES ROM file";
if ( h.zero [7] ) // handle header defaced by a fucking idiot's handle
h.flags2 = 0;
set_mapper( h.flags, h.flags2 );
if ( h.flags & 0x04 ) // skip trainer
BLARGG_RETURN_ERR( in.skip( 512 ) );
BLARGG_RETURN_ERR( resize_prg( h.prg_count * 16 * 1024L ) );
BLARGG_RETURN_ERR( resize_chr( h.chr_count * 8 * 1024L ) );
BLARGG_RETURN_ERR( in.read( prg(), prg_size() ) );
BLARGG_RETURN_ERR( in.read( chr(), chr_size() ) );
return blargg_success;
}
// IPS patching
// IPS patch file format (integers are big-endian):
// 5 "PATCH"
// n blocks
//
// normal block:
// 3 offset
// 2 size
// n data
//
// repeated byte block:
// 3 offset
// 2 0
// 2 size
// 1 fill value
//
// end block (optional):
// 3 "EOF"
//
// A block can append data to the file by specifying an offset at the end of
// the current file data.
typedef BOOST::uint8_t byte;
static blargg_err_t apply_ips_patch( Data_Reader& patch, byte** file, long* file_size )
{
byte signature [5];
BLARGG_RETURN_ERR( patch.read( signature, sizeof signature ) );
if ( memcmp( signature, "PATCH", sizeof signature ) )
return "Not an IPS patch file";
while ( patch.remain() )
{
// read offset
byte buf [6];
BLARGG_RETURN_ERR( patch.read( buf, 3 ) );
long offset = buf [0] * 0x10000 + buf [1] * 0x100 + buf [2];
if ( offset == 'EOF' )
break;
// read size
BLARGG_RETURN_ERR( patch.read( buf, 2 ) );
long size = buf [0] * 0x100 + buf [1];
// size = 0 signals a run of identical bytes
int fill = -1;
if ( size == 0 )
{
BLARGG_RETURN_ERR( patch.read( buf, 3 ) );
size = buf [0] * 0x100 + buf [1];
fill = buf [2];
}
// expand file if new data is at exact end of file
if ( offset == *file_size )
{
*file_size = offset + size;
void* p = realloc( *file, *file_size );
BLARGG_CHECK_ALLOC( p );
*file = (byte*) p;
}
//dprintf( "Patch offset: 0x%04X, size: 0x%04X\n", (int) offset, (int) size );
if ( offset < 0 || *file_size < offset + size )
return "IPS tried to patch past end of file";
// read/fill data
if ( fill < 0 )
BLARGG_RETURN_ERR( patch.read( *file + offset, size ) );
else
memset( *file + offset, fill, size );
}
return blargg_success;
}
blargg_err_t Nes_Rom::load_patched_ines_rom( Data_Reader& in, Data_Reader& patch )
{
// read file into memory
long size = in.remain();
byte* ines = (byte*) malloc( size );
BLARGG_CHECK_ALLOC( ines );
const char* err = in.read( ines, size );
// apply patch
if ( !err )
err = apply_ips_patch( patch, &ines, &size );
// load patched file
if ( !err )
{
Mem_File_Reader patched( ines, size );
err = load_ines_rom( patched );
}
free( ines );
return err;
}

View File

@ -1,86 +0,0 @@
// NES ROM data loaded into memory
// Nes_Emu 0.5.6. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
#ifndef NES_ROM_H
#define NES_ROM_H
#include "blargg_common.h"
#include "abstract_file.h"
class Nes_Rom {
typedef BOOST::uint8_t byte;
public:
Nes_Rom();
~Nes_Rom();
// Load iNES ROM file
blargg_err_t load_ines_rom( Data_Reader& );
// Load iNES ROM file and apply IPS patch
blargg_err_t load_patched_ines_rom( Data_Reader&, Data_Reader& ips_patch );
// to do: support UNIF?
// True if a ROM is currently loaded
bool loaded() const { return prg_ != NULL; }
// Free any loaded ROM data
void reset();
// True if ROM claims to have battery-backed memory
bool has_battery_ram() const;
// Size of PRG data
long prg_size() const { return prg_size_; }
// Size of CHR data
long chr_size() const { return chr_size_; }
// Change size of PRG (code) data
blargg_err_t resize_prg( long );
// Change size of CHR (graphics) data
blargg_err_t resize_chr( long );
// Set mapper and information bytes. LSB and MSB are the standard iNES header
// bytes at offsets 6 and 7.
void set_mapper( int mapper_lsb, int mapper_msb );
// Initial mirroring setup
int mirroring() const { return mapper & 0x09; }
// iNES mapper code
int mapper_code() const;
// Pointer to beginning of PRG data
byte * prg() { return prg_; }
byte const* prg() const { return prg_; }
// Pointer to beginning of CHR data
byte * chr() { return chr_; }
byte const* chr() const { return chr_; }
// End of public interface
private:
enum { bank_size = 8 * 1024L }; // bank sizes must be a multiple of this
byte* prg_;
byte* chr_;
long prg_size_;
long chr_size_;
unsigned mapper;
long round_to_bank_size( long n );
};
inline bool Nes_Rom::has_battery_ram() const { return mapper & 0x02; }
inline void Nes_Rom::set_mapper( int mapper_lsb, int mapper_msb )
{
mapper = mapper_msb * 0x100 + mapper_lsb;
}
inline int Nes_Rom::mapper_code() const { return ((mapper >> 8) & 0xf0) | ((mapper >> 4) & 0x0f); }
#endif

View File

@ -1,329 +0,0 @@
// Nes_Emu 0.5.6. http://www.slack.net/~ant/
#include "Nes_Snapshot.h"
#include <stdlib.h>
#include <string.h>
#include "blargg_endian.h"
#include "Nes_Emu.h"
#include "Nes_Mapper.h"
/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
typedef BOOST::uint8_t byte;
Nes_Snapshot_Array::Nes_Snapshot_Array()
{
data = NULL;
size_ = 0;
}
Nes_Snapshot_Array::~Nes_Snapshot_Array()
{
free( data );
}
blargg_err_t Nes_Snapshot_Array::resize( int new_size )
{
void* new_mem = realloc( data, new_size * sizeof (Nes_Snapshot) );
BLARGG_CHECK_ALLOC( !new_size || new_mem );
data = (Nes_Snapshot*) new_mem;
int old_size = size_;
size_ = new_size;
for ( int i = old_size; i < new_size; i++ )
(*this) [i].clear();
return blargg_success;
}
void Nes_Snapshot::clear()
{
memset( &nes, 0, sizeof nes );
nes.frame_count = invalid_frame_count;
nes_valid = false;
cpu_valid = false;
joypad_valid = false;
apu_valid = false;
ppu_valid = false;
mapper_valid = false;
ram_valid = false;
sram_size = 0;
spr_ram_valid = false;
nametable_size = 0;
chr_size = 0;
}
// write
blargg_err_t Nes_Snapshot_Writer::end( Nes_Emu const& emu )
{
Nes_Snapshot_Array snapshots;
BLARGG_RETURN_ERR( snapshots.resize( 1 ) );
emu.save_snapshot( &snapshots [0] );
return end( snapshots [0] );
}
blargg_err_t Nes_Snapshot_Writer::end( Nes_Snapshot const& ss )
{
BLARGG_RETURN_ERR( ss.write_blocks( *this ) );
return Nes_File_Writer::end();
}
blargg_err_t Nes_Snapshot::write( Data_Writer& out ) const
{
Nes_Snapshot_Writer writer;
BLARGG_RETURN_ERR( writer.begin( &out ) );
return writer.end( *this );
}
blargg_err_t Nes_Snapshot::write_blocks( Nes_File_Writer& out ) const
{
if ( nes_valid )
{
nes_state_t s = nes;
s.pal = false;
s.timestamp = nes.timestamp * 15;
BLARGG_RETURN_ERR( write_nes_state( out, s ) );
}
if ( cpu_valid )
{
cpu_state_t s;
memset( &s, 0, sizeof s );
s.pc = cpu.pc;
s.s = cpu.sp;
s.a = cpu.a;
s.x = cpu.x;
s.y = cpu.y;
s.p = cpu.status;
BLARGG_RETURN_ERR( write_nes_state( out, s ) );
}
if ( ppu_valid )
{
ppu_state_t s = ppu;
BLARGG_RETURN_ERR( write_nes_state( out, s ) );
}
if ( apu_valid )
{
apu_snapshot_t s = apu;
BLARGG_RETURN_ERR( write_nes_state( out, s ) );
}
if ( joypad_valid )
{
joypad_state_t s = joypad;
BLARGG_RETURN_ERR( write_nes_state( out, s ) );
}
if ( mapper_valid )
BLARGG_RETURN_ERR( out.write_block( 'MAPR', mapper.data, mapper.size ) );
if ( ram_valid )
BLARGG_RETURN_ERR( out.write_block( 'LRAM', ram, sizeof ram ) );
if ( spr_ram_valid )
BLARGG_RETURN_ERR( out.write_block( 'SPRT', spr_ram, sizeof spr_ram ) );
if ( nametable_size )
BLARGG_RETURN_ERR( out.write_block( 'NTAB', nametable, nametable_size ) );
if ( chr_size )
BLARGG_RETURN_ERR( out.write_block( 'CHRR', chr, chr_size ) );
if ( sram_size )
BLARGG_RETURN_ERR( out.write_block( 'SRAM', sram, sram_size ) );
return blargg_success;
}
// read
Nes_Snapshot_Reader::Nes_Snapshot_Reader()
{
snapshot_ = NULL;
}
Nes_Snapshot_Reader::~Nes_Snapshot_Reader()
{
}
blargg_err_t Nes_Snapshot_Reader::begin( Data_Reader* dr, Nes_Snapshot* out )
{
snapshot_ = out;
if ( !out )
{
BLARGG_RETURN_ERR( snapshots.resize( 1 ) );
snapshot_ = &snapshots [0];
}
BLARGG_RETURN_ERR( Nes_File_Reader::begin( dr ) );
if ( block_tag() != snapshot_file_tag )
return "Not a snapshot file";
return blargg_success;
}
blargg_err_t Nes_Snapshot::read( Data_Reader& in )
{
Nes_Snapshot_Reader reader;
BLARGG_RETURN_ERR( reader.begin( &in, this ) );
while ( !reader.done() )
BLARGG_RETURN_ERR( reader.next_block() );
return blargg_success;
}
blargg_err_t Nes_Snapshot_Reader::next_block()
{
if ( depth() != 0 )
return Nes_File_Reader::next_block();
return snapshot_->read_blocks( *this );
}
void Nes_Snapshot::set_nes_state( nes_state_t const& s )
{
nes = s;
nes.timestamp /= 15;
nes_valid = true;
}
blargg_err_t Nes_Snapshot::read_blocks( Nes_File_Reader& in )
{
while ( true )
{
BLARGG_RETURN_ERR( in.next_block() );
switch ( in.block_tag() )
{
case nes.tag:
memset( &nes, 0, sizeof nes );
BLARGG_RETURN_ERR( read_nes_state( in, &nes ) );
set_nes_state( nes );
break;
case cpu_state_t::tag: {
cpu_state_t s;
memset( &s, 0, sizeof s );
BLARGG_RETURN_ERR( read_nes_state( in, &s ) );
cpu.pc = s.pc;
cpu.sp = s.s;
cpu.a = s.a;
cpu.x = s.x;
cpu.y = s.y;
cpu.status = s.p;
cpu_valid = true;
break;
}
case ppu.tag:
memset( &ppu, 0, sizeof ppu );
BLARGG_RETURN_ERR( read_nes_state( in, &ppu ) );
ppu_valid = true;
break;
case apu.tag:
memset( &apu, 0, sizeof apu );
BLARGG_RETURN_ERR( read_nes_state( in, &apu ) );
apu_valid = true;
break;
case joypad.tag:
memset( &joypad, 0, sizeof joypad );
BLARGG_RETURN_ERR( read_nes_state( in, &joypad ) );
joypad_valid = true;
break;
case 'MAPR':
mapper.size = in.remain();
BLARGG_RETURN_ERR( in.read_block_data( mapper.data, sizeof mapper.data ) );
mapper_valid = true;
break;
case 'SPRT':
spr_ram_valid = true;
BLARGG_RETURN_ERR( in.read_block_data( spr_ram, sizeof spr_ram ) );
break;
case 'NTAB':
nametable_size = in.remain();
BLARGG_RETURN_ERR( in.read_block_data( nametable, sizeof nametable ) );
break;
case 'LRAM':
ram_valid = true;
BLARGG_RETURN_ERR( in.read_block_data( ram, sizeof ram ) );
break;
case 'CHRR':
chr_size = in.remain();
BLARGG_RETURN_ERR( in.read_block_data( chr, sizeof chr ) );
break;
case 'SRAM':
sram_size = in.remain();
BLARGG_RETURN_ERR( in.read_block_data( sram, sizeof sram ) );
break;
default:
return blargg_success;
}
}
}
// read_sta_file
struct sta_regs_t {
byte pc [2];
byte a;
byte p;
byte x;
byte y;
byte s;
};
BOOST_STATIC_ASSERT( sizeof (sta_regs_t) == 7 );
blargg_err_t Nes_Snapshot::read_sta_file( Data_Reader& in )
{
sram_size = 0x2000;
BLARGG_RETURN_ERR( in.read( sram, sram_size ) );
ram_valid = true;
BLARGG_RETURN_ERR( in.read( ram, 0x800 ) );
sta_regs_t r;
BLARGG_RETURN_ERR( in.read( &r, sizeof r ) );
this->cpu.pc = r.pc [1] * 0x100 + r.pc [0];
this->cpu.a = r.a;
this->cpu.status = r.p;
this->cpu.x = r.x;
this->cpu.y = r.y;
this->cpu.sp = r.s;
cpu_valid = true;
BLARGG_RETURN_ERR( in.read( spr_ram, 0x100 ) );
spr_ram_valid = true;
chr_size = 0x2000;
BLARGG_RETURN_ERR( in.read( chr, chr_size ) );
nametable_size = 0x1000;
BLARGG_RETURN_ERR( in.read( nametable, nametable_size ) );
return blargg_success;
}

View File

@ -1,165 +0,0 @@
// NES snapshot for saving and restoring emulator state
// Nes_Emu 0.5.6. Copyright (C) 2004-2005 Shay Green. GNU LGPL license.
#ifndef NES_SNAPSHOT_H
#define NES_SNAPSHOT_H
#include "Nes_File.h"
#include "Nes_Cpu.h"
class Nes_Emu;
typedef long frame_count_t;
frame_count_t const invalid_frame_count = LONG_MAX / 2; // a large positive value
class Nes_Snapshot;
class Nes_Snapshot_Array {
public:
Nes_Snapshot_Array();
~Nes_Snapshot_Array();
// Change size of array
blargg_err_t resize( int new_size );
// Current size of array
int size() const;
Nes_Snapshot& operator [] ( int );
Nes_Snapshot const& operator [] ( int ) const;
private:
Nes_Snapshot* data;
int size_;
};
class Nes_Snapshot_Writer : public Nes_File_Writer {
public:
// See Nes_File.h
blargg_err_t begin( Data_Writer* );
// Write snapshot of current emulator state and finish writing file
blargg_err_t end( Nes_Emu const& );
// Write snapshot and finish writing file
blargg_err_t end( Nes_Snapshot const& );
};
class Nes_Snapshot_Reader : public Nes_File_Reader {
public:
Nes_Snapshot_Reader();
~Nes_Snapshot_Reader();
// Optionally read snapshot into designated location instead of
// internal snapshot.
blargg_err_t begin( Data_Reader*, Nes_Snapshot* out = NULL );
// See Nes_File.h
blargg_err_t next_block();
// Snapshot valid after all blocks have been read
Nes_Snapshot const& snapshot() const;
private:
Nes_Snapshot_Array snapshots;
Nes_Snapshot* snapshot_;
};
class Nes_Snapshot {
Nes_Snapshot(); // use Nes_Snapshot_Array
Nes_Snapshot( Nes_Snapshot const& );
public:
// Invalidate all state
void clear();
// Change timestamp
void set_timestamp( frame_count_t );
// Timestamp snapshot was taken at
frame_count_t timestamp() const;
// Read Nesticle .sta file. Currently only reads basic fields.
blargg_err_t read_sta_file( Data_Reader& );
// Write snapshot to file
blargg_err_t write( Data_Writer& ) const;
// Read snapshot from file
blargg_err_t read( Data_Reader& );
// End of general interface
public:
blargg_err_t write_blocks( Nes_File_Writer& ) const;
void set_nes_state( nes_state_t const& );
blargg_err_t read_blocks( Nes_File_Reader& );
private:
nes_state_t nes;
bool nes_valid;
Nes_Cpu::registers_t cpu;
bool cpu_valid;
joypad_state_t joypad;
bool joypad_valid;
apu_snapshot_t apu;
bool apu_valid;
ppu_state_t ppu;
bool ppu_valid;
mapper_state_t mapper;
bool mapper_valid;
BOOST::uint8_t ram [0x800];
bool ram_valid;
BOOST::uint8_t sram [0x2000];
int sram_size;
BOOST::uint8_t spr_ram [0x100];
bool spr_ram_valid;
BOOST::uint8_t nametable [0x1000];
int nametable_size;
BOOST::uint8_t chr [0x2000];
int chr_size;
friend class Nes_Emu;
friend class Nes_Ppu_Impl;
};
inline Nes_Snapshot const& Nes_Snapshot_Reader::snapshot() const
{
assert( depth() == 0 && block_type() == group_end );
return *snapshot_;
}
inline blargg_err_t Nes_Snapshot_Writer::begin( Data_Writer* dw )
{
return Nes_File_Writer::begin( dw, snapshot_file_tag );
}
inline void Nes_Snapshot::set_timestamp( frame_count_t t ) { nes.frame_count = t; }
inline frame_count_t Nes_Snapshot::timestamp() const { return nes.frame_count; }
inline int Nes_Snapshot_Array::size() const { return size_; }
inline Nes_Snapshot& Nes_Snapshot_Array::operator [] ( int i )
{
assert( (unsigned) i < size_ );
return data [i];
}
inline Nes_Snapshot const& Nes_Snapshot_Array::operator [] ( int i ) const
{
assert( (unsigned) i < size_ );
return data [i];
}
#endif

View File

@ -1,231 +0,0 @@
// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/
#include "Nes_Vrc6.h"
/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
Nes_Vrc6::Nes_Vrc6()
{
output( NULL );
volume( 1.0 );
reset();
}
Nes_Vrc6::~Nes_Vrc6()
{
}
void Nes_Vrc6::reset()
{
last_time = 0;
for ( int i = 0; i < osc_count; i++ )
{
Vrc6_Osc& osc = oscs [i];
for ( int j = 0; j < reg_count; j++ )
osc.regs [j] = 0;
osc.delay = 0;
osc.last_amp = 0;
osc.phase = 1;
osc.amp = 0;
}
}
void Nes_Vrc6::volume( double v )
{
double const factor = 0.0967 * 2;
saw_synth.volume_unit( factor / 31 * v );
square_synth.volume_unit( factor * 0.5 / 15 * v );
}
void Nes_Vrc6::treble_eq( blip_eq_t const& eq )
{
saw_synth.treble_eq( eq );
square_synth.treble_eq( eq );
}
void Nes_Vrc6::output( Blip_Buffer* buf )
{
for ( int i = 0; i < osc_count; i++ )
osc_output( i, buf );
}
void Nes_Vrc6::run_until( nes_time_t time )
{
require( time >= last_time );
run_square( oscs [0], time );
run_square( oscs [1], time );
run_saw( time );
last_time = time;
}
void Nes_Vrc6::write_osc( nes_time_t time, int osc_index, int reg, int data )
{
require( (unsigned) osc_index < osc_count );
require( (unsigned) reg < reg_count );
run_until( time );
oscs [osc_index].regs [reg] = data;
}
void Nes_Vrc6::end_frame( nes_time_t time )
{
if ( time > last_time )
run_until( time );
last_time -= time;
assert( last_time >= 0 );
}
void Nes_Vrc6::save_snapshot( vrc6_snapshot_t* out ) const
{
out->saw_amp = oscs [2].amp;
for ( int i = 0; i < osc_count; i++ )
{
Vrc6_Osc const& osc = oscs [i];
for ( int r = 0; r < reg_count; r++ )
out->regs [i] [r] = osc.regs [r];
out->delays [i] = osc.delay;
out->phases [i] = osc.phase;
}
}
void Nes_Vrc6::load_snapshot( vrc6_snapshot_t const& in )
{
reset();
oscs [2].amp = in.saw_amp;
for ( int i = 0; i < osc_count; i++ )
{
Vrc6_Osc& osc = oscs [i];
for ( int r = 0; r < reg_count; r++ )
osc.regs [r] = in.regs [i] [r];
osc.delay = in.delays [i];
osc.phase = in.phases [i];
}
if ( !oscs [2].phase )
oscs [2].phase = 1;
}
#include BLARGG_ENABLE_OPTIMIZER
void Nes_Vrc6::run_square( Vrc6_Osc& osc, nes_time_t end_time )
{
Blip_Buffer* output = osc.output;
if ( !output )
return;
int volume = osc.regs [0] & 15;
if ( !(osc.regs [2] & 0x80) )
volume = 0;
int gate = osc.regs [0] & 0x80;
int duty = ((osc.regs [0] >> 4) & 7) + 1;
int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp;
nes_time_t time = last_time;
if ( delta )
{
osc.last_amp += delta;
square_synth.offset( time, delta, output );
}
time += osc.delay;
osc.delay = 0;
int period = osc.period();
if ( volume && !gate && period > 4 )
{
if ( time < end_time )
{
int phase = osc.phase;
do
{
phase++;
if ( phase == 16 )
{
phase = 0;
osc.last_amp = volume;
square_synth.offset( time, volume, output );
}
if ( phase == duty )
{
osc.last_amp = 0;
square_synth.offset( time, -volume, output );
}
time += period;
}
while ( time < end_time );
osc.phase = phase;
}
osc.delay = time - end_time;
}
}
void Nes_Vrc6::run_saw( nes_time_t end_time )
{
Vrc6_Osc& osc = oscs [2];
Blip_Buffer* output = osc.output;
if ( !output )
return;
int amp = osc.amp;
int amp_step = osc.regs [0] & 0x3F;
nes_time_t time = last_time;
int last_amp = osc.last_amp;
if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) )
{
osc.delay = 0;
int delta = (amp >> 3) - last_amp;
last_amp = amp >> 3;
saw_synth.offset( time, delta, output );
}
else
{
time += osc.delay;
if ( time < end_time )
{
int period = osc.period() * 2;
int phase = osc.phase;
do
{
if ( --phase == 0 )
{
phase = 7;
amp = 0;
}
int delta = (amp >> 3) - last_amp;
if ( delta )
{
last_amp = amp >> 3;
saw_synth.offset( time, delta, output );
}
time += period;
amp = (amp + amp_step) & 0xFF;
}
while ( time < end_time );
osc.phase = phase;
osc.amp = amp;
}
osc.delay = time - end_time;
}
osc.last_amp = last_amp;
}

View File

@ -1,86 +0,0 @@
// Konami VRC6 sound chip emulator
// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
#ifndef NES_VRC6_H
#define NES_VRC6_H
#include "Nes_Apu.h"
#include "Blip_Buffer.h"
struct vrc6_snapshot_t;
class Nes_Vrc6 {
public:
Nes_Vrc6();
~Nes_Vrc6();
// See Nes_Apu.h for reference
void reset();
void volume( double );
void treble_eq( blip_eq_t const& );
void output( Blip_Buffer* );
enum { osc_count = 3 };
void osc_output( int index, Blip_Buffer* );
void end_frame( nes_time_t );
void save_snapshot( vrc6_snapshot_t* ) const;
void load_snapshot( vrc6_snapshot_t const& );
// Oscillator 0 write-only registers are at $9000-$9002
// Oscillator 1 write-only registers are at $A000-$A002
// Oscillator 2 write-only registers are at $B000-$B002
enum { reg_count = 3 };
enum { base_addr = 0x9000 };
enum { addr_step = 0x1000 };
void write_osc( nes_time_t, int osc, int reg, int data );
private:
// noncopyable
Nes_Vrc6( const Nes_Vrc6& );
Nes_Vrc6& operator = ( const Nes_Vrc6& );
struct Vrc6_Osc
{
BOOST::uint8_t regs [3];
Blip_Buffer* output;
int delay;
int last_amp;
int phase;
int amp; // only used by saw
int period() const
{
return (regs [2] & 0x0f) * 0x100L + regs [1] + 1;
}
};
Vrc6_Osc oscs [osc_count];
nes_time_t last_time;
Blip_Synth<blip_med_quality,1> saw_synth;
Blip_Synth<blip_good_quality,1> square_synth;
void run_until( nes_time_t );
void run_square( Vrc6_Osc& osc, nes_time_t );
void run_saw( nes_time_t );
};
struct vrc6_snapshot_t
{
BOOST::uint8_t regs [3] [3];
BOOST::uint8_t saw_amp;
BOOST::uint16_t delays [3];
BOOST::uint8_t phases [3];
BOOST::uint8_t unused;
};
BOOST_STATIC_ASSERT( sizeof (vrc6_snapshot_t) == 20 );
inline void Nes_Vrc6::osc_output( int i, Blip_Buffer* buf )
{
assert( (unsigned) i < osc_count );
oscs [i].output = buf;
}
#endif

View File

@ -1,189 +0,0 @@
// Nes_Emu 0.5.6. http://www.slack.net/~ant/libs/
#include "Nonlinear_Buffer.h"
#include "Nes_Apu.h"
/* Library Copyright (C) 2003-2005 Shay Green. This library is free software;
you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
// Nonlinear_Buffer
Nonlinear_Buffer::Nonlinear_Buffer() :
Multi_Buffer( 1 )
{
}
Nonlinear_Buffer::~Nonlinear_Buffer()
{
}
void Nonlinear_Buffer::enable_nonlinearity( Nes_Apu& apu, bool b )
{
if ( b )
clear();
nonlinearizer.enable( apu, b );
for ( int i = 0; i < apu.osc_count; i++ )
apu.osc_output( i, (i >= 2 ? &tnd : &buf) );
}
blargg_err_t Nonlinear_Buffer::sample_rate( long rate, int msec )
{
BLARGG_RETURN_ERR( buf.sample_rate( rate, msec ) );
BLARGG_RETURN_ERR( tnd.sample_rate( rate, msec ) );
return Multi_Buffer::sample_rate( buf.sample_rate(), buf.length() );
}
void Nonlinear_Buffer::clock_rate( long rate )
{
buf.clock_rate( rate );
tnd.clock_rate( rate );
}
void Nonlinear_Buffer::bass_freq( int freq )
{
buf.bass_freq( freq );
tnd.bass_freq( freq );
}
void Nonlinear_Buffer::clear()
{
nonlinearizer.clear();
buf.clear();
tnd.clear();
}
Nonlinear_Buffer::channel_t Nonlinear_Buffer::channel( int i )
{
channel_t c;
c.center = &buf;
if ( 2 <= i && i <= 4 )
c.center = &tnd; // only use for triangle, noise, and dmc
c.left = c.center;
c.right = c.center;
return c;
}
void Nonlinear_Buffer::end_frame( blip_time_t length, bool )
{
buf.end_frame( length );
tnd.end_frame( length );
}
long Nonlinear_Buffer::samples_avail() const
{
return buf.samples_avail();
}
#include BLARGG_ENABLE_OPTIMIZER
long Nonlinear_Buffer::read_samples( blip_sample_t* out, long count )
{
count = nonlinearizer.make_nonlinear( tnd, count );
if ( count )
{
Blip_Reader lin;
Blip_Reader nonlin;
int lin_bass = lin.begin( buf );
int nonlin_bass = nonlin.begin( tnd );
for ( int n = count; n--; )
{
int s = lin.read() + nonlin.read();
lin.next( lin_bass );
nonlin.next( nonlin_bass );
*out++ = s;
if ( (BOOST::int16_t) s != s )
out [-1] = 0x7FFF - (s >> 24);
}
lin.end( buf );
nonlin.end( tnd );
buf.remove_samples( count );
tnd.remove_samples( count );
}
return count;
}
// Nes_Nonlinearizer
Nes_Nonlinearizer::Nes_Nonlinearizer()
{
nonlinear = false;
double gain = 0x7fff * 1.3;
// don't use entire range, so any overflow will stay within table
int const range = half * Nes_Apu::nonlinear_tnd_gain();
for ( int i = 0; i < half * 2; i++ )
{
int out = i << shift;
if ( i > half )
{
int j = i - half;
if ( j >= range )
j = range - 1;
double n = 202.0 / (range - 1) * j;
double d = 163.67 / (24329.0 / n + 100);
out = int (d * gain) + 0x8000;
assert( out < 0x10000 );
}
table [i] = out;
}
clear();
}
void Nes_Nonlinearizer::enable( Nes_Apu& apu, bool b )
{
nonlinear = b;
if ( b )
apu.enable_nonlinear( 1.0 );
else
apu.volume( 1.0 );
}
long Nes_Nonlinearizer::make_nonlinear( Blip_Buffer& buf, long count )
{
long avail = buf.samples_avail();
if ( count > avail )
count = avail;
if ( count && nonlinear )
{
const int zero_offset = Blip_Buffer::sample_offset_;
#define ENTRY( s ) (table [((s) >> shift) & entry_mask])
BOOST::uint16_t* p = buf.buffer_;
unsigned prev = ENTRY( accum );
long accum = this->accum;
for ( unsigned n = count; n--; )
{
accum += (long) *p - zero_offset;
check( (accum >> shift) < half * 2 );
unsigned entry = ENTRY( accum );
*p++ = entry - prev + zero_offset;
prev = entry;
}
this->accum = accum;
}
return count;
}

View File

@ -1,65 +0,0 @@
// NES non-linear audio output handling.
// Nes_Emu 0.5.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
#ifndef NONLINEAR_BUFFER_H
#define NONLINEAR_BUFFER_H
#include "Multi_Buffer.h"
class Nes_Apu;
// Use to make samples non-linear in Blip_Buffer used for triangle, noise, and DMC only
class Nes_Nonlinearizer {
public:
Nes_Nonlinearizer();
// Must be called when buffer is cleared
void clear() { accum = 0x8000; }
// Enable/disable non-linear output
void enable( Nes_Apu&, bool = true );
// Make at most 'count' samples in buffer non-linear and return number
// of samples modified. This many samples must then be read out of the buffer.
long make_nonlinear( Blip_Buffer&, long count );
private:
enum { shift = 5 };
enum { half = 0x8000 >> shift };
enum { entry_mask = half * 2 - 1 };
BOOST::uint16_t table [half * 2];
long accum;
bool nonlinear;
};
class Nonlinear_Buffer : public Multi_Buffer {
public:
Nonlinear_Buffer();
~Nonlinear_Buffer();
// Enable/disable non-linear output
void enable_nonlinearity( Nes_Apu&, bool = true );
// Blip_Buffer to output other sound chips to
Blip_Buffer* buffer() { return &buf; }
// See Multi_Buffer.h
blargg_err_t sample_rate( long rate, int msec = blip_default_length );
Multi_Buffer::sample_rate;
void clock_rate( long );
void bass_freq( int );
void clear();
channel_t channel( int );
void end_frame( blip_time_t, bool unused = true );
long samples_avail() const;
long read_samples( blip_sample_t*, long );
private:
Blip_Buffer buf;
Blip_Buffer tnd;
Nes_Nonlinearizer nonlinearizer;
};
#endif

View File

@ -1,69 +0,0 @@
// Nes_Emu 0.5.6. http://www.slack.net/~ant/libs/
#include "Nonlinear_Effects_Buffer.h"
/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
Nonlinear_Effects_Buffer::Nonlinear_Effects_Buffer() : Effects_Buffer( true )
{
config_t c;
c.effects_enabled = false;
config( c );
}
Nonlinear_Effects_Buffer::~Nonlinear_Effects_Buffer()
{
}
void Nonlinear_Effects_Buffer::enable_nonlinearity( Nes_Apu& apu, bool b )
{
if ( b )
clear();
nonlinearizer.enable( apu, b );
}
void Nonlinear_Effects_Buffer::config( const config_t& in )
{
config_t c = in;
if ( !c.effects_enabled )
{
// effects must always be enabled to keep separate buffers, so
// set parameters to be equivalent to disabled
c.pan_1 = 0;
c.pan_2 = 0;
c.echo_level = 0;
c.reverb_level = 0;
c.effects_enabled = true;
}
Effects_Buffer::config( c );
}
void Nonlinear_Effects_Buffer::clear()
{
nonlinearizer.clear();
Effects_Buffer::clear();
}
Nonlinear_Effects_Buffer::channel_t Nonlinear_Effects_Buffer::channel( int i )
{
return Effects_Buffer::channel( (2 <= i && i <= 4) ? 2 : i & 1 );
}
long Nonlinear_Effects_Buffer::read_samples( blip_sample_t* out, long count )
{
count = 2 * nonlinearizer.make_nonlinear( *channel( 2 ).center, count / 2 );
return Effects_Buffer::read_samples( out, count );
}

View File

@ -1,33 +0,0 @@
// Effects_Buffer with non-linear sound
// Nes_Emu 0.5.6. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
#ifndef NONLINEAR_EFFECTS_BUFFER_H
#define NONLINEAR_EFFECTS_BUFFER_H
#include "Nonlinear_Buffer.h"
#include "Effects_Buffer.h"
// Effects_Buffer uses several buffers and outputs stereo sample pairs.
class Nonlinear_Effects_Buffer : public Effects_Buffer {
public:
Nonlinear_Effects_Buffer();
~Nonlinear_Effects_Buffer();
// Enable/disable non-linear output
void enable_nonlinearity( Nes_Apu&, bool = true );
// See Effects_Buffer.h for reference
void config( const config_t& );
void clear();
channel_t channel( int );
long read_samples( blip_sample_t*, long );
// End of public interface
private:
Nes_Nonlinearizer nonlinearizer;
};
#endif

View File

@ -1,126 +0,0 @@
// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/
#include "apu_snapshot.h"
#include "Nes_Apu.h"
/* Copyright (C) 2003-2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#ifdef BLARGG_SOURCE_BEGIN
#include BLARGG_SOURCE_BEGIN
#endif
template<int mode>
struct apu_reflection
{
#define REFLECT( apu, state ) (mode ? void (apu = state) : void (state = apu))
static void reflect_env( apu_snapshot_t::env_t& state, Nes_Envelope& osc )
{
REFLECT( state [0], osc.env_delay );
REFLECT( state [1], osc.envelope );
REFLECT( state [2], osc.reg_written [3] );
}
static void reflect_square( apu_snapshot_t::square_t& state, Nes_Square& osc )
{
reflect_env( state.env, osc );
REFLECT( state.delay, osc.delay );
REFLECT( state.length, osc.length_counter );
REFLECT( state.phase, osc.phase );
REFLECT( state.swp_delay, osc.sweep_delay );
REFLECT( state.swp_reset, osc.reg_written [1] );
}
static void reflect_triangle( apu_snapshot_t::triangle_t& state, Nes_Triangle& osc )
{
REFLECT( state.delay, osc.delay );
REFLECT( state.length, osc.length_counter );
REFLECT( state.linear_counter, osc.linear_counter );
REFLECT( state.linear_mode, osc.reg_written [3] );
}
static void reflect_noise( apu_snapshot_t::noise_t& state, Nes_Noise& osc )
{
reflect_env( state.env, osc );
REFLECT( state.delay, osc.delay );
REFLECT( state.length, osc.length_counter );
REFLECT( state.shift_reg, osc.noise );
}
static void reflect_dmc( apu_snapshot_t::dmc_t& state, Nes_Dmc& osc )
{
REFLECT( state.delay, osc.delay );
REFLECT( state.remain, osc.length_counter );
REFLECT( state.buf, osc.buf );
REFLECT( state.bits_remain, osc.bits_remain );
REFLECT( state.bits, osc.bits );
REFLECT( state.buf_empty, osc.buf_empty );
REFLECT( state.silence, osc.silence );
REFLECT( state.irq_flag, osc.irq_flag );
if ( mode )
state.addr = osc.address | 0x8000;
else
osc.address = state.addr & 0x7fff;
}
};
void Nes_Apu::save_snapshot( apu_snapshot_t* state ) const
{
for ( int i = 0; i < osc_count * 4; i++ )
state->w40xx [i] = oscs [i >> 2]->regs [i & 3];
state->w40xx [0x11] = dmc.dac;
state->w4015 = osc_enables;
state->w4017 = frame_mode;
state->delay = frame_delay;
state->step = frame;
state->irq_flag = irq_flag;
typedef apu_reflection<1> refl;
Nes_Apu& apu = *(Nes_Apu*) this; // const_cast
refl::reflect_square ( state->square1, apu.square1 );
refl::reflect_square ( state->square2, apu.square2 );
refl::reflect_triangle( state->triangle, apu.triangle );
refl::reflect_noise ( state->noise, apu.noise );
refl::reflect_dmc ( state->dmc, apu.dmc );
}
void Nes_Apu::load_snapshot( apu_snapshot_t const& state )
{
reset();
write_register( 0, 0x4017, state.w4017 );
write_register( 0, 0x4015, state.w4015 );
for ( int i = 0; i < osc_count * 4; i++ )
{
int n = state.w40xx [i];
oscs [i >> 2]->regs [i & 3] = n;
write_register( 0, 0x4000 + i, n );
}
frame_delay = state.delay;
frame = state.step;
irq_flag = state.irq_flag;
typedef apu_reflection<0> refl;
apu_snapshot_t& st = (apu_snapshot_t&) state; // const_cast
refl::reflect_square ( st.square1, square1 );
refl::reflect_square ( st.square2, square2 );
refl::reflect_triangle( st.triangle, triangle );
refl::reflect_noise ( st.noise, noise );
refl::reflect_dmc ( st.dmc, dmc );
dmc.recalc_irq();
dmc.last_amp = dmc.dac;
}

View File

@ -1,75 +0,0 @@
// NES APU snapshot support
// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
#ifndef APU_SNAPSHOT_H
#define APU_SNAPSHOT_H
#include "blargg_common.h"
struct apu_snapshot_t
{
typedef BOOST::uint8_t byte;
typedef byte env_t [3];
/*struct env_t {
byte delay;
byte env;3
byte written;
};*/
byte w40xx [0x14]; // $4000-$4013
byte w4015; // enables
byte w4017; // mode
BOOST::uint16_t delay;
byte step;
byte irq_flag;
struct square_t {
BOOST::uint16_t delay;
env_t env;
byte length;
byte phase;
byte swp_delay;
byte swp_reset;
byte unused [1];
};
square_t square1;
square_t square2;
struct triangle_t {
BOOST::uint16_t delay;
byte length;
byte phase;
byte linear_counter;
byte linear_mode;
} triangle;
struct noise_t {
BOOST::uint16_t delay;
env_t env;
byte length;
BOOST::uint16_t shift_reg;
} noise;
struct dmc_t {
BOOST::uint16_t delay;
BOOST::uint16_t remain;
BOOST::uint16_t addr;
byte buf;
byte bits_remain;
byte bits;
byte buf_empty;
byte silence;
byte irq_flag;
} dmc;
enum { tag = 'APUR' };
void swap();
};
BOOST_STATIC_ASSERT( sizeof (apu_snapshot_t) == 72 );
#endif

View File

@ -1,194 +0,0 @@
// Optional less-common simple mappers
// Nes_Emu 0.5.6. http://www.slack.net/~ant/
#include "Nes_Mapper.h"
/* Copyright (C) 2004-2005 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include BLARGG_SOURCE_BEGIN
// Nina-1 (Deadly Towers only)
class Mapper_Nina1 : public Nes_Mapper {
byte bank;
public:
Mapper_Nina1()
{
register_state( &bank, 1 );
}
virtual void apply_mapping()
{
write( 0, 0, bank );
}
virtual void write( nes_time_t, nes_addr_t addr, int data )
{
bank = data;
set_prg_bank( 0x8000, bank_32k, bank );
}
};
// GNROM
class Mapper_Gnrom : public Nes_Mapper {
byte bank;
public:
Mapper_Gnrom()
{
register_state( &bank, 1 );
}
virtual void apply_mapping()
{
int b = bank;
bank = ~b;
write( 0, 0, b );
}
virtual void write( nes_time_t, nes_addr_t addr, int data )
{
int changed = bank ^ data;
bank = data;
if ( changed & 0x30 )
set_prg_bank( 0x8000, bank_32k, bank >> 4 & 3 );
if ( changed & 0x03 )
set_chr_bank( 0, bank_8k, bank & 3 );
}
};
// Color Dreams
class Mapper_Color_Dreams : public Nes_Mapper {
byte bank;
public:
Mapper_Color_Dreams()
{
register_state( &bank, 1 );
}
virtual void apply_mapping()
{
int b = bank;
bank = ~b;
write( 0, 0, b );
}
virtual void write( nes_time_t, nes_addr_t addr, int data )
{
int changed = bank ^ data;
bank = data;
if ( changed & 0x0f )
set_prg_bank( 0x8000, bank_32k, bank & 0x0f );
if ( changed & 0xf0 )
set_chr_bank( 0, bank_8k, bank >> 4 );
}
};
// Camerica
class Mapper_Camerica : public Nes_Mapper {
byte regs [3];
public:
Mapper_Camerica()
{
register_state( regs, sizeof regs );
}
virtual void apply_mapping()
{
write( 0, 0xc000, regs [0] );
if ( regs [1] & 0x80 )
write( 0, 0x9000, regs [1] );
}
virtual void write( nes_time_t, nes_addr_t addr, int data )
{
if ( addr >= 0xc000 )
{
regs [0] = data;
set_prg_bank( 0x8000, bank_16k, data );
}
else if ( (addr & 0xf000) == 0x9000 )
{
regs [1] = 0x80 | data;
mirror_single( (data >> 4) & 1 );
}
}
};
// Quattro
class Mapper_Quattro : public Nes_Mapper {
byte regs [2];
public:
Mapper_Quattro()
{
register_state( regs, sizeof regs );
}
virtual void reset_state()
{
regs [0] = 0;
regs [1] = 3;
}
virtual void apply_mapping()
{
int bank = regs [0] >> 1 & 0x0c;
set_prg_bank( 0x8000, bank_16k, bank + (regs [1] & 3) );
set_prg_bank( 0xC000, bank_16k, bank + 3 );
}
virtual void write( nes_time_t, nes_addr_t addr, int data )
{
if ( addr < 0xc000 )
regs [0] = data;
else
regs [1] = data;
Mapper_Quattro::apply_mapping();
}
};
void register_misc_mappers();
void register_misc_mappers()
{
register_mapper<Mapper_Color_Dreams>( 11 );
register_mapper<Mapper_Nina1>( 34 );
register_mapper<Mapper_Gnrom>( 66 );
register_mapper<Mapper_Camerica>( 71 );
register_mapper<Mapper_Quattro>( 232 );
}
void Nes_Mapper::register_optional_mappers()
{
register_misc_mappers();
extern void register_vrc6_mapper();
register_vrc6_mapper();
extern void register_mmc5_mapper();
register_mmc5_mapper();
extern void register_fme07_mapper();
register_fme07_mapper();
extern void register_namco106_mapper();
register_namco106_mapper();
}