quicknes: clean up unneeded files
This commit is contained in:
parent
ce36a94894
commit
f76ceaad8b
quicknes
fex
Binary_Extractor.cppBinary_Extractor.hFile_Extractor.cppFile_Extractor.hGzip_Extractor.cppGzip_Extractor.hGzip_Reader.cppGzip_Reader.hRar_Extractor.cppRar_Extractor.hZip7_Extractor.cppZip7_Extractor.hZip_Extractor.cppZip_Extractor.hZlib_Inflater.cppZlib_Inflater.hfex.cppfex.h
mingw
nes_emu
Mapper_Fme07.cppNes_Apu_State.cppNes_Blitter.cppNes_Blitter.hNes_Film.cppNes_Film.hNes_Film_Data.cppNes_Film_Data.hNes_Film_Packer.cppNes_Film_Packer.hNes_Fme07_Apu.cppNes_Fme07_Apu.hNes_Nonlinearizer.cppNes_Nonlinearizer.hNes_Ppu_Core.cppNes_Recorder.cppNes_Recorder.hNes_Rewinder.cppNes_Rewinder.hNes_Rom.cppNes_Rom.hNes_Snapshot.cppNes_Snapshot.hNes_Vrc6.cppNes_Vrc6.hNonlinear_Buffer.cppNonlinear_Buffer.hNonlinear_Effects_Buffer.cppNonlinear_Effects_Buffer.hapu_snapshot.cppapu_snapshot.hoptional_mappers.cpp
|
@ -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 );
|
||||
}
|
|
@ -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
|
|
@ -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 );
|
||||
}
|
|
@ -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
|
|
@ -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 );
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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 ); }
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 );
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue